Merge "msm: kgsl: Don't permit the user to set the lowest frequency"
diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt
index 654f26e..440628d 100644
--- a/Documentation/devicetree/bindings/arm/msm/imem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/imem.txt
@@ -46,6 +46,12 @@
 -compatible: "qcom,msm-imem-restart_reason
 -reg: start address and size of restart_reason region in imem
 
+Download Mode Type:
+-------------------
+Required properties:
+-compatible: "qcom,msm-imem-dload-type"
+-reg: start address and size of dload type region in imem
+
 Download Mode:
 --------------
 Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 765b5e4..b29a11f 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -282,6 +282,7 @@
 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 +322,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/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
index 628b2aa..90bc368 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
@@ -31,6 +31,9 @@
 4. LLCC AMON Driver:
 Keeps track of the data progress within the internal channels of LLCC.
 
+5. LLCC Performance Monitor
+Used to monitor the events of LLCC sub blocks.
+
 == llcc device ==
 
 Require Properties:
@@ -107,6 +110,10 @@
 			compatible = "qcom,llcc-amon";
 			qcom,fg-cnt = <0x7>;
 		};
+
+		qcom,llcc-perfmon {
+			compatible = "qcom,llcc-perfmon";
+		};
 	};
 
 == Client ==
diff --git a/Documentation/devicetree/bindings/crypto/msm/ice.txt b/Documentation/devicetree/bindings/crypto/msm/ice.txt
index 2d0e580..fe8671f 100644
--- a/Documentation/devicetree/bindings/crypto/msm/ice.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/ice.txt
@@ -5,16 +5,22 @@
   - reg : <register mapping>
 
 Optional properties:
-  - interrupt-names     : name describing the interrupts for ICE IRQ
-  - interrupts          : <interrupt mapping for ICE IRQ>
-  - qcom,enable-ice-clk : should enable clocks for ICE HW
-  - clocks              : List of phandle and clock specifier pairs
-  - clock-names         : List of clock input name strings sorted in the same
-                          order as the clocks property.
-  - qocm,op-freq-hz     : max clock speed sorted in the same order as the clocks
-                          property.
-  - qcom,instance-type  : describe the storage type for which ICE node is defined
-			  currently, only "ufs" and "sdcc" are supported storage type
+  - interrupt-names     	: name describing the interrupts for ICE IRQ
+  - interrupts          	: <interrupt mapping for ICE IRQ>
+  - qcom,enable-ice-clk 	: should enable clocks for ICE HW
+  - clocks              	: List of phandle and clock specifier pairs
+  - clock-names         	: List of clock input name strings sorted in the same
+                          	  order as the clocks property.
+  - qocm,op-freq-hz     	: max clock speed sorted in the same order as the clocks
+                          	  property.
+  - qcom,instance-type  	: describe the storage type for which ICE node is defined
+			 	  currently, only "ufs" and "sdcc" are supported storage type
+  - vdd-hba-supply		: regulated supply to be used by ICE HW
+  - qcom,msm-bus,name		: bus for ICE transactions
+  - qcom,msm-bus,num-cases	: bus case mapping for ICE HW
+  - qcom,msm-bus,num-paths	: bus path mapping for iCE HW
+  - qcom,msm-bus,vectors-KBps	: bus bandwidth to be voted
+  - qcom,bus-vector-names	: bus vectors mapping
 
 Example:
         ufs_ice: ufsice@630000 {
@@ -30,3 +36,26 @@
 		qcom,instance-type = "ufs";
                 status = "disabled";
         };
+
+	ufs_card_ice: ufscardice@1db0000 {
+		compatible = "qcom,ice_card";
+		reg = <0x1db0000 0x8000>;
+		qcom,enable-ice-clk;
+		clock-names = "ufs_core_clk", "bus_clk",
+			      "iface_clk", "ice_core_clk";
+		clocks = <&clock_gcc GCC_UFS_CARD_AXI_CLK>,
+			 <&clock_gcc GCC_UFS_CARD_CLKREF_CLK>,
+			 <&clock_gcc GCC_UFS_CARD_AHB_CLK>,
+			 <&clock_gcc GCC_UFS_CARD_ICE_CORE_CLK>;
+		qcom,op-freq-hz = <0>, <0>, <0>, <300000000>;
+		vdd-hba-supply = <&ufs_card_gdsc>;
+		qcom,msm-bus,name = "ufs_card_ice_noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<1 650 0 0>,    /* No vote */
+				<1 650 1000 0>; /* Max. bandwidth */
+		qcom,bus-vector-names = "MIN",
+					"MAX";
+		qcom,instance-type = "ufs_card";
+	};
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 051b315..6f1d8e3 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -20,7 +20,7 @@
   - qcom,ce-hw-key : optional, indicates if the hardware supports use of HW KEY.
   - qcom,support-core-clk-only : optional, indicates if the HW supports single crypto core clk.
   - qcom,bsm-ee : optional, indicate the BAM EE value, changes from target to target. Default value is 1 if not specified.
-  - qcom,smmu-s1-bypass : Boolean flag to bypass SMMU stage 1 translation.
+  - qcom,smmu-s1-enable : Boolean flag to enable SMMU stage 1 translation.
   - iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device.
 
 Example:
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
index fa27198..231f31a 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
@@ -40,7 +40,7 @@
 	required. For other targets such as fsm, they do not perform
 	bus scaling. It is not required for those targets.
 
-  - qcom,smmu-s1-bypass : Boolean flag to bypass SMMU stage 1 translation.
+  - qcom,smmu-s1-enable : Boolean flag to bypass SMMU stage 1 translation.
   - iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device.
 
 Example:
diff --git a/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
index 67dc991..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/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/pinctrl/qcom,sdm845-pinctrl b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl
index 9c26374..7e75d2c 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl
@@ -6,7 +6,7 @@
 - compatible:
 	Usage: required
 	Value type: <string>
-	Definition: must be "qcom,sdm845-pinctrl"
+	Definition: must be "qcom,sdm845-pinctrl" or "qcom,sdm845-pinctrl-v2"
 
 - reg:
 	Usage: required
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index e821feb..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.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/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 57a227e..d4db970 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -2640,6 +2640,11 @@
 - qcom,cdc-comp-gpios : phandle for compander gpios.
 - qcom,cdc-dmic-gpios : phandle for Digital mic clk and data gpios.
 - qcom,cdc-sdw-gpios : phandle for soundwire clk and data gpios.
+- qcom,pri-mi2s-gpios : phandle for primary MI2S clk, word select and data gpios.
+- qcom,sec-mi2s-gpios : phandle for secondary MI2S clk, word select and data gpios.
+- qcom,tert-mi2s-gpios : phandle for tertiary MI2S clk, word select and data gpios.
+- qcom,quat-mi2s-gpios : phandle for quaternary MI2S clk, word select and data gpios.
+- qcom,quin-mi2s-gpios : phandle for quinary MI2S clk, word select and data gpios.
 - qcom,msm-mbhc-moist-cfg: This property is used to set moisture detection
 		threshold values for different codecs. First parameter is V(voltage)
 		second one is i(current), third one is r (resistance). Depending on the
@@ -2746,6 +2751,11 @@
 - qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
 - qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
 - qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+- qcom,pri-mi2s-gpios : phandle for primary MI2S clk, word select and data gpios.
+- qcom,sec-mi2s-gpios : phandle for secondary MI2S clk, word select and data gpios.
+- qcom,tert-mi2s-gpios : phandle for tertiary MI2S clk, word select and data gpios.
+- qcom,quat-mi2s-gpios : phandle for quaternary MI2S clk, word select and data gpios.
+- qcom,quin-mi2s-gpios : phandle for quinary MI2S clk, word select and data gpios.
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
index bef9193..c50d678 100644
--- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
+++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
@@ -42,6 +42,11 @@
     cell 4: interrupt flags indicating level-sense information, as defined in
             dt-bindings/interrupt-controller/irq.h
 
+Optional properties:
+- qcom,enable-ahb-bus-workaround : Boolean flag which indicates that the AHB bus
+				   workaround sequence should be used for SPMI
+				   write transactions to avoid corruption
+
 Example V1 PMIC-Arbiter:
 
 	spmi {
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 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/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/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/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-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
new file mode 100644
index 0000000..6be47b4
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.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/;
+
+
+#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";
+};
+
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..15ae24c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.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/;
+
+
+#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";
+};
+
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.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index d538efe..aa8e31b 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -459,6 +459,7 @@
 };
 
 #include "pmxpoorwills.dtsi"
+#include "sdxpoorwills-blsp.dtsi"
 #include "sdxpoorwills-regulator.dtsi"
 #include "sdxpoorwills-smp2p.dtsi"
 #include "sdxpoorwills-usb.dtsi"
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 2765e20..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
@@ -287,14 +301,15 @@
 CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_TRACER_PKT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_IIO=y
 CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
 CONFIG_QCOM_SHOW_RESUME_IRQ=y
 CONFIG_ANDROID=y
 CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 1062175..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
@@ -285,7 +297,6 @@
 CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_TRACER_PKT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
@@ -293,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/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index aefdb52..ee803e2 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -79,6 +79,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 +106,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 +134,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-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index a48fba0..faeaa9e 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -316,6 +316,7 @@
 	};
 
 	kgsl_iommu_test_device {
+		status = "disabled";
 		compatible = "iommu-debug-test";
 		/*
 		 * 0x7 isn't a valid sid, but should pass the sid sanity check.
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/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index a8b826a..2957c68 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>;
@@ -328,7 +328,8 @@
 				reg = <0x4400 0x100>;
 				interrupts = <0x2 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
 					     <0x2 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+					     <0x2 0x44 0x2
+							IRQ_TYPE_EDGE_RISING>;
 				interrupt-names = "ima-rdy",
 						  "mem-xcp",
 						  "dma-grant";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index fa06779..60a81ff 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 {
@@ -233,7 +233,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..b7d5702 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
@@ -729,13 +729,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>;
 				};
 			};
 		};
@@ -935,6 +964,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 +1600,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>;
 			};
 		};
 	};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index 89dee0c..e51b700 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -289,12 +289,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-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 466062b..5e88b0a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -61,7 +61,7 @@
 };
 
 &qupv3_se6_4uart {
-	status = "disabled";
+	status = "ok";
 };
 
 &sdhc_1 {
@@ -242,7 +242,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-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..4f40678
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -0,0 +1,37 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm670-pmic-overlay.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 = "disabled";
+};
+
+&qupv3_se6_4uart {
+	status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index 3e9d967..2a61e18 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,7 +477,6 @@
 
 	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";
@@ -318,7 +486,6 @@
 	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>;
@@ -331,10 +498,11 @@
 			<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",
-			"hdcp_physical";
+			"usb3_dp_com", "hdcp_physical";
 
 		interrupt-parent = <&mdss_mdp>;
 		interrupts = <12 0>;
@@ -417,7 +585,6 @@
 };
 
 &sde_dp {
-	status = "disabled";
 	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 +594,7 @@
 };
 
 &mdss_mdp {
-	connectors = <&sde_rscc &sde_wb>;
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
 };
 
 &dsi_dual_nt35597_truly_video {
@@ -595,3 +762,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-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.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 9aafe03..a743149 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -612,6 +612,94 @@
 	ranges = <0 0 0 0xffffffff>;
 	compatible = "simple-bus";
 
+	jtag_mm0: jtagmm@7040000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7040000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU0>;
+	};
+
+	jtag_mm1: jtagmm@7140000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7140000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qom,coresight-jtagmm-cpu = <&CPU1>;
+	};
+
+	jtag_mm2: jtagmm@7240000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7240000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU2>;
+	};
+
+	jtag_mm3: jtagmm@7340000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7340000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU3>;
+	};
+
+	jtag_mm4: jtagmm@7440000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7440000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU4>;
+	};
+
+	jtag_mm5: jtagmm@7540000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7540000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU5>;
+	};
+
+	jtag_mm6: jtagmm@7640000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7640000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU6>;
+	};
+
+	jtag_mm7: jtagmm@7740000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7740000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU7>;
+	};
+
 	intc: interrupt-controller@17a00000 {
 		compatible = "arm,gic-v3";
 		#interrupt-cells = <3>;
@@ -886,7 +974,7 @@
 		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;
@@ -1554,6 +1642,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>;
@@ -1637,6 +1726,10 @@
 		qcom,client-id = <0x00000001>;
 	};
 
+	qcom,msm_gsi {
+		compatible = "qcom,msm_gsi";
+	};
+
 	qcom,rmnet-ipa {
 		compatible = "qcom,rmnet-ipa3";
 		qcom,rmnet-ipa-ssr;
@@ -1978,8 +2071,11 @@
 		qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>;
 
 		clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>,
-			<&clock_gcc GCC_SDCC1_APPS_CLK>;
-		clock-names = "iface_clk", "core_clk";
+			<&clock_gcc GCC_SDCC1_APPS_CLK>,
+			<&clock_gcc GCC_SDCC1_ICE_CORE_CLK>;
+		clock-names = "iface_clk", "core_clk", "ice_core_clk";
+
+		qcom,ice-clk-rates = <300000000 75000000>;
 
 		qcom,nonremovable;
 
@@ -2131,6 +2227,21 @@
 		};
 	};
 
+	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";
@@ -2155,32 +2266,7 @@
 		compatible = "qcom,devbw";
 		governor = "performance";
 		qcom,src-dst-ports =
-			<MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_LLCC>;
-		qcom,active-only;
-		qcom,bw-tbl =
-			< MHZ_TO_MBPS(150, 16) >, /*  2288 MB/s */
-			< MHZ_TO_MBPS(300, 16) >, /*  4577 MB/s */
-			< MHZ_TO_MBPS(466, 16) >, /*  7110 MB/s */
-			< MHZ_TO_MBPS(600, 16) >, /*  9155 MB/s */
-			< MHZ_TO_MBPS(806, 16) >, /* 12298 MB/s */
-			< MHZ_TO_MBPS(933, 16) >; /* 14236 MB/s */
-	};
-
-	bwmon: qcom,cpu-bwmon {
-		compatible = "qcom,bimc-bwmon4";
-		reg = <0x1436400 0x300>, <0x1436300 0x200>;
-		reg-names = "base", "global_base";
-		interrupts = <0 581 4>;
-		qcom,mport = <0>;
-		qcom,hw-timer-hz = <19200000>;
-		qcom,target-dev = <&cpubw>;
-	};
-
-	llccbw: qcom,llccbw {
-		compatible = "qcom,devbw";
-		governor = "powersave";
-		qcom,src-dst-ports =
-			<MSM_BUS_MASTER_LLCC MSM_BUS_SLAVE_EBI_CH0>;
+			<MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_EBI_CH0>;
 		qcom,active-only;
 		qcom,bw-tbl =
 			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
@@ -2196,16 +2282,14 @@
 			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
-	llcc_bwmon: qcom,llcc-bwmon {
-		compatible = "qcom,bimc-bwmon5";
-		reg = <0x0114a000 0x1000>;
-		reg-names = "base";
-		interrupts = <GIC_SPI 580 IRQ_TYPE_LEVEL_HIGH>;
+	bwmon: qcom,cpu-bwmon {
+		compatible = "qcom,bimc-bwmon4";
+		reg = <0x1436400 0x300>, <0x1436300 0x200>;
+		reg-names = "base", "global_base";
+		interrupts = <0 581 4>;
+		qcom,mport = <0>;
 		qcom,hw-timer-hz = <19200000>;
-		qcom,target-dev = <&llccbw>;
-		qcom,count-unit = <0x200000>;
-		qcom,byte-mid-mask = <0xe000>;
-		qcom,byte-mid-match = <0xe000>;
+		qcom,target-dev = <&cpubw>;
 	};
 
 	memlat_cpu0: qcom,memlat-cpu0 {
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 9a1f055..04418d4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
@@ -376,4 +376,47 @@
 		clock-cntl-level = "turbo";
 		clock-rates = <24000000>;
 	};
+
+	qcom,cam-sensor@3 {
+		cell-index = <3>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x03>;
+		csiphy-sd-index = <3>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk3_active
+				 &cam_sensor_iris_active>;
+		pinctrl-1 = <&cam_sensor_mclk3_suspend
+				 &cam_sensor_iris_suspend>;
+		gpios = <&tlmm 16 0>,
+			<&tlmm 9 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK3",
+					"CAM_RESET3",
+					"CAM_VANA1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK3_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
index 2702ca1..9088fac 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
@@ -376,4 +376,47 @@
 		clock-cntl-level = "turbo";
 		clock-rates = <24000000>;
 	};
+	qcom,cam-sensor@3 {
+		cell-index = <3>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x03>;
+		csiphy-sd-index = <3>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk3_active
+				 &cam_sensor_iris_active>;
+		pinctrl-1 = <&cam_sensor_mclk3_suspend
+				 &cam_sensor_iris_suspend>;
+		gpios = <&tlmm 16 0>,
+			<&tlmm 9 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK3",
+					"CAM_RESET3",
+					"CAM_VANA1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK3_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index e4f768f..7c9482c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -817,7 +817,6 @@
 			"soc_ahb_clk",
 			"cpas_ahb_clk",
 			"camnoc_axi_clk",
-			"icp_apb_clk",
 			"icp_clk",
 			"icp_clk_src";
 		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
@@ -826,11 +825,10 @@
 				<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 				<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
 				<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
-				<&clock_camcc CAM_CC_ICP_APB_CLK>,
 				<&clock_camcc CAM_CC_ICP_CLK>,
 				<&clock_camcc CAM_CC_ICP_CLK_SRC>;
 
-		clock-rates = <0 0 400000000 0 0 0 0 0 600000000>;
+		clock-rates = <0 0 400000000 0 0 0 0 600000000>;
 		clock-cntl-level = "turbo";
 		fw_name = "CAMERA_ICP.elf";
 		status = "ok";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 11b6a4d..4fd1a67 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;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
index 73cd794..cc609aa 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
@@ -183,6 +183,12 @@
 		/delete-property/ cam_vio-supply;
 		/delete-property/ cam_vana-supply;
 	};
+
+	qcom,cam-sensor@3 {
+		/delete-property/ cam_vio-supply;
+		/delete-property/ cam_vana-supply;
+	};
+
 };
 
 &clock_gcc {
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 f73bf3a..a0207e5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -2826,6 +2826,35 @@
 			};
 		};
 
+		cam_sensor_mclk3_active: cam_sensor_mclk3_active {
+			/* MCLK3 */
+			mux {
+				pins = "gpio16";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio16";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk3_suspend: cam_sensor_mclk3_suspend {
+			/* MCLK3 */
+			mux {
+				pins = "gpio16";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio16";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+
 		cam_sensor_front_active: cam_sensor_front_active {
 			/* RESET  AVDD_LDO*/
 			mux {
@@ -2855,6 +2884,36 @@
 			};
 		};
 
+		cam_sensor_iris_active: cam_sensor_iris_active {
+			/* RESET  AVDD_LDO*/
+			mux {
+				pins = "gpio9", "gpio8";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio9", "gpio8";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_iris_suspend: cam_sensor_iris_suspend {
+			/* RESET */
+			mux {
+				pins = "gpio9";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio9";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+				output-low;
+			};
+		};
+
+
 		cam_sensor_mclk2_active: cam_sensor_mclk2_active {
 			/* MCLK1 */
 			mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-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..77495bf 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>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index d89722f..ae49fec 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>;
 };
 
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 0494495..e7a946c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -211,6 +211,7 @@
 			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 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
index c070ed6..0b6e27e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
@@ -24,8 +24,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -60,8 +59,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -97,8 +95,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -124,7 +121,7 @@
 	cam_csiphy3: qcom,csiphy@ac68000 {
 		cell-index = <3>;
 		compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
-		reg = <0xac67000 0x1000>;
+		reg = <0xac68000 0x1000>;
 		reg-names = "csiphy";
 		reg-cam-base = <0x68000>;
 		interrupts = <0 448 0>;
@@ -133,8 +130,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -319,14 +315,14 @@
 			"turbo", "turbo";
 		client-id-based;
 		client-names =
-			"csiphy0", "csiphy1", "csiphy2", "cci0",
+			"csiphy0", "csiphy1", "csiphy2", "csiphy3", "cci0",
 			"csid0", "csid1", "csid2",
 			"ife0", "ife1", "ife2", "ipe0",
 			"ipe1", "cam-cdm-intf0", "cpas-cdm0", "bps0",
 			"icp0", "jpeg-dma0", "jpeg-enc0", "fd0";
 		client-axi-port-names =
-			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
-			"cam_hf_1", "cam_hf_2", "cam_hf_2",
+			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_hf_2",
+			"cam_sf_1", "cam_hf_1", "cam_hf_2", "cam_hf_2",
 			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
 			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1",
 			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 944ab24..b3cb8f5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -25,6 +25,10 @@
 /delete-node/ &apc0_cpr;
 /delete-node/ &apc1_cpr;
 
+&tlmm {
+	compatible = "qcom,sdm845-pinctrl-v2";
+};
+
 &soc {
 	/* CPR controller regulators */
 	apc0_cpr: cprh-ctrl@17dc0000 {
@@ -1168,6 +1172,7 @@
 
 &clock_camcc {
 	compatible = "qcom,cam_cc-sdm845-v2", "syscon";
+	qcom,cam_cc_csi3phytimer_clk_src-opp-handle = <&cam_csiphy3>;
 };
 
 &clock_dispcc {
@@ -1193,7 +1198,6 @@
 
 &refgen {
 	status = "ok";
-	regulator-always-on;
 };
 
 &spss_utils {
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index ac2a550..3beccfa 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -79,7 +79,7 @@
 			};
 			L1_I_0: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_0: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -110,7 +110,7 @@
 			};
 			L1_I_100: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_100: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -141,7 +141,7 @@
 			};
 			L1_I_200: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_200: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -172,7 +172,7 @@
 			};
 			L1_I_300: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_300: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -203,14 +203,14 @@
 			};
 			L1_I_400: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_400: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_400: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -234,14 +234,14 @@
 			};
 			L1_I_500: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_500: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_500: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -265,14 +265,14 @@
 			};
 			L1_I_600: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_600: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_600: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -296,14 +296,14 @@
 			};
 			L1_I_700: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_700: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_700: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -846,6 +846,7 @@
 		interrupt-controller;
 		#interrupt-cells = <4>;
 		cell-index = <0>;
+		qcom,enable-ahb-bus-workaround;
 	};
 
 	spmi_debug_bus: qcom,spmi-debug@6b22000 {
@@ -2280,6 +2281,10 @@
 			max-slices = <32>;
 		};
 
+		qcom,llcc-perfmon {
+			compatible = "qcom,llcc-perfmon";
+		};
+
 		qcom,llcc-erp {
 			compatible = "qcom,llcc-erp";
 			interrupt-names = "ecc_irq";
@@ -2718,7 +2723,7 @@
 			<0x1dc4000 0x24000>;
 		reg-names = "crypto-base","crypto-bam-base";
 		interrupts = <0 272 0>;
-		qcom,bam-pipe-pair = <1>;
+		qcom,bam-pipe-pair = <3>;
 		qcom,ce-hw-instance = <0>;
 		qcom,ce-device = <0>;
 		qcom,ce-hw-shared;
@@ -2737,9 +2742,9 @@
 			 <&clock_gcc GCC_CE1_AXI_CLK>;
 		qcom,ce-opp-freq = <171430000>;
 		qcom,request-bw-before-clk;
-		qcom,smmu-s1-bypass;
-		iommus = <&apps_smmu 0x702 0x1>,
-			 <&apps_smmu 0x712 0x1>;
+		qcom,smmu-s1-enable;
+		iommus = <&apps_smmu 0x706 0x1>,
+			 <&apps_smmu 0x716 0x1>;
 	};
 
 	qcom_msmhdcp: qcom,msm_hdcp {
@@ -2778,9 +2783,9 @@
 		qcom,use-sw-ahash-algo;
 		qcom,use-sw-aead-algo;
 		qcom,use-sw-hmac-algo;
-		qcom,smmu-s1-bypass;
-		iommus = <&apps_smmu 0x704 0x3>,
-			 <&apps_smmu 0x714 0x3>;
+		qcom,smmu-s1-enable;
+		iommus = <&apps_smmu 0x704 0x1>,
+			 <&apps_smmu 0x714 0x1>;
 	};
 
 	qcom,msm_gsi {
@@ -2819,7 +2824,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>;
@@ -2944,15 +2948,23 @@
 			compatible = "qcom,ipa-smmu-ap-cb";
 			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>;
 		};
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 4e7fe31..6b83b36 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -405,7 +405,6 @@
 CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_MSM_GLINK_SPI_XPRT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index 6951086..791d349 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -422,7 +422,6 @@
 CONFIG_MSM_GLINK_SPI_XPRT=y
 CONFIG_TRACER_PKT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index e05f395..485ab7e 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -282,6 +282,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
@@ -513,7 +514,6 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index b47850c..b0d0807 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
@@ -519,7 +520,6 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index cae87d3..3c5b65c 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -21,6 +21,7 @@
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_BPF=y
 CONFIG_SCHED_CORE_CTL=y
+CONFIG_SCHED_CORE_ROTATE=y
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
@@ -187,6 +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
@@ -488,6 +490,7 @@
 CONFIG_QCOM_RUN_QUEUE_STATS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_SDM845_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
 CONFIG_MSM_SERVICE_LOCATOR=y
 CONFIG_MSM_SERVICE_NOTIFIER=y
 CONFIG_MSM_BOOT_STATS=y
@@ -508,7 +511,6 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 0d89dc7..09f81c0 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -25,6 +25,7 @@
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_BPF=y
 CONFIG_SCHED_CORE_CTL=y
+CONFIG_SCHED_CORE_ROTATE=y
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
@@ -193,6 +194,7 @@
 CONFIG_IP_NF_ARP_MANGLE=y
 CONFIG_NF_CONNTRACK_IPV6=y
 CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
 CONFIG_IP6_NF_MATCH_RPFILTER=y
 CONFIG_IP6_NF_FILTER=y
 CONFIG_IP6_NF_TARGET_REJECT=y
@@ -503,6 +505,7 @@
 CONFIG_QCOM_RUN_QUEUE_STATS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_SDM845_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
 CONFIG_MSM_SERVICE_LOCATOR=y
 CONFIG_MSM_SERVICE_NOTIFIER=y
 CONFIG_MSM_BOOT_STATS=y
@@ -525,7 +528,6 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
index 06ff7fd..b6c6fa2 100644
--- a/arch/arm64/include/asm/module.h
+++ b/arch/arm64/include/asm/module.h
@@ -22,14 +22,19 @@
 #define MODULE_ARCH_VERMAGIC	"aarch64"
 
 #ifdef CONFIG_ARM64_MODULE_PLTS
-struct mod_arch_specific {
+struct mod_plt_sec {
 	struct elf64_shdr	*plt;
 	int			plt_num_entries;
 	int			plt_max_entries;
 };
+
+struct mod_arch_specific {
+	struct mod_plt_sec	core;
+	struct mod_plt_sec	init;
+};
 #endif
 
-u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
+u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela,
 			  Elf64_Sym *sym);
 
 #ifdef CONFIG_RANDOMIZE_BASE
diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
index 1ce90d8..d05dbe6 100644
--- a/arch/arm64/kernel/module-plts.c
+++ b/arch/arm64/kernel/module-plts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -26,35 +26,21 @@
 	__le32	br;	/* br	x16				*/
 };
 
-u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
+static bool in_init(const struct module *mod, void *loc)
+{
+	return (u64)loc - (u64)mod->init_layout.base < mod->init_layout.size;
+}
+
+u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela,
 			  Elf64_Sym *sym)
 {
-	struct plt_entry *plt = (struct plt_entry *)mod->arch.plt->sh_addr;
-	int i = mod->arch.plt_num_entries;
+	struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
+							  &mod->arch.init;
+	struct plt_entry *plt = (struct plt_entry *)pltsec->plt->sh_addr;
+	int i = pltsec->plt_num_entries;
 	u64 val = sym->st_value + rela->r_addend;
 
 	/*
-	 * We only emit PLT entries against undefined (SHN_UNDEF) symbols,
-	 * which are listed in the ELF symtab section, but without a type
-	 * or a size.
-	 * So, similar to how the module loader uses the Elf64_Sym::st_value
-	 * field to store the resolved addresses of undefined symbols, let's
-	 * borrow the Elf64_Sym::st_size field (whose value is never used by
-	 * the module loader, even for symbols that are defined) to record
-	 * the address of a symbol's associated PLT entry as we emit it for a
-	 * zero addend relocation (which is the only kind we have to deal with
-	 * in practice). This allows us to find duplicates without having to
-	 * go through the table every time.
-	 */
-	if (rela->r_addend == 0 && sym->st_size != 0) {
-		BUG_ON(sym->st_size < (u64)plt || sym->st_size >= (u64)&plt[i]);
-		return sym->st_size;
-	}
-
-	mod->arch.plt_num_entries++;
-	BUG_ON(mod->arch.plt_num_entries > mod->arch.plt_max_entries);
-
-	/*
 	 * MOVK/MOVN/MOVZ opcode:
 	 * +--------+------------+--------+-----------+-------------+---------+
 	 * | sf[31] | opc[30:29] | 100101 | hw[22:21] | imm16[20:5] | Rd[4:0] |
@@ -72,8 +58,19 @@
 		cpu_to_le32(0xd61f0200)
 	};
 
-	if (rela->r_addend == 0)
-		sym->st_size = (u64)&plt[i];
+	/*
+	 * Check if the entry we just created is a duplicate. Given that the
+	 * relocations are sorted, this will be the last entry we allocated.
+	 * (if one exists).
+	 */
+	if (i > 0 &&
+	    plt[i].mov0 == plt[i - 1].mov0 &&
+	    plt[i].mov1 == plt[i - 1].mov1 &&
+	    plt[i].mov2 == plt[i - 1].mov2)
+		return (u64)&plt[i - 1];
+
+	pltsec->plt_num_entries++;
+	BUG_ON(pltsec->plt_num_entries > pltsec->plt_max_entries);
 
 	return (u64)&plt[i];
 }
@@ -104,7 +101,8 @@
 	return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0;
 }
 
-static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num)
+static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num,
+			       Elf64_Word dstidx)
 {
 	unsigned int ret = 0;
 	Elf64_Sym *s;
@@ -116,13 +114,17 @@
 		case R_AARCH64_CALL26:
 			/*
 			 * We only have to consider branch targets that resolve
-			 * to undefined symbols. This is not simply a heuristic,
-			 * it is a fundamental limitation, since the PLT itself
-			 * is part of the module, and needs to be within 128 MB
-			 * as well, so modules can never grow beyond that limit.
+			 * to symbols that are defined in a different section.
+			 * This is not simply a heuristic, it is a fundamental
+			 * limitation, since there is no guaranteed way to emit
+			 * PLT entries sufficiently close to the branch if the
+			 * section size exceeds the range of a branch
+			 * instruction. So ignore relocations against defined
+			 * symbols if they live in the same section as the
+			 * relocation target.
 			 */
 			s = syms + ELF64_R_SYM(rela[i].r_info);
-			if (s->st_shndx != SHN_UNDEF)
+			if (s->st_shndx == dstidx)
 				break;
 
 			/*
@@ -149,7 +151,8 @@
 int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
 			      char *secstrings, struct module *mod)
 {
-	unsigned long plt_max_entries = 0;
+	unsigned long core_plts = 0;
+	unsigned long init_plts = 0;
 	Elf64_Sym *syms = NULL;
 	int i;
 
@@ -158,14 +161,16 @@
 	 * entries. Record the symtab address as well.
 	 */
 	for (i = 0; i < ehdr->e_shnum; i++) {
-		if (strcmp(".plt", secstrings + sechdrs[i].sh_name) == 0)
-			mod->arch.plt = sechdrs + i;
+		if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
+			mod->arch.core.plt = sechdrs + i;
+		else if (!strcmp(secstrings + sechdrs[i].sh_name, ".init.plt"))
+			mod->arch.init.plt = sechdrs + i;
 		else if (sechdrs[i].sh_type == SHT_SYMTAB)
 			syms = (Elf64_Sym *)sechdrs[i].sh_addr;
 	}
 
-	if (!mod->arch.plt) {
-		pr_err("%s: module PLT section missing\n", mod->name);
+	if (!mod->arch.core.plt || !mod->arch.init.plt) {
+		pr_err("%s: module PLT section(s) missing\n", mod->name);
 		return -ENOEXEC;
 	}
 	if (!syms) {
@@ -188,14 +193,27 @@
 		/* sort by type, symbol index and addend */
 		sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL);
 
-		plt_max_entries += count_plts(syms, rels, numrels);
+		if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0)
+			core_plts += count_plts(syms, rels, numrels,
+						sechdrs[i].sh_info);
+		else
+			init_plts += count_plts(syms, rels, numrels,
+						sechdrs[i].sh_info);
 	}
 
-	mod->arch.plt->sh_type = SHT_NOBITS;
-	mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
-	mod->arch.plt->sh_addralign = L1_CACHE_BYTES;
-	mod->arch.plt->sh_size = plt_max_entries * sizeof(struct plt_entry);
-	mod->arch.plt_num_entries = 0;
-	mod->arch.plt_max_entries = plt_max_entries;
+	mod->arch.core.plt->sh_type = SHT_NOBITS;
+	mod->arch.core.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+	mod->arch.core.plt->sh_addralign = L1_CACHE_BYTES;
+	mod->arch.core.plt->sh_size = (core_plts  + 1) * sizeof(struct plt_entry);
+	mod->arch.core.plt_num_entries = 0;
+	mod->arch.core.plt_max_entries = core_plts;
+
+	mod->arch.init.plt->sh_type = SHT_NOBITS;
+	mod->arch.init.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+	mod->arch.init.plt->sh_addralign = L1_CACHE_BYTES;
+	mod->arch.init.plt->sh_size = (init_plts + 1) * sizeof(struct plt_entry);
+	mod->arch.init.plt_num_entries = 0;
+	mod->arch.init.plt_max_entries = init_plts;
+
 	return 0;
 }
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 7f31698..c9a2ab4 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -380,7 +380,7 @@
 
 			if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
 			    ovf == -ERANGE) {
-				val = module_emit_plt_entry(me, &rel[i], sym);
+				val = module_emit_plt_entry(me, loc, &rel[i], sym);
 				ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,
 						     26, AARCH64_INSN_IMM_26);
 			}
diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
index 8949f6c..f7c9781 100644
--- a/arch/arm64/kernel/module.lds
+++ b/arch/arm64/kernel/module.lds
@@ -1,3 +1,4 @@
 SECTIONS {
 	.plt (NOLOAD) : { BYTE(0) }
+	.init.plt (NOLOAD) : { BYTE(0) }
 }
diff --git a/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/power/main.c b/drivers/base/power/main.c
index c1e56c3..4f99101 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);
 }
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/char/adsprpc.c b/drivers/char/adsprpc.c
index 89bf7c6..876ed90 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -1830,16 +1830,18 @@
 	struct smq_invoke_ctx *ictx = NULL;
 	struct smq_invoke_ctx *ctx;
 	int bfound = 0;
+	unsigned long flags;
+	unsigned long flags1;
 
 	ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rctx));
 	if (!ctx)
 		return bfound;
 
-	spin_lock(&me->hlock);
+	spin_lock_irqsave(&me->hlock, flags);
 	hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
 		if (ctx->fl != fl)
 			continue;
-		spin_lock(&fl->hlock);
+		spin_lock_irqsave(&fl->hlock, flags1);
 		hlist_for_each_entry_safe(ictx, m, &fl->clst.pending, hn) {
 			if (ptr_to_uint64(ictx) == rctx) {
 				bfound = 1;
@@ -1852,11 +1854,11 @@
 				break;
 			}
 		}
-		spin_unlock(&fl->hlock);
+		spin_unlock_irqrestore(&fl->hlock, flags1);
 		if (bfound)
 			break;
 	}
-	spin_unlock(&me->hlock);
+	spin_unlock_irqrestore(&me->hlock, flags);
 	return bfound;
 }
 
@@ -2088,8 +2090,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;
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index ac3c1fd..c9ae689 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -21,7 +21,7 @@
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/sched.h>
-#include <linux/wakelock.h>
+#include <linux/device.h>
 #include <linux/atomic.h>
 #include "diagfwd_bridge.h"
 
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index e00a61d..0703c38 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -836,6 +836,7 @@
 	uint8_t peripheral;
 	uint8_t type;
 	struct diagfwd_info *fwd_info = NULL;
+	int transport = 0;
 
 	diag_socket_exit();
 
@@ -857,7 +858,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/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 746cdc0..dc5ec96 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -1006,9 +1006,13 @@
 		bool from_idle, int predicted)
 {
 	struct lpm_cluster_level *level = &cluster->levels[idx];
+	struct cpumask online_cpus;
+
+	cpumask_and(&online_cpus, &cluster->num_children_in_sync,
+					cpu_online_mask);
 
 	if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
-			|| is_IPI_pending(&cluster->num_children_in_sync)) {
+			|| is_IPI_pending(&online_cpus)) {
 		return -EPERM;
 	}
 
@@ -1460,7 +1464,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/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 35d7542..4426bc7 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -162,7 +162,7 @@
 	bool cadence_flag;
 	uint8_t *dummyreq_in_buf;
 	struct dma_iommu_mapping *smmu_mapping;
-	bool bypass_s1_smmu;
+	bool enable_s1_smmu;
 };
 
 static void print_notify_debug(struct sps_event_notify *notify);
@@ -5716,8 +5716,8 @@
 		pce_dev->ce_opp_freq_hz = CE_CLK_100MHZ;
 	}
 
-	if (of_property_read_bool((&pdev->dev)->of_node, "qcom,smmu-s1-bypass"))
-		pce_dev->bypass_s1_smmu = true;
+	if (of_property_read_bool((&pdev->dev)->of_node, "qcom,smmu-s1-enable"))
+		pce_dev->enable_s1_smmu = true;
 
 	pce_dev->ce_bam_info.dest_pipe_index	=
 			2 * pce_dev->ce_bam_info.pipe_pair_index;
@@ -5963,7 +5963,7 @@
 static int qce_smmu_init(struct qce_device *pce_dev)
 {
 	struct dma_iommu_mapping *mapping;
-	int s1_bypass = 1;
+	int attr = 1;
 	int ret = 0;
 
 	mapping = arm_iommu_create_mapping(&platform_bus_type,
@@ -5975,9 +5975,16 @@
 	}
 
 	ret = iommu_domain_set_attr(mapping->domain,
-				DOMAIN_ATTR_S1_BYPASS, &s1_bypass);
+				DOMAIN_ATTR_ATOMIC, &attr);
 	if (ret < 0) {
-		pr_err("Set s1_bypass attribute failed, err = %d\n", ret);
+		pr_err("Set ATOMIC attr failed, err = %d\n", ret);
+		goto ext_fail_set_attr;
+	}
+
+	ret = iommu_domain_set_attr(mapping->domain,
+				DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR, &attr);
+	if (ret < 0) {
+		pr_err("Set UPSTREAM_IOVA_ALLOCATOR failed, err = %d\n", ret);
 		goto ext_fail_set_attr;
 	}
 
@@ -6020,6 +6027,13 @@
 		goto err_pce_dev;
 	}
 
+	if (pce_dev->enable_s1_smmu) {
+		if (qce_smmu_init(pce_dev)) {
+			*rc = -EIO;
+			goto err_pce_dev;
+		}
+	}
+
 	for (i = 0; i < MAX_QCE_ALLOC_BAM_REQ; i++)
 		atomic_set(&pce_dev->ce_request_info[i].in_use, false);
 	pce_dev->ce_request_index = 0;
@@ -6051,13 +6065,6 @@
 	if (*rc)
 		goto err_enable_clk;
 
-	if (pce_dev->bypass_s1_smmu) {
-		if (qce_smmu_init(pce_dev)) {
-			*rc = -EIO;
-			goto err_smmu;
-		}
-	}
-
 	if (_probe_ce_engine(pce_dev)) {
 		*rc = -ENXIO;
 		goto err;
@@ -6084,9 +6091,6 @@
 	mutex_unlock(&qce_iomap_mutex);
 	return pce_dev;
 err:
-	if (pce_dev->bypass_s1_smmu)
-		qce_iommu_release_iomapping(pce_dev);
-err_smmu:
 	qce_disable_clk(pce_dev);
 
 err_enable_clk:
@@ -6099,6 +6103,9 @@
 		dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
 			pce_dev->coh_vmem, pce_dev->coh_pmem);
 err_iobase:
+	if (pce_dev->enable_s1_smmu)
+		qce_iommu_release_iomapping(pce_dev);
+
 	if (pce_dev->iobase)
 		iounmap(pce_dev->iobase);
 err_pce_dev:
@@ -6128,7 +6135,7 @@
 	kfree(pce_dev->dummyreq_in_buf);
 	kfree(pce_dev->iovec_vmem);
 
-	if (pce_dev->bypass_s1_smmu)
+	if (pce_dev->enable_s1_smmu)
 		qce_iommu_release_iomapping(pce_dev);
 
 	qce_disable_clk(pce_dev);
diff --git a/drivers/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/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index ffe60de..f9b758f 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -575,7 +575,9 @@
 		count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1;
 		break;
 	case MON3:
-		count = readl_relaxed(MON3_ZONE_MAX(m, zone)) + 1;
+		count = readl_relaxed(MON3_ZONE_MAX(m, zone));
+		if (count)
+			count++;
 		break;
 	}
 
diff --git a/drivers/devfreq/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/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 25407c4..92ac0ec 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -74,7 +74,7 @@
 	struct dp_debug_private *debug = file->private_data;
 	char buf[SZ_32];
 	size_t len = 0;
-	int hdisplay = 0, vdisplay = 0, vrefresh = 0;
+	int hdisplay = 0, vdisplay = 0, vrefresh = 0, aspect_ratio;
 
 	if (!debug)
 		return -ENODEV;
@@ -89,7 +89,8 @@
 
 	buf[len] = '\0';
 
-	if (sscanf(buf, "%d %d %d", &hdisplay, &vdisplay, &vrefresh) != 3)
+	if (sscanf(buf, "%d %d %d %d", &hdisplay, &vdisplay, &vrefresh,
+				&aspect_ratio) != 4)
 		goto clear;
 
 	if (!hdisplay || !vdisplay || !vrefresh)
@@ -99,6 +100,7 @@
 	debug->dp_debug.hdisplay = hdisplay;
 	debug->dp_debug.vdisplay = vdisplay;
 	debug->dp_debug.vrefresh = vrefresh;
+	debug->dp_debug.aspect_ratio = aspect_ratio;
 	goto end;
 clear:
 	pr_debug("clearing debug modes\n");
@@ -198,11 +200,11 @@
 
 	list_for_each_entry(mode, &connector->modes, head) {
 		len += snprintf(buf + len, SZ_4K - len,
-		"%s %d %d %d %d %d %d %d %d %d 0x%x\n",
-		mode->name, mode->vrefresh, mode->hdisplay,
-		mode->hsync_start, mode->hsync_end, mode->htotal,
-		mode->vdisplay, mode->vsync_start, mode->vsync_end,
-		mode->vtotal, mode->flags);
+		"%s %d %d %d %d %d %d %d %d %d %d 0x%x\n",
+		mode->name, mode->vrefresh, mode->picture_aspect_ratio,
+		mode->hdisplay, mode->hsync_start, mode->hsync_end,
+		mode->htotal, mode->vdisplay, mode->vsync_start,
+		mode->vsync_end, mode->vtotal, mode->flags);
 	}
 
 	if (copy_to_user(user_buff, buf, len)) {
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h
index 7fd5330..d5a9301 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.h
+++ b/drivers/gpu/drm/msm/dp/dp_debug.h
@@ -28,6 +28,7 @@
  */
 struct dp_debug {
 	bool debug_en;
+	int aspect_ratio;
 	int vdisplay;
 	int hdisplay;
 	int vrefresh;
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 06f8558..254343a 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -514,25 +514,19 @@
 	dp_disp = display;
 	debug = dp_disp->get_debug(dp_disp);
 
-	if (debug->debug_en) {
-		if (mode->hdisplay == debug->hdisplay &&
-				mode->vdisplay == debug->vdisplay &&
-				mode->vrefresh == debug->vrefresh &&
-				mode->clock <= dp_disp->max_pclk_khz)
-			return MODE_OK;
-		else
-			return MODE_ERROR;
-	} else {
-		if (mode->vrefresh == 0) {
-			int vrefresh = (mode->clock * 1000) /
-				(mode->vtotal * mode->htotal);
-			if (vrefresh > 60)
-				return MODE_BAD;
-		}
+	mode->vrefresh = drm_mode_vrefresh(mode);
 
-		if (mode->clock > dp_disp->max_pclk_khz)
-			return MODE_BAD;
-		else
-			return MODE_OK;
-	}
+	if (mode->vrefresh > 60)
+		return MODE_BAD;
+
+	if (mode->clock > dp_disp->max_pclk_khz)
+		return MODE_BAD;
+
+	if (debug->debug_en && (mode->hdisplay != debug->hdisplay ||
+			mode->vdisplay != debug->vdisplay ||
+			mode->vrefresh != debug->vrefresh ||
+			mode->picture_aspect_ratio != debug->aspect_ratio))
+		return MODE_BAD;
+
+	return MODE_OK;
 }
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 0f48db6..25138a3 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;
@@ -1369,7 +1367,9 @@
 			continue;
 		len = event->length + sizeof(struct drm_msm_event_resp);
 		if (node->base.file_priv->event_space < len) {
-			DRM_ERROR("Insufficient space to notify\n");
+			DRM_ERROR("Insufficient space %d for event %x len %d\n",
+				node->base.file_priv->event_space, event->type,
+				len);
 			continue;
 		}
 		notify = kzalloc(len, GFP_ATOMIC);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index e19d1db..2f4de7c 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
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index faef4ce..e9ffb96 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -62,6 +62,8 @@
 
 static void dspp_igc_install_property(struct drm_crtc *crtc);
 
+static void dspp_hist_install_property(struct drm_crtc *crtc);
+
 typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc);
 
 static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX];
@@ -77,6 +79,8 @@
 static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc,
 		enum ad_property ad_prop);
 
+static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg);
+
 #define setup_dspp_prop_install_funcs(func) \
 do { \
 	func[SDE_DSPP_PCC] = dspp_pcc_install_property; \
@@ -86,6 +90,7 @@
 	func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \
 	func[SDE_DSPP_GC] = dspp_gc_install_property; \
 	func[SDE_DSPP_IGC] = dspp_igc_install_property; \
+	func[SDE_DSPP_HIST] = dspp_hist_install_property; \
 } while (0)
 
 typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -111,7 +116,8 @@
 	SDE_CP_CRTC_DSPP_SIXZONE,
 	SDE_CP_CRTC_DSPP_GAMUT,
 	SDE_CP_CRTC_DSPP_DITHER,
-	SDE_CP_CRTC_DSPP_HIST,
+	SDE_CP_CRTC_DSPP_HIST_CTRL,
+	SDE_CP_CRTC_DSPP_HIST_IRQ,
 	SDE_CP_CRTC_DSPP_AD,
 	SDE_CP_CRTC_DSPP_VLUT,
 	SDE_CP_CRTC_DSPP_AD_MODE,
@@ -365,6 +371,13 @@
 		return;
 	}
 
+	/* create blob to store histogram data */
+	sde_crtc->hist_blob = drm_property_create_blob(crtc->dev,
+				sizeof(struct drm_msm_hist), NULL);
+	if (IS_ERR(sde_crtc->hist_blob))
+		sde_crtc->hist_blob = NULL;
+
+	mutex_init(&sde_crtc->crtc_cp_lock);
 	INIT_LIST_HEAD(&sde_crtc->active_list);
 	INIT_LIST_HEAD(&sde_crtc->dirty_list);
 	INIT_LIST_HEAD(&sde_crtc->feature_list);
@@ -531,6 +544,77 @@
 	sde_cp_crtc_prop_attach(&prop_attach);
 }
 
+static struct sde_crtc_irq_info *_sde_cp_get_intr_node(u32 event,
+				struct sde_crtc *sde_crtc)
+{
+	bool found = false;
+	struct sde_crtc_irq_info *node = NULL;
+
+	list_for_each_entry(node, &sde_crtc->user_event_list, list) {
+		if (node->event == event) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found)
+		node = NULL;
+
+	return node;
+}
+
+static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc)
+{
+	struct drm_crtc *crtc_drm = &sde_crtc->base;
+	struct sde_kms *kms = NULL;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_crtc_irq_info *node = NULL;
+	int i, irq_idx, ret = 0;
+	unsigned long flags;
+
+	if (!crtc_drm) {
+		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
+		return;
+	}
+
+	kms = get_kms(crtc_drm);
+
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		hw_lm = sde_crtc->mixers[i].hw_lm;
+		hw_dspp = sde_crtc->mixers[i].hw_dspp;
+		if (!hw_lm->cfg.right_mixer)
+			break;
+	}
+
+	if (!hw_dspp) {
+		DRM_ERROR("invalid dspp\n");
+		return;
+	}
+
+	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
+					hw_dspp->idx);
+	if (irq_idx < 0) {
+		DRM_ERROR("failed to get irq idx\n");
+		return;
+	}
+
+	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, sde_crtc);
+	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
+	if (!node)
+		return;
+
+	if (node->state == IRQ_DISABLED) {
+		ret = sde_core_irq_enable(kms, &irq_idx, 1);
+		if (ret)
+			DRM_ERROR("failed to enable irq %d\n", irq_idx);
+		else
+			node->state = IRQ_ENABLED;
+	}
+}
+
 static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
 				   struct sde_crtc *sde_crtc)
 {
@@ -639,6 +723,21 @@
 			}
 			hw_lm->ops.setup_gc(hw_lm, &hw_cfg);
 			break;
+		case SDE_CP_CRTC_DSPP_HIST_CTRL:
+			if (!hw_dspp || !hw_dspp->ops.setup_histogram) {
+				ret = -EINVAL;
+				continue;
+			}
+			hw_dspp->ops.setup_histogram(hw_dspp, &feature_enabled);
+			break;
+		case SDE_CP_CRTC_DSPP_HIST_IRQ:
+			if (!hw_dspp || !hw_lm) {
+				ret = -EINVAL;
+				continue;
+			}
+			if (!hw_lm->cfg.right_mixer)
+				_sde_cp_crtc_enable_hist_irq(sde_crtc);
+			break;
 		case SDE_CP_CRTC_DSPP_AD_MODE:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
 				ret = -EINVAL;
@@ -746,6 +845,8 @@
 		return;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
+
 	/* Check if dirty lists are empty and ad features are disabled for
 	 * early return. If ad properties are active then we need to issue
 	 * dspp flush.
@@ -754,7 +855,7 @@
 		list_empty(&sde_crtc->ad_dirty)) {
 		if (list_empty(&sde_crtc->ad_active)) {
 			DRM_DEBUG_DRIVER("Dirty list is empty\n");
-			return;
+			goto exit;
 		}
 		sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
 		set_dspp_flush = true;
@@ -794,6 +895,8 @@
 			ctl->ops.update_pending_flush(ctl, flush_mask);
 		}
 	}
+exit:
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 }
 
 void sde_cp_crtc_install_properties(struct drm_crtc *crtc)
@@ -824,13 +927,15 @@
 		return;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
+
 	/**
 	 * Function can be called during the atomic_check with test_only flag
 	 * and actual commit. Allocate properties only if feature list is
 	 * empty during the atomic_check with test_only flag.
 	 */
 	if (!list_empty(&sde_crtc->feature_list))
-		return;
+		goto exit;
 
 	catalog = kms->catalog;
 	priv = crtc->dev->dev_private;
@@ -846,7 +951,7 @@
 		setup_lm_prop_install_funcs(lm_prop_install_func);
 	}
 	if (!priv->cp_property)
-		return;
+		goto exit;
 
 	if (!catalog->dspp_count)
 		goto lm_property;
@@ -862,7 +967,7 @@
 
 lm_property:
 	if (!catalog->mixer_count)
-		return;
+		goto exit;
 
 	/* Check for all the LM properties and attach it to CRTC */
 	features = catalog->mixer[0].features;
@@ -872,6 +977,9 @@
 		if (lm_prop_install_func[i])
 			lm_prop_install_func[i](crtc);
 	}
+exit:
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
+
 }
 
 int sde_cp_crtc_set_property(struct drm_crtc *crtc,
@@ -894,6 +1002,7 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
 	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
 		if (property->base.id == prop_node->property_id) {
 			found = 1;
@@ -902,7 +1011,8 @@
 	}
 
 	if (!found)
-		return 0;
+		goto exit;
+
 	/**
 	 * sde_crtc is virtual ensure that hardware has been attached to the
 	 * crtc. Check LM and dspp counts based on whether feature is a
@@ -912,7 +1022,8 @@
 	    sde_crtc->num_mixers > ARRAY_SIZE(sde_crtc->mixers)) {
 		DRM_ERROR("Invalid mixer config act cnt %d max cnt %ld\n",
 			sde_crtc->num_mixers, ARRAY_SIZE(sde_crtc->mixers));
-		return -EINVAL;
+		ret = -EINVAL;
+		goto exit;
 	}
 
 	dspp_cnt = 0;
@@ -927,17 +1038,19 @@
 	if (prop_node->is_dspp_feature && dspp_cnt < sde_crtc->num_mixers) {
 		DRM_ERROR("invalid dspp cnt %d mixer cnt %d\n", dspp_cnt,
 			sde_crtc->num_mixers);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto exit;
 	} else if (lm_cnt < sde_crtc->num_mixers) {
 		DRM_ERROR("invalid lm cnt %d mixer cnt %d\n", lm_cnt,
 			sde_crtc->num_mixers);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto exit;
 	}
 
 	ret = sde_cp_ad_validate_prop(prop_node, sde_crtc);
 	if (ret) {
 		DRM_ERROR("ad property validation failed ret %d\n", ret);
-		return ret;
+		goto exit;
 	}
 
 	/* remove the property from dirty list */
@@ -955,6 +1068,8 @@
 		/* Mark the feature as dirty */
 		sde_cp_update_list(prop_node, sde_crtc, true);
 	}
+exit:
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 	return ret;
 }
 
@@ -977,12 +1092,14 @@
 	}
 	/* Return 0 if property is not supported */
 	*val = 0;
+	mutex_lock(&sde_crtc->crtc_cp_lock);
 	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
 		if (property->base.id == prop_node->property_id) {
 			*val = prop_node->prop_val;
 			break;
 		}
 	}
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 	return 0;
 }
 
@@ -1015,6 +1132,10 @@
 		kfree(prop_node);
 	}
 
+	if (sde_crtc->hist_blob)
+		drm_property_unreference_blob(sde_crtc->hist_blob);
+
+	mutex_destroy(&sde_crtc->crtc_cp_lock);
 	INIT_LIST_HEAD(&sde_crtc->active_list);
 	INIT_LIST_HEAD(&sde_crtc->dirty_list);
 	INIT_LIST_HEAD(&sde_crtc->feature_list);
@@ -1035,6 +1156,7 @@
 		return;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
 	list_for_each_entry_safe(prop_node, n, &sde_crtc->active_list,
 				 active_list) {
 		sde_cp_update_list(prop_node, sde_crtc, true);
@@ -1046,6 +1168,7 @@
 		sde_cp_update_list(prop_node, sde_crtc, true);
 		list_del_init(&prop_node->active_list);
 	}
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 }
 
 void sde_cp_crtc_resume(struct drm_crtc *crtc)
@@ -1273,6 +1396,30 @@
 	}
 }
 
+static void dspp_hist_install_property(struct drm_crtc *crtc)
+{
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+
+	version = catalog->dspp[0].sblk->hist.version >> 16;
+	switch (version) {
+	case 1:
+		sde_cp_crtc_install_enum_property(crtc,
+			SDE_CP_CRTC_DSPP_HIST_CTRL, sde_hist_modes,
+			ARRAY_SIZE(sde_hist_modes), "SDE_DSPP_HIST_CTRL_V1");
+		sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_HIST_IRQ_V1",
+			SDE_CP_CRTC_DSPP_HIST_IRQ, 0, U16_MAX, 0);
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
 static void sde_cp_update_list(struct sde_cp_node *prop_node,
 		struct sde_crtc *crtc, bool dirty_list)
 {
@@ -1394,6 +1541,7 @@
 	int i;
 	int irq_idx, ret;
 	struct sde_cp_node prop_node;
+	struct sde_crtc_irq_info *node = NULL;
 
 	if (!crtc_drm || !ad_irq) {
 		DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, ad_irq);
@@ -1438,8 +1586,23 @@
 		goto exit;
 	}
 
+	node = _sde_cp_get_intr_node(DRM_EVENT_AD_BACKLIGHT, crtc);
+
 	if (!en) {
-		sde_core_irq_disable(kms, &irq_idx, 1);
+		if (node) {
+			if (node->state == IRQ_ENABLED) {
+				ret = sde_core_irq_disable(kms, &irq_idx, 1);
+				if (ret)
+					DRM_ERROR("disable irq %d error %d\n",
+						irq_idx, ret);
+				else
+					node->state = IRQ_NOINIT;
+			} else {
+				node->state = IRQ_NOINIT;
+			}
+		} else {
+			DRM_ERROR("failed to get node from crtc event list\n");
+		}
 		sde_core_irq_unregister_callback(kms, irq_idx, ad_irq);
 		ret = 0;
 		goto exit;
@@ -1452,10 +1615,30 @@
 		DRM_ERROR("failed to register the callback ret %d\n", ret);
 		goto exit;
 	}
-	ret = sde_core_irq_enable(kms, &irq_idx, 1);
-	if (ret) {
-		DRM_ERROR("failed to enable irq ret %d\n", ret);
-		sde_core_irq_unregister_callback(kms, irq_idx, ad_irq);
+
+	if (node) {
+		/* device resume or resume from IPC cases */
+		if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
+			ret = sde_core_irq_enable(kms, &irq_idx, 1);
+			if (ret) {
+				DRM_ERROR("enable irq %d error %d\n",
+					irq_idx, ret);
+				sde_core_irq_unregister_callback(kms,
+					irq_idx, ad_irq);
+			} else {
+				node->state = IRQ_ENABLED;
+			}
+		}
+	} else {
+		/* request from userspace to register the event
+		 * in this case, node has not been added into the event list
+		 */
+		ret = sde_core_irq_enable(kms, &irq_idx, 1);
+		if (ret) {
+			DRM_ERROR("failed to enable irq ret %d\n", ret);
+			sde_core_irq_unregister_callback(kms,
+				irq_idx, ad_irq);
+		}
 	}
 exit:
 	return ret;
@@ -1518,3 +1701,201 @@
 
 	sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESUME);
 }
+
+static void sde_cp_hist_interrupt_cb(void *arg, int irq_idx)
+{
+	struct sde_crtc *crtc = arg;
+	struct drm_crtc *crtc_drm = &crtc->base;
+	struct sde_hw_dspp *hw_dspp;
+	struct sde_kms *kms;
+	struct sde_crtc_irq_info *node = NULL;
+	u32 i;
+	int ret = 0;
+	unsigned long flags;
+
+	/* disable histogram irq */
+	kms = get_kms(crtc_drm);
+	spin_lock_irqsave(&crtc->spin_lock, flags);
+	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc);
+	spin_unlock_irqrestore(&crtc->spin_lock, flags);
+
+	if (!node) {
+		DRM_ERROR("cannot find histogram event node in crtc\n");
+		return;
+	}
+
+	if (node->state == IRQ_ENABLED) {
+		if (sde_core_irq_disable_nolock(kms, irq_idx)) {
+			DRM_ERROR("failed to disable irq %d, ret %d\n",
+				irq_idx, ret);
+			return;
+		}
+		node->state = IRQ_DISABLED;
+	}
+
+	/* lock histogram buffer */
+	for (i = 0; i < crtc->num_mixers; i++) {
+		hw_dspp = crtc->mixers[i].hw_dspp;
+		if (hw_dspp && hw_dspp->ops.lock_histogram)
+			hw_dspp->ops.lock_histogram(hw_dspp, NULL);
+	}
+
+	/* notify histogram event */
+	sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event, NULL);
+}
+
+static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg)
+{
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_crtc *crtc;
+	struct drm_event event;
+	struct drm_msm_hist *hist_data;
+	struct drm_msm_hist tmp_hist_data;
+	u32 i, j;
+
+	if (!crtc_drm) {
+		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
+		return;
+	}
+
+	crtc = to_sde_crtc(crtc_drm);
+	if (!crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", crtc);
+		return;
+	}
+
+	if (!crtc->hist_blob)
+		return;
+
+	/* read histogram data into blob */
+	hist_data = (struct drm_msm_hist *)crtc->hist_blob->data;
+	for (i = 0; i < crtc->num_mixers; i++) {
+		hw_dspp = crtc->mixers[i].hw_dspp;
+		if (!hw_dspp || !hw_dspp->ops.read_histogram) {
+			DRM_ERROR("invalid dspp %pK or read_histogram func\n",
+				hw_dspp);
+			return;
+		}
+		if (!i) {
+			hw_dspp->ops.read_histogram(hw_dspp, hist_data);
+		} else {
+			/* Merge hist data for DSPP0 and DSPP1 */
+			hw_dspp->ops.read_histogram(hw_dspp, &tmp_hist_data);
+			for (j = 0; j < HIST_V_SIZE; j++)
+				hist_data->data[j] += tmp_hist_data.data[j];
+		}
+	}
+
+	/* send histogram event with blob id */
+	event.length = sizeof(u32);
+	event.type = DRM_EVENT_HISTOGRAM;
+	msm_mode_object_event_notify(&crtc_drm->base, crtc_drm->dev,
+			&event, (u8 *)(&crtc->hist_blob->base.id));
+}
+
+int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
+	struct sde_irq_callback *hist_irq)
+{
+	struct sde_kms *kms = NULL;
+	u32 num_mixers;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_crtc *crtc;
+	struct sde_crtc_irq_info *node = NULL;
+	int i, irq_idx, ret = 0;
+
+	if (!crtc_drm || !hist_irq) {
+		DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, hist_irq);
+		return -EINVAL;
+	}
+
+	crtc = to_sde_crtc(crtc_drm);
+	if (!crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", crtc);
+		return -EINVAL;
+	}
+
+	kms = get_kms(crtc_drm);
+	num_mixers = crtc->num_mixers;
+
+	for (i = 0; i < num_mixers; i++) {
+		hw_lm = crtc->mixers[i].hw_lm;
+		hw_dspp = crtc->mixers[i].hw_dspp;
+		if (!hw_lm->cfg.right_mixer)
+			break;
+	}
+
+	if (!hw_dspp) {
+		DRM_ERROR("invalid dspp\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
+			hw_dspp->idx);
+	if (irq_idx < 0) {
+		DRM_ERROR("failed to get the irq idx ret %d\n", irq_idx);
+		ret = irq_idx;
+		goto exit;
+	}
+
+	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc);
+
+	/* deregister histogram irq */
+	if (!en) {
+		if (node) {
+			/* device suspend case or suspend to IPC cases */
+			if (node->state == IRQ_ENABLED) {
+				ret = sde_core_irq_disable(kms, &irq_idx, 1);
+				if (ret)
+					DRM_ERROR("disable irq %d error %d\n",
+						irq_idx, ret);
+				else
+					node->state = IRQ_NOINIT;
+			} else {
+				node->state = IRQ_NOINIT;
+			}
+		} else {
+			DRM_ERROR("failed to get node from crtc event list\n");
+		}
+
+		sde_core_irq_unregister_callback(kms, irq_idx, hist_irq);
+		goto exit;
+	}
+
+	/* register histogram irq */
+	hist_irq->arg = crtc;
+	hist_irq->func = sde_cp_hist_interrupt_cb;
+	ret = sde_core_irq_register_callback(kms, irq_idx, hist_irq);
+	if (ret) {
+		DRM_ERROR("failed to register the callback ret %d\n", ret);
+		goto exit;
+	}
+
+	if (node) {
+		/* device resume or resume from IPC cases */
+		if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
+			ret = sde_core_irq_enable(kms, &irq_idx, 1);
+			if (ret) {
+				DRM_ERROR("enable irq %d error %d\n",
+					irq_idx, ret);
+				sde_core_irq_unregister_callback(kms,
+					irq_idx, hist_irq);
+			} else {
+				node->state = IRQ_ENABLED;
+			}
+		}
+	} else {
+		/* request from userspace to register the event
+		 * in this case, node has not been added into the event list
+		 */
+		ret = sde_core_irq_enable(kms, &irq_idx, 1);
+		if (ret) {
+			DRM_ERROR("failed to enable irq ret %d\n", ret);
+			sde_core_irq_unregister_callback(kms,
+				irq_idx, hist_irq);
+		}
+	}
+exit:
+	return ret;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index 08e345d..aff07ef 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -29,6 +29,25 @@
 	MEMCOLOR_FOLIAGE
 };
 
+/*
+ * PA HISTOGRAM modes
+ * @HIST_DISABLED          Histogram disabled
+ * @HIST_ENABLED           Histogram enabled
+ */
+enum sde_hist_modes {
+	HIST_DISABLED,
+	HIST_ENABLED
+};
+
+/**
+ * struct drm_prop_enum_list - drm structure for creating enum property and
+ *                             enumerating values
+ */
+static const struct drm_prop_enum_list sde_hist_modes[] = {
+	{HIST_DISABLED, "hist_off"},
+	{HIST_ENABLED, "hist_on"},
+};
+
 /**
  * sde_cp_crtc_init(): Initialize color processing lists for a crtc.
  *                     Should be called during crtc initialization.
@@ -117,4 +136,13 @@
  * @crtc: Pointer to crtc.
  */
 void sde_cp_crtc_post_ipc(struct drm_crtc *crtc);
+
+/**
+ * sde_cp_hist_interrupt: Api to enable/disable histogram interrupt
+ * @crtc: Pointer to crtc.
+ * @en: Variable to enable/disable interrupt.
+ * @irq: Pointer to irq callback
+ */
+int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
+	struct sde_irq_callback *hist_irq);
 #endif /*_SDE_COLOR_PROCESSING_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 4b7ec6a..23ca2e9 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -48,14 +48,6 @@
 #define MEM_PROTECT_SD_CTRL_SWITCH 0x18
 #define MDP_DEVICE_ID            0x1A
 
-struct sde_crtc_irq_info {
-	struct sde_irq_callback irq;
-	u32 event;
-	int (*func)(struct drm_crtc *crtc, bool en,
-			struct sde_irq_callback *irq);
-	struct list_head list;
-};
-
 struct sde_crtc_custom_events {
 	u32 event;
 	int (*func)(struct drm_crtc *crtc, bool en,
@@ -70,7 +62,8 @@
 static struct sde_crtc_custom_events custom_events[] = {
 	{DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt},
 	{DRM_EVENT_CRTC_POWER, sde_crtc_power_interrupt_handler},
-	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler}
+	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler},
+	{DRM_EVENT_HISTOGRAM, sde_cp_hist_interrupt},
 };
 
 /* default input fence timeout, in ms */
@@ -3229,7 +3222,7 @@
 static int _sde_crtc_wait_for_frame_done(struct drm_crtc *crtc)
 {
 	struct sde_crtc *sde_crtc;
-	int ret, rc = 0;
+	int ret, rc = 0, i;
 
 	if (!crtc) {
 		SDE_ERROR("invalid argument\n");
@@ -3242,7 +3235,17 @@
 		return 0;
 	}
 
-	SDE_EVT32_VERBOSE(DRMID(crtc), SDE_EVTLOG_FUNC_ENTRY);
+	SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_ENTRY);
+
+	/*
+	 * flush all the event thread work to make sure all the
+	 * FRAME_EVENTS from encoder are propagated to crtc
+	 */
+	for (i = 0; i < ARRAY_SIZE(sde_crtc->frame_events); i++) {
+		if (list_empty(&sde_crtc->frame_events[i].list))
+			kthread_flush_work(&sde_crtc->frame_events[i].work);
+	}
+
 	ret = wait_for_completion_timeout(&sde_crtc->frame_done_comp,
 			msecs_to_jiffies(SDE_FRAME_DONE_TIMEOUT));
 	if (!ret) {
@@ -5471,6 +5474,8 @@
 
 	if (!ret) {
 		spin_lock_irqsave(&crtc->spin_lock, flags);
+		/* irq is regiestered and enabled and set the state */
+		node->state = IRQ_ENABLED;
 		list_add_tail(&node->list, &crtc->user_event_list);
 		spin_unlock_irqrestore(&crtc->spin_lock, flags);
 	} else {
@@ -5494,7 +5499,6 @@
 	spin_lock_irqsave(&crtc->spin_lock, flags);
 	list_for_each_entry(node, &crtc->user_event_list, list) {
 		if (node->event == event) {
-			list_del(&node->list);
 			found = true;
 			break;
 		}
@@ -5510,12 +5514,15 @@
 	 * no need to disable/de-register.
 	 */
 	if (!crtc_drm->enabled) {
+		list_del(&node->list);
 		kfree(node);
 		return 0;
 	}
 	priv = kms->dev->dev_private;
 	sde_power_resource_enable(&priv->phandle, kms->core_client, true);
 	ret = node->func(crtc_drm, false, &node->irq);
+	list_del(&node->list);
+	kfree(node);
 	sde_power_resource_enable(&priv->phandle, kms->core_client, false);
 	return ret;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index c459c62..59bfc47 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -256,6 +256,7 @@
 	struct list_head user_event_list;
 
 	struct mutex crtc_lock;
+	struct mutex crtc_cp_lock;
 
 	atomic_t frame_pending;
 	struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
@@ -284,6 +285,9 @@
 	struct list_head rp_head;
 
 	struct sde_crtc_smmu_state_data smmu_state;
+
+	/* blob for histogram data */
+	struct drm_property_blob *hist_blob;
 };
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
@@ -406,6 +410,29 @@
 	struct sde_crtc_respool rp;
 };
 
+enum sde_crtc_irq_state {
+	IRQ_NOINIT,
+	IRQ_ENABLED,
+	IRQ_DISABLED,
+};
+
+/**
+ * sde_crtc_irq_info - crtc interrupt info
+ * @irq: interrupt callback
+ * @event: event type of the interrupt
+ * @func: function pointer to enable/disable the interrupt
+ * @list: list of user customized event in crtc
+ * @ref_count: reference count for the interrupt
+ */
+struct sde_crtc_irq_info {
+	struct sde_irq_callback irq;
+	u32 event;
+	int (*func)(struct drm_crtc *crtc, bool en,
+			struct sde_irq_callback *irq);
+	struct list_head list;
+	enum sde_crtc_irq_state state;
+};
+
 #define to_sde_crtc_state(x) \
 	container_of(x, struct sde_crtc_state, base)
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 304106d..7a391ae 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -81,6 +81,7 @@
 		if (cdm == m->cdm[i].id) {
 			b->base_off = addr;
 			b->blk_off = m->cdm[i].base;
+			b->length = m->cdm[i].len;
 			b->hwversion = m->hwversion;
 			b->log_mask = SDE_DBG_MASK_CDM;
 			return &m->cdm[i];
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index c8e732a..8e54a2a 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
@@ -24,6 +24,9 @@
 #define PA_VAL_DSPP_OFF		0x240
 #define PA_CONT_DSPP_OFF	0x244
 
+#define PA_HIST_CTRL_DSPP_OFF	0x4
+#define PA_HIST_DATA_DSPP_OFF	0x400
+
 #define PA_LUTV_DSPP_OFF	0x1400
 #define PA_LUT_SWAP_OFF		0x234
 
@@ -70,6 +73,7 @@
 #define DSPP_OP_PA_CONT_EN	BIT(28)
 #define DSPP_OP_PA_EN		BIT(20)
 #define DSPP_OP_PA_LUTV_EN	BIT(19)
+#define DSPP_OP_PA_HIST_EN	BIT(16)
 #define DSPP_OP_PA_SKIN_EN	BIT(5)
 #define DSPP_OP_PA_FOL_EN	BIT(6)
 #define DSPP_OP_PA_SKY_EN	BIT(7)
@@ -563,3 +567,69 @@
 	i = BIT(0) | ((payload->flags & PGC_8B_ROUND) ? BIT(1) : 0);
 	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, i);
 }
+
+void sde_setup_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	u32 base, offset;
+	u32 op_mode;
+	bool feature_enabled;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid parameters ctx %pK cfg %pK", ctx, cfg);
+		return;
+	}
+
+	feature_enabled = *(bool *)cfg;
+	base = ctx->cap->sblk->hist.base;
+	offset = base + PA_HIST_CTRL_DSPP_OFF;
+
+	op_mode = SDE_REG_READ(&ctx->hw, base);
+	if (!feature_enabled) {
+		op_mode &= ~DSPP_OP_PA_HIST_EN;
+		if (PA_DSPP_DISABLE_REQUIRED(op_mode))
+			op_mode &= ~DSPP_OP_PA_EN;
+	} else {
+		op_mode |= DSPP_OP_PA_HIST_EN | DSPP_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, offset, 0);
+	SDE_REG_WRITE(&ctx->hw, base, op_mode);
+}
+
+void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_hist *hist_data;
+	u32 offset, offset_ctl;
+	u32 i;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid parameters ctx %pK cfg %pK", ctx, cfg);
+		return;
+	}
+
+	hist_data = (struct drm_msm_hist *)cfg;
+	offset = ctx->cap->sblk->hist.base + PA_HIST_DATA_DSPP_OFF;
+	offset_ctl = ctx->cap->sblk->hist.base + PA_HIST_CTRL_DSPP_OFF;
+
+	for (i = 0; i < HIST_V_SIZE; i++)
+		hist_data->data[i] = SDE_REG_READ(&ctx->hw, offset + i * 4) &
+					REG_MASK(24);
+
+	/* unlock hist buffer */
+	SDE_REG_WRITE(&ctx->hw, offset_ctl, 0);
+}
+
+void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	u32 offset_ctl;
+
+	if (!ctx) {
+		DRM_ERROR("invalid parameters ctx %pK", ctx);
+		return;
+	}
+
+	offset_ctl = ctx->cap->sblk->hist.base + PA_HIST_CTRL_DSPP_OFF;
+
+	/* lock hist buffer */
+	SDE_REG_WRITE(&ctx->hw, offset_ctl, 1);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
index 4cd2e5a..74018a3 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
@@ -89,4 +89,23 @@
  */
 void sde_setup_dspp_gc_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 
+/**
+ * sde_setup_dspp_hist_v1_7 - setup DSPP histogram feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to histogram control data
+ */
+void sde_setup_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_read_dspp_hist_v1_7 - read DSPP histogram data in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to histogram data
+ */
+void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_lock_dspp_hist_v1_7 - lock DSPP histogram buffer in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ */
+void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 #endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index d30c0ae..36e30b7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -90,6 +90,17 @@
 					sde_setup_dspp_pa_vlut_v1_8;
 			}
 			break;
+		case SDE_DSPP_HIST:
+			if (c->cap->sblk->hist.version ==
+				(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
+				c->ops.setup_histogram =
+				    sde_setup_dspp_hist_v1_7;
+				c->ops.read_histogram =
+				    sde_read_dspp_hist_v1_7;
+				c->ops.lock_histogram =
+				    sde_lock_dspp_hist_v1_7;
+			}
+			break;
 		case SDE_DSPP_GAMUT:
 			if (c->cap->sblk->gamut.version ==
 					SDE_COLOR_PROCESS_VER(0x4, 0)) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 44b3831..4878fc6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -38,6 +38,13 @@
 	void (*read_histogram)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
+	 * lock_histogram - lock dspp histogram buffer
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*lock_histogram)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
 	 * setup_igc - update dspp igc
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
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_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index bf06dfb..81eb851 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1569,15 +1569,132 @@
 	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_check_secure_transition(struct msm_kms *kms,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index f4672b8..ef4eac1 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -2868,20 +2868,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);
diff --git a/drivers/gpu/drm/msm/sde/sde_reg_dma.c b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
index e38524f..a52abd9 100644
--- a/drivers/gpu/drm/msm/sde/sde_reg_dma.c
+++ b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
@@ -99,9 +99,6 @@
 		rc = init_v1(&reg_dma);
 		if (rc)
 			DRM_DEBUG("init v1 dma ops failed\n");
-		else
-			sde_dbg_reg_register_base("reg_dma", addr,
-					reg_dma.caps->len);
 		break;
 	default:
 		break;
diff --git a/drivers/gpu/drm/msm/sde/sde_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_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/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..d1dad8a 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -325,7 +325,7 @@
 		.core = 6,
 		.major = 3,
 		.minor = 0,
-		.patchid = ANY_ID,
+		.patchid = 0,
 		.features = ADRENO_64BIT | ADRENO_RPMH |
 			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
 		.sqefw_name = "a630_sqe.fw",
@@ -341,6 +341,26 @@
 		.max_power = 5448,
 	},
 	{
+		.gpurev = ADRENO_REV_A630,
+		.core = 6,
+		.major = 3,
+		.minor = 0,
+		.patchid = ANY_ID,
+		.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_IFPC |
+			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
+		.sqefw_name = "a630_sqe.fw",
+		.zap_name = "a630_zap",
+		.gpudev = &adreno_a6xx_gpudev,
+		.gmem_size = SZ_1M,
+		.num_protected_regs = 0x20,
+		.busy_mask = 0xFFFFFFFE,
+		.gpmufw_name = "a630_gmu.bin",
+		.gpmu_major = 0x0,
+		.gpmu_minor = 0x005,
+		.gpmu_tsens = 0x000C000D,
+		.max_power = 5448,
+	},
+	{
 		.gpurev = ADRENO_REV_A615,
 		.core = 6,
 		.major = 1,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 844142a..f55ce04 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,
@@ -1389,9 +1397,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 +1414,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;
 	}
 }
 
@@ -1712,8 +1723,6 @@
 		}
 	}
 
-	adreno_set_active_ctxs_null(adreno_dev);
-
 	adreno_dispatcher_stop(adreno_dev);
 
 	adreno_ringbuffer_stop(adreno_dev);
@@ -1752,6 +1761,12 @@
 
 	kgsl_mmu_stop(&device->mmu);
 
+	/*
+	 * At this point, MMU is turned off so we can safely
+	 * destroy any pending contexts and their pagetables
+	 */
+	adreno_set_active_ctxs_null(adreno_dev);
+
 	clear_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
 
 	return 0;
@@ -2377,9 +2392,9 @@
 	int ret;
 
 	if (gpudev->oob_set) {
-		ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
-				OOB_CPINIT_CHECK_MASK,
-				OOB_CPINIT_CLEAR_MASK);
+		ret = gpudev->oob_set(adreno_dev, OOB_GPU_SET_MASK,
+				OOB_GPU_CHECK_MASK,
+				OOB_GPU_CLEAR_MASK);
 		if (ret)
 			return ret;
 	}
@@ -2403,7 +2418,7 @@
 		ret = _soft_reset(adreno_dev);
 	if (ret) {
 		if (gpudev->oob_clear)
-			gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+			gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
 		return ret;
 	}
 
@@ -2453,7 +2468,7 @@
 	adreno_perfcounter_restore(adreno_dev);
 
 	if (gpudev->oob_clear)
-		gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+		gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
 
 	return ret;
 }
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 3118375..fc7801b 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
@@ -258,6 +260,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 +270,9 @@
 	struct timer_list timer;
 	struct work_struct work;
 	bool token_submit;
+	unsigned int preempt_level;
+	bool skipsaverestore;
+	bool usesgmem;
 };
 
 
@@ -483,10 +491,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_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 434fef8..f3e8650 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -1277,8 +1277,8 @@
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	const struct firmware *fw;
-	uint32_t block_size = 0, block_total = 0, fw_size;
-	uint32_t *block;
+	uint64_t block_size = 0, block_total = 0;
+	uint32_t fw_size, *block;
 	int ret = -EINVAL;
 
 	if (!adreno_dev->gpucore->regfw_name)
@@ -1300,7 +1300,8 @@
 	/* All offset numbers calculated from file description */
 	while (block_total < fw_size) {
 		block_size = block[0];
-		if (block_size >= fw_size || block_size < 2)
+		if (((block_total + block_size) >= fw_size)
+				|| block_size < 5)
 			goto err;
 		if (block[1] != GPMU_SEQUENCE_ID)
 			goto err;
@@ -1316,6 +1317,9 @@
 				goto err;
 
 			adreno_dev->lm_fw = fw;
+
+			if (block[2] > (block_size - 2))
+				goto err;
 			adreno_dev->lm_sequence = block + block[2] + 3;
 			adreno_dev->lm_size = block_size - block[2] - 2;
 		}
@@ -1328,7 +1332,7 @@
 err:
 	release_firmware(fw);
 	KGSL_PWR_ERR(device,
-		"Register file failed to load sz=%d bsz=%d header=%d\n",
+		"Register file failed to load sz=%d bsz=%llu header=%d\n",
 		fw_size, block_size, ret);
 }
 
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index f56af39..453ef4f 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,20 +143,20 @@
 	{A6XX_RBBM_CLOCK_CNTL2_RB1, 0x00002222},
 	{A6XX_RBBM_CLOCK_CNTL2_RB2, 0x00002222},
 	{A6XX_RBBM_CLOCK_CNTL2_RB3, 0x00002222},
-	{A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00002220},
-	{A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00002220},
-	{A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00002220},
-	{A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00002220},
+	{A6XX_RBBM_CLOCK_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, 0x05022022},
-	{A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005555},
+	{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, 0x04222222},
-	{A6XX_RBBM_CLOCK_MODE_GPC, 0x02222222},
+	{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},
@@ -265,7 +265,7 @@
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 
 	if (kgsl_allocate_global(device, &adreno_dev->pwrup_reglist,
-		PAGE_SIZE, 0, KGSL_MEMDESC_PRIVILEGED,
+		PAGE_SIZE, KGSL_MEMFLAGS_GPUREADONLY, 0,
 		"powerup_register_list")) {
 		adreno_dev->pwrup_reglist.gpuaddr = 0;
 		return;
@@ -699,7 +699,7 @@
 		*cmds++ = lower_32_bits(gpuaddr);
 		*cmds++ = upper_32_bits(gpuaddr);
 		/* Size is in dwords */
-		*cmds++ = sizeof(a6xx_pwrup_reglist) >> 2;
+		*cmds++ = 0;
 	}
 
 	/* Pad rest of the cmds with 0's */
@@ -937,42 +937,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);
@@ -1014,7 +1014,6 @@
 			return 0;
 		/* Wait 100us to reduce unnecessary AHB bus traffic */
 		udelay(100);
-		cond_resched();
 	} while (!time_after(jiffies, t));
 
 	/* Double check one last time */
@@ -1411,7 +1410,9 @@
 	int val;
 
 	kgsl_gmu_regread(device, A6XX_GPU_CC_GX_DOMAIN_MISC, &val);
-	WARN_ON(!(val & 0x1));
+	if (!(val & 0x1))
+		dev_err_ratelimited(&gmu->pdev->dev,
+			"GMEM CLAMP IO not set while GFX rail off\n");
 
 	/* RSC wake sequence */
 	kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, BIT(1));
@@ -1778,7 +1779,6 @@
 
 		/* Wait 100us to reduce unnecessary AHB bus traffic */
 		udelay(100);
-		cond_resched();
 	}
 
 	/* Check one last time */
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_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c
index 2d001af6..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,
@@ -220,6 +232,23 @@
 	return 0;
 }
 
+static int _gmu_idle_level_store(struct adreno_device *adreno_dev,
+		unsigned int val)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+
+	mutex_lock(&device->mutex);
+
+	/* Power down the GPU before changing the idle level */
+	kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND);
+	gmu->idle_level = val;
+	kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER);
+
+	mutex_unlock(&device->mutex);
+	return 0;
+}
+
 static unsigned int _preemption_show(struct adreno_device *adreno_dev)
 {
 	return adreno_is_preemption_execution_enabled(adreno_dev);
@@ -268,6 +297,40 @@
 	return test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag);
 }
 
+static int _ifpc_store(struct adreno_device *adreno_dev, unsigned int val)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+	unsigned int requested_idle_level;
+
+	if (!kgsl_gmu_isenabled(device) ||
+			!ADRENO_FEATURE(adreno_dev, ADRENO_IFPC))
+		return -EINVAL;
+
+	if ((val && gmu->idle_level >= GPU_HW_IFPC) ||
+			(!val && gmu->idle_level < GPU_HW_IFPC))
+		return 0;
+
+	if (val)
+		requested_idle_level = GPU_HW_IFPC;
+	else {
+		if (ADRENO_FEATURE(adreno_dev, ADRENO_SPTP_PC))
+			requested_idle_level = GPU_HW_SPTP_PC;
+		else
+			requested_idle_level = GPU_HW_ACTIVE;
+	}
+
+	return _gmu_idle_level_store(adreno_dev, requested_idle_level);
+}
+
+static unsigned int _ifpc_show(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+
+	return kgsl_gmu_isenabled(device) && gmu->idle_level >= GPU_HW_IFPC;
+}
+
 static ssize_t _sysfs_store_u32(struct device *dev,
 		struct device_attribute *attr,
 		const char *buf, size_t count)
@@ -366,6 +429,7 @@
 static ADRENO_SYSFS_BOOL(preemption);
 static ADRENO_SYSFS_BOOL(hwcg);
 static ADRENO_SYSFS_BOOL(throttling);
+static ADRENO_SYSFS_BOOL(ifpc);
 
 
 
@@ -386,6 +450,7 @@
 	&adreno_attr_preempt_level.attr,
 	&adreno_attr_usesgmem.attr,
 	&adreno_attr_skipsaverestore.attr,
+	&adreno_attr_ifpc.attr,
 	NULL,
 };
 
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 0a7d165..7da90c6 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2763,6 +2763,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 +3465,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 +3494,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 +3724,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;
 
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 78ef8e5..07bc00a 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;
 }
 
@@ -1300,37 +1308,6 @@
 	return -ETIMEDOUT;
 }
 
-static int gmu_fast_boot(struct kgsl_device *device)
-{
-	int ret;
-	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
-	struct gmu_device *gmu = &device->gmu;
-
-	hfi_stop(gmu);
-	clear_bit(GMU_HFI_ON, &gmu->flags);
-
-	ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
-		GMU_RESET, 0);
-	if (ret)
-		return ret;
-
-	/*FIXME: enabling WD interrupt*/
-
-	ret = hfi_start(gmu, GMU_WARM_BOOT);
-	if (ret)
-		return ret;
-
-	ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
-			OOB_CPINIT_CHECK_MASK, OOB_CPINIT_CLEAR_MASK);
-
-	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
-		gpudev->oob_clear(adreno_dev,
-				OOB_BOOT_SLUMBER_CLEAR_MASK);
-
-	return ret;
-}
-
 static int gmu_suspend(struct kgsl_device *device)
 {
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -1464,14 +1441,19 @@
 			/* Send DCVS level prior to reset*/
 			kgsl_pwrctrl_pwrlevel_change(device,
 				pwr->default_pwrlevel);
+		} else {
+			/* GMU fast boot */
+			hfi_stop(gmu);
 
-			ret = gpudev->oob_set(adreno_dev,
-				OOB_CPINIT_SET_MASK,
-				OOB_CPINIT_CHECK_MASK,
-				OOB_CPINIT_CLEAR_MASK);
+			ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
+					GMU_RESET, 0);
+			if (ret)
+				goto error_gmu;
 
-		} else
-			gmu_fast_boot(device);
+			ret = hfi_start(gmu, GMU_WARM_BOOT);
+			if (ret)
+				goto error_gmu;
+		}
 		break;
 	default:
 		break;
@@ -1480,6 +1462,9 @@
 	return ret;
 
 error_gmu:
+	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
+		gpudev->oob_clear(adreno_dev,
+				OOB_BOOT_SLUMBER_CLEAR_MASK);
 	gmu_snapshot(device);
 	return ret;
 }
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index fc6bafa..adabbc2 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -70,15 +70,12 @@
 #define OOB_DCVS_SET_MASK		BIT(23)
 #define OOB_DCVS_CHECK_MASK		BIT(31)
 #define OOB_DCVS_CLEAR_MASK		BIT(31)
-#define OOB_CPINIT_SET_MASK		BIT(16)
-#define OOB_CPINIT_CHECK_MASK		BIT(24)
-#define OOB_CPINIT_CLEAR_MASK		BIT(24)
+#define OOB_GPU_SET_MASK		BIT(16)
+#define OOB_GPU_CHECK_MASK		BIT(24)
+#define OOB_GPU_CLEAR_MASK		BIT(24)
 #define OOB_PERFCNTR_SET_MASK		BIT(17)
 #define OOB_PERFCNTR_CHECK_MASK		BIT(25)
 #define OOB_PERFCNTR_CLEAR_MASK		BIT(25)
-#define OOB_GPU_SET_MASK		BIT(18)
-#define OOB_GPU_CHECK_MASK		BIT(26)
-#define OOB_GPU_CLEAR_MASK		BIT(26)
 
 /* Bits for the flags field in the gmu structure */
 enum gmu_flags {
diff --git a/drivers/gpu/msm/kgsl_hfi.h b/drivers/gpu/msm/kgsl_hfi.h
index 191987e..105599c 100644
--- a/drivers/gpu/msm/kgsl_hfi.h
+++ b/drivers/gpu/msm/kgsl_hfi.h
@@ -115,7 +115,7 @@
 	HFI_F2H_QPRI_DEBUG = 40,
 };
 
-#define HFI_RSP_TIMEOUT 500 /* msec */
+#define HFI_RSP_TIMEOUT 5000 /* msec */
 #define HFI_H2F_CMD_IRQ_MASK BIT(0)
 
 enum hfi_msg_type {
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 8ea4492..a0fd3ec 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -26,6 +26,17 @@
 
 static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable);
 
+static void _deferred_destroy(struct work_struct *ws)
+{
+	struct kgsl_pagetable *pagetable = container_of(ws,
+					struct kgsl_pagetable, destroy_ws);
+
+	if (PT_OP_VALID(pagetable, mmu_destroy_pagetable))
+		pagetable->pt_ops->mmu_destroy_pagetable(pagetable);
+
+	kfree(pagetable);
+}
+
 static void kgsl_destroy_pagetable(struct kref *kref)
 {
 	struct kgsl_pagetable *pagetable = container_of(kref,
@@ -33,10 +44,7 @@
 
 	kgsl_mmu_detach_pagetable(pagetable);
 
-	if (PT_OP_VALID(pagetable, mmu_destroy_pagetable))
-		pagetable->pt_ops->mmu_destroy_pagetable(pagetable);
-
-	kfree(pagetable);
+	kgsl_schedule_work(&pagetable->destroy_ws);
 }
 
 static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable)
@@ -299,6 +307,7 @@
 	kref_init(&pagetable->refcount);
 
 	spin_lock_init(&pagetable->lock);
+	INIT_WORK(&pagetable->destroy_ws, _deferred_destroy);
 
 	pagetable->mmu = mmu;
 	pagetable->name = name;
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 56bb317..7a8ab74 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -42,6 +42,7 @@
 	struct list_head list;
 	unsigned int name;
 	struct kobject *kobj;
+	struct work_struct destroy_ws;
 
 	struct {
 		atomic_t entries;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 6e396682..db948a9 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;
 }
@@ -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;
 }
@@ -2485,9 +2485,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/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index a597ef9..c4da780 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -27,17 +27,11 @@
 #include <linux/spmi.h>
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
-#ifdef CONFIG_WAKELOCK
-#include <linux/wakelock.h>
-#endif
 #include <linux/interrupt.h>
 #include <linux/completion.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/platform_device.h>
-#ifdef CONFIG_WAKELOCK
-#include <linux/wakelock.h>
-#endif
 
 /* QPNP IADC register definition */
 #define QPNP_IADC_REVISION1				0x0
diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c
index 496738c..7ef2710 100644
--- a/drivers/hwtracing/coresight/coresight-byte-cntr.c
+++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c
@@ -341,7 +341,7 @@
 	if (byte_cntr_irq < 0)
 		return NULL;
 
-	byte_cntr_data = devm_kmalloc(dev, sizeof(*byte_cntr_data), GFP_KERNEL);
+	byte_cntr_data = devm_kzalloc(dev, sizeof(*byte_cntr_data), GFP_KERNEL);
 	if (!byte_cntr_data)
 		return NULL;
 
@@ -363,7 +363,6 @@
 	}
 
 	tmcdrvdata = drvdata;
-	byte_cntr_data->block_size = 0;
 	byte_cntr_data->byte_cntr_irq = byte_cntr_irq;
 	atomic_set(&byte_cntr_data->irq_cnt, 0);
 	init_waitqueue_head(&byte_cntr_data->wq);
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 71e4103..ee4b8b4 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -976,7 +976,12 @@
 
 	spin_lock_init(&drvdata->spinlock);
 
-	drvdata->cpu = pdata ? pdata->cpu : 0;
+	drvdata->cpu = pdata ? pdata->cpu : -1;
+
+	if (drvdata->cpu == -1) {
+		dev_info(dev, "CPU not available\n");
+		return -ENODEV;
+	}
 
 	get_online_cpus();
 
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 9e024ce..eb70e7a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -994,7 +994,7 @@
 		goto out;
 	}
 
-	if (!drvdata->byte_cntr || drvdata->byte_cntr->enable) {
+	if (drvdata->byte_cntr && drvdata->byte_cntr->enable) {
 		ret = -EINVAL;
 		goto out;
 	}
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index 81bbd78..5473fcf 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -182,8 +182,8 @@
 		} while (ep);
 	}
 
-	/* Affinity defaults to CPU0 */
-	pdata->cpu = 0;
+	/* Affinity defaults to invalid */
+	pdata->cpu = -1;
 	dn = of_parse_phandle(node, "cpu", 0);
 	for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) {
 		if (dn == of_get_cpu_node(cpu, NULL)) {
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 7e9999b..2460ba7 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -910,8 +910,23 @@
 
 static int geni_i2c_suspend_noirq(struct device *device)
 {
-	if (!pm_runtime_status_suspended(device))
+	struct geni_i2c_dev *gi2c = dev_get_drvdata(device);
+	int ret;
+
+	/* Make sure no transactions are pending */
+	ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
+	if (!ret) {
+		GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
+				"late I2C transaction request\n");
 		return -EBUSY;
+	}
+	if (!pm_runtime_status_suspended(device)) {
+		geni_i2c_runtime_suspend(device);
+		pm_runtime_disable(device);
+		pm_runtime_set_suspended(device);
+		pm_runtime_enable(device);
+	}
+	i2c_unlock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
 	return 0;
 }
 #else
diff --git a/drivers/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/mailbox/msm_qmp.c b/drivers/mailbox/msm_qmp.c
index 3b07c47..d6e41ae 100644
--- a/drivers/mailbox/msm_qmp.c
+++ b/drivers/mailbox/msm_qmp.c
@@ -359,7 +359,7 @@
 	addr = mdev->msgram + mbox->mcore_mbox_offset;
 	if (mbox->tx_sent) {
 		spin_unlock_irqrestore(&mbox->tx_lock, flags);
-		return -EBUSY;
+		return -EAGAIN;
 	}
 
 	if (pkt->size + sizeof(pkt->size) > mbox->mcore_mbox_size) {
diff --git a/drivers/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/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c
index 2c90e47..be7f4a1 100644
--- a/drivers/media/platform/msm/broadcast/tspp.c
+++ b/drivers/media/platform/msm/broadcast/tspp.c
@@ -36,7 +36,6 @@
 #include <linux/regulator/consumer.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include <linux/msm-sps.h>            /* BAM stuff */
-#include <linux/wakelock.h>      /* Locking functions */
 #include <linux/timer.h>         /* Timer services */
 #include <linux/jiffies.h>       /* Jiffies counter */
 #include <linux/qcom_tspp.h>
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
index 6c8bde1..3fbb3f0 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
@@ -338,6 +338,7 @@
 				}
 			} else {
 				client->stream_on = false;
+				rc = 0;
 				CAM_DBG(CAM_CDM,
 					"Client stream off success =%d",
 					cdm_hw->open_count);
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c
index b230d4e..d76f344 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c
@@ -131,8 +131,10 @@
 				cdm_cmd->cmd[i].len, client->data.base_array,
 				client->data.base_array_cnt, core->bl_tag);
 			if (rc) {
-				CAM_ERR(CAM_CDM, "write failed for cnt=%d:%d",
-					i, req->data->cmd_arrary_count);
+				CAM_ERR(CAM_CDM,
+					"write failed for cnt=%d:%d len %u",
+					i, req->data->cmd_arrary_count,
+					cdm_cmd->cmd[i].len);
 				break;
 			}
 		} else {
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index 3a78b5e..6e48c6a 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -65,7 +65,8 @@
 
 	rc = cam_context_handle_acquire_dev(ctx, acquire);
 	if (rc) {
-		CAM_ERR(CAM_CORE, "Acquire device failed");
+		CAM_ERR(CAM_CORE, "Acquire device failed for node %s",
+			node->name);
 		goto free_ctx;
 	}
 
@@ -82,6 +83,7 @@
 	struct cam_start_stop_dev_cmd *start)
 {
 	struct cam_context *ctx = NULL;
+	int rc;
 
 	if (!start)
 		return -EINVAL;
@@ -103,13 +105,18 @@
 		return -EINVAL;
 	}
 
-	return cam_context_handle_start_dev(ctx, start);
+	rc = cam_context_handle_start_dev(ctx, start);
+	if (rc)
+		CAM_ERR(CAM_CORE, "Start failure for node %s", node->name);
+
+	return rc;
 }
 
 static int __cam_node_handle_stop_dev(struct cam_node *node,
 	struct cam_start_stop_dev_cmd *stop)
 {
 	struct cam_context *ctx = NULL;
+	int rc;
 
 	if (!stop)
 		return -EINVAL;
@@ -131,13 +138,18 @@
 		return -EINVAL;
 	}
 
-	return cam_context_handle_stop_dev(ctx, stop);
+	rc = cam_context_handle_stop_dev(ctx, stop);
+	if (rc)
+		CAM_ERR(CAM_CORE, "Stop failure for node %s", node->name);
+
+	return rc;
 }
 
 static int __cam_node_handle_config_dev(struct cam_node *node,
 	struct cam_config_dev_cmd *config)
 {
 	struct cam_context *ctx = NULL;
+	int rc;
 
 	if (!config)
 		return -EINVAL;
@@ -159,7 +171,11 @@
 		return -EINVAL;
 	}
 
-	return cam_context_handle_config_dev(ctx, config);
+	rc = cam_context_handle_config_dev(ctx, config);
+	if (rc)
+		CAM_ERR(CAM_CORE, "Config failure for node %s", node->name);
+
+	return rc;
 }
 
 static int __cam_node_handle_release_dev(struct cam_node *node,
@@ -183,18 +199,19 @@
 
 	ctx = (struct cam_context *)cam_get_device_priv(release->dev_handle);
 	if (!ctx) {
-		CAM_ERR(CAM_CORE, "Can not get context for handle %d",
-			release->dev_handle);
+		CAM_ERR(CAM_CORE, "Can not get context for handle %d node %s",
+			release->dev_handle, node->name);
 		return -EINVAL;
 	}
 
 	rc = cam_context_handle_release_dev(ctx, release);
 	if (rc)
-		CAM_ERR(CAM_CORE, "context release failed");
+		CAM_ERR(CAM_CORE, "context release failed node %s", node->name);
 
 	rc = cam_destroy_device_hdl(release->dev_handle);
 	if (rc)
-		CAM_ERR(CAM_CORE, "destroy device handle is failed");
+		CAM_ERR(CAM_CORE, "destroy device handle is failed node %s",
+			node->name);
 
 	mutex_lock(&node->list_mutex);
 	list_add_tail(&ctx->list, &node->free_ctx_list);
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 11a81d6..b8a5685 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
@@ -661,7 +661,6 @@
 
 	if (fd_hw->open_count > 0) {
 		rc = 0;
-		mutex_unlock(&fd_hw->hw_mutex);
 		goto cdm_streamon;
 	}
 
@@ -681,12 +680,13 @@
 
 	fd_hw->hw_state = CAM_HW_STATE_POWER_UP;
 	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+
+cdm_streamon:
 	fd_hw->open_count++;
 	CAM_DBG(CAM_FD, "FD HW Init ref count after %d", fd_hw->open_count);
 
 	mutex_unlock(&fd_hw->hw_mutex);
 
-cdm_streamon:
 	if (init_args->ctx_hw_private) {
 		struct cam_fd_ctx_hw_private *ctx_hw_private =
 			init_args->ctx_hw_private;
@@ -712,7 +712,7 @@
 int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size)
 {
 	struct cam_hw_info *fd_hw = hw_priv;
-	struct cam_fd_core *fd_core;
+	struct cam_fd_core *fd_core = NULL;
 	struct cam_fd_hw_deinit_args *deinit_args =
 		(struct cam_fd_hw_deinit_args *)deinit_hw_args;
 	int rc = 0;
@@ -728,23 +728,7 @@
 		return -EINVAL;
 	}
 
-	fd_core = (struct cam_fd_core *)fd_hw->core_info;
-
-	if (deinit_args->ctx_hw_private) {
-		struct cam_fd_ctx_hw_private *ctx_hw_private =
-			deinit_args->ctx_hw_private;
-
-		rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle);
-		if (rc) {
-			CAM_ERR(CAM_FD,
-				"Failed in CDM StreamOff, handle=0x%x, rc=%d",
-				ctx_hw_private->cdm_handle, rc);
-			return rc;
-		}
-	}
-
 	mutex_lock(&fd_hw->hw_mutex);
-
 	if (fd_hw->open_count == 0) {
 		mutex_unlock(&fd_hw->hw_mutex);
 		CAM_ERR(CAM_FD, "Error Unbalanced deinit");
@@ -754,9 +738,9 @@
 	fd_hw->open_count--;
 	CAM_DBG(CAM_FD, "FD HW ref count=%d", fd_hw->open_count);
 
-	if (fd_hw->open_count) {
+	if (fd_hw->open_count > 0) {
 		rc = 0;
-		goto unlock_return;
+		goto positive_ref_cnt;
 	}
 
 	rc = cam_fd_soc_disable_resources(&fd_hw->soc_info);
@@ -764,9 +748,26 @@
 		CAM_ERR(CAM_FD, "Failed in Disable SOC, rc=%d", rc);
 
 	fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+	fd_core = (struct cam_fd_core *)fd_hw->core_info;
+
+	/* With the ref_cnt correct, this should never happen */
+	WARN_ON(!fd_core);
+
 	fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN;
 
-unlock_return:
+positive_ref_cnt:
+	if (deinit_args->ctx_hw_private) {
+		struct cam_fd_ctx_hw_private *ctx_hw_private =
+			deinit_args->ctx_hw_private;
+
+		rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle);
+		if (rc) {
+			CAM_ERR(CAM_FD,
+				"Failed in CDM StreamOff, handle=0x%x, rc=%d",
+				ctx_hw_private->cdm_handle, rc);
+		}
+	}
+
 	mutex_unlock(&fd_hw->hw_mutex);
 	return rc;
 }
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 3354e2c..3844673 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -47,7 +47,6 @@
 #include "cam_debug_util.h"
 #include "cam_soc_util.h"
 
-#define ICP_WORKQ_NUM_TASK      30
 #define ICP_WORKQ_TASK_CMD_TYPE 1
 #define ICP_WORKQ_TASK_MSG_TYPE 2
 
@@ -295,6 +294,7 @@
 {
 	uint32_t next_clk_level;
 	uint32_t actual_clk;
+	bool rc = false;
 
 	/* 1. if current request frame cycles(fc) are more than previous
 	 *      frame fc
@@ -308,7 +308,8 @@
 	 * 2. if current fc is less than or equal to previous  frame fc
 	 *      Still Bump up the clock to next available level
 	 *      if it is available, then update clock, make overclk cnt to
-	 *      zero
+	 *      zero. If the clock is already at highest clock rate then
+	 *      no need to update the clock
 	 */
 	mutex_lock(&hw_mgr->hw_mgr_mutex);
 	ctx_data->clk_info.curr_fc = clk_info->frame_cycles;
@@ -326,14 +327,19 @@
 				ctx_data, hw_mgr_clk_info->curr_clk);
 			hw_mgr_clk_info->curr_clk = next_clk_level;
 		}
+		rc = true;
 	} else {
-		hw_mgr_clk_info->curr_clk =
+		next_clk_level =
 			cam_icp_get_next_clk_rate(hw_mgr, ctx_data,
 			hw_mgr_clk_info->curr_clk);
+		if (hw_mgr_clk_info->curr_clk < next_clk_level) {
+			hw_mgr_clk_info->curr_clk = next_clk_level;
+			rc = true;
+		}
 	}
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
-	return true;
+	return rc;
 }
 
 static bool cam_icp_update_clk_overclk_free(struct cam_icp_hw_mgr *hw_mgr,
@@ -581,8 +587,9 @@
 	rc = cam_icp_update_bw(hw_mgr, ctx_data, hw_mgr_clk_info,
 		clk_info, busy);
 
-	CAM_DBG(CAM_ICP, "bw = %d update_bw = %d",
-		hw_mgr_clk_info->uncompressed_bw, rc);
+	CAM_DBG(CAM_ICP, "ubw = %lld, cbw = %lld, update_bw = %d",
+		hw_mgr_clk_info->uncompressed_bw,
+		hw_mgr_clk_info->compressed_bw, rc);
 
 	return rc;
 }
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
index d1793e6..c4a483f 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -33,7 +33,7 @@
 
 #define CAM_MAX_OUT_RES         6
 
-#define ICP_WORKQ_NUM_TASK      30
+#define ICP_WORKQ_NUM_TASK      100
 #define ICP_WORKQ_TASK_CMD_TYPE 1
 #define ICP_WORKQ_TASK_MSG_TYPE 2
 
@@ -124,6 +124,7 @@
  * @curr_fc: Context latest request frame cycles
  * @rt_flag: Flag to indicate real time request
  * @base_clk: Base clock to process the request
+ * @reserved: Reserved field
  * #uncompressed_bw: Current bandwidth voting
  * @compressed_bw: Current compressed bandwidth voting
  * @clk_rate: Supported clock rates for the context
@@ -132,8 +133,9 @@
 	uint32_t curr_fc;
 	uint32_t rt_flag;
 	uint32_t base_clk;
-	uint32_t uncompressed_bw;
-	uint32_t compressed_bw;
+	uint32_t reserved;
+	uint64_t uncompressed_bw;
+	uint64_t compressed_bw;
 	int32_t clk_rate[CAM_MAX_VOTE];
 };
 /**
@@ -196,8 +198,8 @@
 	uint32_t curr_clk;
 	uint32_t threshold;
 	uint32_t over_clked;
-	uint32_t uncompressed_bw;
-	uint32_t compressed_bw;
+	uint64_t uncompressed_bw;
+	uint64_t compressed_bw;
 };
 
 /**
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 4ecb36e..aad7902 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
@@ -64,6 +64,51 @@
 	return 0;
 }
 
+static const char *__cam_isp_resource_handle_id_to_type
+	(uint32_t resource_handle)
+{
+	switch (resource_handle) {
+	case CAM_ISP_IFE_OUT_RES_FULL:
+		return "CAM_ISP_IFE_OUT_RES_FULL";
+	case CAM_ISP_IFE_OUT_RES_DS4:
+		return "CAM_ISP_IFE_OUT_RES_DS4";
+	case CAM_ISP_IFE_OUT_RES_DS16:
+		return "CAM_ISP_IFE_OUT_RES_DS16";
+	case CAM_ISP_IFE_OUT_RES_RAW_DUMP:
+		return "CAM_ISP_IFE_OUT_RES_RAW_DUMP";
+	case CAM_ISP_IFE_OUT_RES_FD:
+		return "CAM_ISP_IFE_OUT_RES_FD";
+	case CAM_ISP_IFE_OUT_RES_PDAF:
+		return "CAM_ISP_IFE_OUT_RES_PDAF";
+	case CAM_ISP_IFE_OUT_RES_RDI_0:
+		return "CAM_ISP_IFE_OUT_RES_RDI_0";
+	case CAM_ISP_IFE_OUT_RES_RDI_1:
+		return "CAM_ISP_IFE_OUT_RES_RDI_1";
+	case CAM_ISP_IFE_OUT_RES_RDI_2:
+		return "CAM_ISP_IFE_OUT_RES_RDI_2";
+	case CAM_ISP_IFE_OUT_RES_RDI_3:
+		return "CAM_ISP_IFE_OUT_RES_RDI_3";
+	case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE:
+		return "CAM_ISP_IFE_OUT_RES_STATS_HDR_BE";
+	case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST:
+		return "CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST";
+	case CAM_ISP_IFE_OUT_RES_STATS_TL_BG:
+		return "CAM_ISP_IFE_OUT_RES_STATS_TL_BG";
+	case CAM_ISP_IFE_OUT_RES_STATS_BF:
+		return "CAM_ISP_IFE_OUT_RES_STATS_BF";
+	case CAM_ISP_IFE_OUT_RES_STATS_AWB_BG:
+		return "CAM_ISP_IFE_OUT_RES_STATS_AWB_BG";
+	case CAM_ISP_IFE_OUT_RES_STATS_BHIST:
+		return "CAM_ISP_IFE_OUT_RES_STATS_BHIST";
+	case CAM_ISP_IFE_OUT_RES_STATS_RS:
+		return "CAM_ISP_IFE_OUT_RES_STATS_RS";
+	case CAM_ISP_IFE_OUT_RES_STATS_CS:
+		return "CAM_ISP_IFE_OUT_RES_STATS_CS";
+	default:
+		return "CAM_ISP_Invalid_Resource_Type";
+	}
+}
+
 static uint64_t __cam_isp_ctx_get_event_ts(uint32_t evt_id, void *evt_data)
 {
 	uint64_t ts = 0;
@@ -101,6 +146,23 @@
 	return ts;
 }
 
+static void __cam_isp_ctx_handle_buf_done_fail_log(
+	struct cam_isp_ctx_req *req_isp)
+{
+	int i;
+
+	CAM_ERR_RATE_LIMIT(CAM_ISP,
+		"Resource Handles that fail to generate buf_done in prev frame");
+	for (i = 0; i < req_isp->num_fence_map_out; i++) {
+		if (req_isp->fence_map_out[i].sync_id != -1)
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"Resource_Handle: [%s] Sync_ID: [0x%x]",
+			__cam_isp_resource_handle_id_to_type(
+			req_isp->fence_map_out[i].resource_handle),
+			req_isp->fence_map_out[i].sync_id);
+	}
+}
+
 static int __cam_isp_ctx_handle_buf_done_in_activated_state(
 	struct cam_isp_context *ctx_isp,
 	struct cam_isp_hw_done_event_data *done,
@@ -143,8 +205,12 @@
 		if (!bubble_state) {
 			CAM_DBG(CAM_ISP, "Sync with success: fd 0x%x",
 				   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 (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);
@@ -731,14 +797,6 @@
 	 *
 	 */
 	ctx_isp = (struct cam_isp_context *) ctx->ctx_priv;
-	if (ctx_isp->active_req_cnt >=  2) {
-		CAM_ERR_RATE_LIMIT(CAM_ISP,
-			"Reject apply request due to congestion(cnt = %d)",
-			ctx_isp->active_req_cnt);
-		rc = -EFAULT;
-		goto end;
-	}
-
 	req = list_first_entry(&ctx->pending_req_list, struct cam_ctx_request,
 		list);
 
@@ -757,6 +815,14 @@
 	CAM_DBG(CAM_ISP, "Apply request %lld", req->request_id);
 	req_isp = (struct cam_isp_ctx_req *) req->req_priv;
 
+	if (ctx_isp->active_req_cnt >=  2) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"Reject apply request due to congestion(cnt = %d)",
+			ctx_isp->active_req_cnt);
+		__cam_isp_ctx_handle_buf_done_fail_log(req_isp);
+		rc = -EFAULT;
+		goto end;
+	}
 	req_isp->bubble_report = apply->report_if_bubble;
 
 	cfg.ctxt_to_hw_map = ctx_isp->hw_ctx;
@@ -1507,6 +1573,7 @@
 		add_req.link_hdl = ctx->link_hdl;
 		add_req.dev_hdl  = ctx->dev_hdl;
 		add_req.req_id   = req->request_id;
+		add_req.skip_before_applying = 0;
 		rc = ctx->ctx_crm_intf->add_req(&add_req);
 		if (rc) {
 			CAM_ERR(CAM_ISP, "Error: Adding request id=%llu",
@@ -1882,27 +1949,14 @@
 	struct cam_release_dev_cmd *cmd)
 {
 	int rc = 0;
-	struct cam_isp_context *ctx_isp =
-		(struct cam_isp_context *) ctx->ctx_priv;
 
-	__cam_isp_ctx_stop_dev_in_activated_unlock(ctx);
+	rc = __cam_isp_ctx_stop_dev_in_activated_unlock(ctx);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Stop device failed rc=%d", rc);
 
-	if (ctx_isp->hw_ctx) {
-		struct cam_hw_release_args   arg;
-
-		arg.ctxt_to_hw_map = ctx_isp->hw_ctx;
-		ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv,
-			&arg);
-		ctx_isp->hw_ctx = NULL;
-	}
-
-	ctx->session_hdl = 0;
-	ctx->dev_hdl = 0;
-	ctx->link_hdl = 0;
-	ctx->ctx_crm_intf = NULL;
-
-	ctx->state =  CAM_CTX_AVAILABLE;
-	trace_cam_context_state("ISP", ctx);
+	rc = __cam_isp_ctx_release_dev_in_top_state(ctx, cmd);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Release device failed rc=%d", rc);
 
 	return rc;
 }
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 d84be30..6060278 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
+#include <linux/debugfs.h>
 #include <uapi/media/cam_isp.h>
 #include "cam_smmu_api.h"
 #include "cam_req_mgr_workq.h"
@@ -495,6 +496,7 @@
 		vfe_acquire.vfe_out.out_port_info = out_port;
 		vfe_acquire.vfe_out.split_id = CAM_ISP_HW_SPLIT_LEFT;
 		vfe_acquire.vfe_out.unique_id = ife_ctx->ctx_index;
+		vfe_acquire.vfe_out.is_dual = 0;
 		hw_intf = ife_src_res->hw_res[0]->hw_intf;
 		rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
 			&vfe_acquire,
@@ -834,6 +836,7 @@
 
 	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;
 
 		for (j = i + 1; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
 			if (!ife_hw_mgr->csid_devices[j])
@@ -1038,13 +1041,6 @@
 	struct cam_hw_intf                  *hw_intf;
 	struct cam_csid_hw_reserve_resource_args  csid_acquire;
 
-	/* no dual vfe for TPG */
-	if ((in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) &&
-		(in_port->usage_type != 0)) {
-		CAM_ERR(CAM_ISP, "No Dual VFE on TPG input");
-		goto err;
-	}
-
 	ife_hw_mgr = ife_ctx->hw_mgr;
 
 	rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &cid_res);
@@ -1727,6 +1723,16 @@
 
 	cam_tasklet_start(ctx->common.tasklet_info);
 
+	/* set current csid debug information to CSID HW */
+	for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
+		if (g_ife_hw_mgr.csid_devices[i])
+			rc = g_ife_hw_mgr.csid_devices[i]->hw_ops.process_cmd(
+				g_ife_hw_mgr.csid_devices[i]->hw_priv,
+				CAM_IFE_CSID_SET_CSID_DEBUG,
+				&g_ife_hw_mgr.debug_cfg.csid_debug,
+				sizeof(g_ife_hw_mgr.debug_cfg.csid_debug));
+	}
+
 	/* INIT IFE Root: do nothing */
 
 	CAM_DBG(CAM_ISP, "INIT IFE CID ... in ctx id:%d",
@@ -1973,13 +1979,14 @@
 		/* get command buffers */
 		if (ctx->base[i].split_id != CAM_ISP_HW_SPLIT_MAX) {
 			rc = cam_isp_add_command_buffers(prepare,
-				ctx->base[i].split_id);
-			if (rc) {
-				CAM_ERR(CAM_ISP,
-					"Failed in add cmdbuf, i=%d, split_id=%d, rc=%d",
-					i, ctx->base[i].split_id, rc);
-				goto end;
-			}
+			ctx->base[i].split_id, ctx->base[i].idx,
+			ctx->res_list_ife_out, CAM_IFE_HW_OUT_RES_MAX);
+				if (rc) {
+					CAM_ERR(CAM_ISP,
+						"Failed in add cmdbuf, i=%d, split_id=%d, rc=%d",
+						i, ctx->base[i].split_id, rc);
+					goto end;
+				}
 		}
 
 		if (blob_info.hfr_config) {
@@ -2520,7 +2527,7 @@
 	case CAM_ISP_HW_EVENT_SOF:
 		event_cnt = ife_hw_mgr_ctx->sof_cnt;
 		break;
-	case CAM_ISP_HW_EVENT_REG_UPDATE:
+	case CAM_ISP_HW_EVENT_EPOCH:
 		event_cnt = ife_hw_mgr_ctx->epoch_cnt;
 		break;
 	case CAM_ISP_HW_EVENT_EOF:
@@ -2540,8 +2547,10 @@
 		return rc;
 	}
 
-	if ((event_cnt[core_idx0] - event_cnt[core_idx1] > 1) ||
-		(event_cnt[core_idx1] - event_cnt[core_idx0] > 1)) {
+	if ((event_cnt[core_idx0] &&
+		(event_cnt[core_idx0] - event_cnt[core_idx1] > 1)) ||
+		(event_cnt[core_idx1] &&
+		(event_cnt[core_idx1] - event_cnt[core_idx0] > 1))) {
 
 		CAM_WARN(CAM_ISP,
 			"One of the VFE cound not generate hw event %d",
@@ -2628,6 +2637,8 @@
 
 				if (!epoch_status)
 					ife_hwr_mgr_ctx->epoch_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			/* SOF check for Right side VFE */
@@ -2637,6 +2648,8 @@
 
 				if (!epoch_status)
 					ife_hwr_mgr_ctx->epoch_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			core_index0 = hw_res_l->hw_intf->hw_idx;
@@ -2739,6 +2752,8 @@
 				hw_res_l, evt_payload);
 			if (!sof_status)
 				ife_hwr_mgr_ctx->sof_cnt[core_idx]++;
+			else
+				break;
 		}
 
 		/* SOF check for Right side VFE */
@@ -2754,6 +2769,8 @@
 				evt_payload);
 			if (!sof_status)
 				ife_hwr_mgr_ctx->sof_cnt[core_idx]++;
+			else
+				break;
 		}
 
 		core_index0 = hw_res_l->hw_intf->hw_idx;
@@ -2932,6 +2949,8 @@
 
 				if (!eof_status)
 					ife_hwr_mgr_ctx->eof_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			/* EOF check for Right side VFE */
@@ -2941,6 +2960,8 @@
 
 				if (!eof_status)
 					ife_hwr_mgr_ctx->eof_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			core_index0 = hw_res_l->hw_intf->hw_idx;
@@ -2955,7 +2976,7 @@
 			if (!rc)
 				ife_hwr_irq_eof_cb(
 					ife_hwr_mgr_ctx->common.cb_priv,
-					CAM_ISP_HW_EVENT_EPOCH,
+					CAM_ISP_HW_EVENT_EOF,
 					&eof_done_event_data);
 
 			break;
@@ -3015,6 +3036,8 @@
 			hw_res_l->hw_intf->hw_idx))
 			buf_done_status = hw_res_l->bottom_half_handler(
 				hw_res_l, evt_payload);
+		else
+			continue;
 
 		switch (buf_done_status) {
 		case CAM_VFE_IRQ_STATUS_ERR_COMP:
@@ -3114,7 +3137,8 @@
 	evt_payload = evt_payload_priv;
 	ife_hwr_mgr_ctx = (struct cam_ife_hw_mgr_ctx *)evt_payload->ctx;
 
-	CAM_DBG(CAM_ISP, "addr of evt_payload = %llx", (uint64_t)evt_payload);
+	CAM_DBG(CAM_ISP, "addr of evt_payload = %llx core index:0x%x",
+		(uint64_t)evt_payload, evt_payload->core_index);
 	CAM_DBG(CAM_ISP, "bus_irq_status_0: = %x", evt_payload->irq_reg_val[0]);
 	CAM_DBG(CAM_ISP, "bus_irq_status_1: = %x", evt_payload->irq_reg_val[1]);
 	CAM_DBG(CAM_ISP, "bus_irq_status_2: = %x", evt_payload->irq_reg_val[2]);
@@ -3144,7 +3168,9 @@
 	evt_payload = evt_payload_priv;
 	ife_hwr_mgr_ctx = (struct cam_ife_hw_mgr_ctx *)handler_priv;
 
-	CAM_DBG(CAM_ISP, "addr of evt_payload = %llx", (uint64_t)evt_payload);
+	CAM_DBG(CAM_ISP, "addr of evt_payload = %pK core_index:%d",
+		(void *)evt_payload,
+		evt_payload->core_index);
 	CAM_DBG(CAM_ISP, "irq_status_0: = %x", evt_payload->irq_reg_val[0]);
 	CAM_DBG(CAM_ISP, "irq_status_1: = %x", evt_payload->irq_reg_val[1]);
 	CAM_DBG(CAM_ISP, "Violation register: = %x",
@@ -3215,6 +3241,51 @@
 	return 0;
 }
 
+static int cam_ife_set_csid_debug(void *data, u64 val)
+{
+	g_ife_hw_mgr.debug_cfg.csid_debug = val;
+	CAM_DBG(CAM_ISP, "Set CSID Debug value :%lld", val);
+	return 0;
+}
+
+static int cam_ife_get_csid_debug(void *data, u64 *val)
+{
+	*val = g_ife_hw_mgr.debug_cfg.csid_debug;
+	CAM_DBG(CAM_ISP, "Get CSID Debug value :%lld",
+		g_ife_hw_mgr.debug_cfg.csid_debug);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_ife_csid_debug,
+	cam_ife_get_csid_debug,
+	cam_ife_set_csid_debug, "%16llu");
+
+static int cam_ife_hw_mgr_debug_register(void)
+{
+	g_ife_hw_mgr.debug_cfg.dentry = debugfs_create_dir("camera_ife",
+		NULL);
+
+	if (!g_ife_hw_mgr.debug_cfg.dentry) {
+		CAM_ERR(CAM_ISP, "failed to create dentry");
+		return -ENOMEM;
+	}
+
+	if (!debugfs_create_file("ife_csid_debug",
+		0644,
+		g_ife_hw_mgr.debug_cfg.dentry, NULL,
+		&cam_ife_csid_debug)) {
+		CAM_ERR(CAM_ISP, "failed to create cam_ife_csid_debug");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	debugfs_remove_recursive(g_ife_hw_mgr.debug_cfg.dentry);
+	return -ENOMEM;
+}
+
 int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf)
 {
 	int rc = -EFAULT;
@@ -3380,6 +3451,8 @@
 	hw_mgr_intf->hw_prepare_update = cam_ife_mgr_prepare_hw_update;
 	hw_mgr_intf->hw_config = cam_ife_mgr_config_hw;
 	hw_mgr_intf->hw_cmd = cam_ife_mgr_cmd;
+
+	cam_ife_hw_mgr_debug_register();
 	CAM_DBG(CAM_ISP, "Exit");
 
 	return 0;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
index 750b43e..2e66210 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
@@ -81,6 +81,18 @@
 };
 
 /**
+ * struct cam_ife_hw_mgr_debug - contain the debug information
+ *
+ * @dentry:              Debugfs entry
+ * @csid_debug:          csid debug information
+ *
+ */
+struct cam_ife_hw_mgr_debug {
+	struct dentry                  *dentry;
+	uint64_t                        csid_debug;
+};
+
+/**
  * struct cam_vfe_hw_mgr_ctx - IFE HW manager Context object
  *
  * @list:                   used by the ctx list.
@@ -142,7 +154,6 @@
 	uint32_t                        eof_cnt[CAM_IFE_HW_NUM_MAX];
 	atomic_t                        overflow_pending;
 	uint32_t                        is_rdi_only_context;
-
 };
 
 /**
@@ -176,6 +187,7 @@
 						CAM_IFE_CSID_HW_NUM_MAX];
 	struct cam_vfe_hw_get_hw_cap   ife_dev_caps[CAM_IFE_HW_NUM_MAX];
 	struct cam_req_mgr_core_workq  *workq;
+	struct cam_ife_hw_mgr_debug     debug_cfg;
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
index c58578e..5f68f21 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
@@ -82,10 +82,66 @@
 	return rc;
 }
 
+static int cam_isp_update_dual_config(
+	struct cam_hw_prepare_update_args  *prepare,
+	struct cam_cmd_buf_desc            *cmd_desc,
+	uint32_t                            split_id,
+	uint32_t                            base_idx,
+	struct cam_ife_hw_mgr_res          *res_list_isp_out,
+	uint32_t                            size_isp_out)
+{
+	int rc = -EINVAL;
+	struct cam_isp_dual_config                 *dual_config;
+	struct cam_ife_hw_mgr_res                  *hw_mgr_res;
+	struct cam_isp_resource_node               *res;
+	struct cam_isp_hw_dual_isp_update_args      dual_isp_update_args;
+	size_t                                      len = 0;
+	uint32_t                                   *cpu_addr;
+	uint32_t                                    i, j;
+
+	CAM_DBG(CAM_UTIL, "cmd des size %d, length: %d",
+		cmd_desc->size, cmd_desc->length);
+
+	rc = cam_packet_util_get_cmd_mem_addr(
+		cmd_desc->mem_handle, &cpu_addr, &len);
+	if (rc)
+		return rc;
+
+	cpu_addr += (cmd_desc->offset / 4);
+	dual_config = (struct cam_isp_dual_config *)cpu_addr;
+
+	for (i = 0; i < dual_config->num_ports; i++) {
+		hw_mgr_res = &res_list_isp_out[i];
+		for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) {
+			if (!hw_mgr_res->hw_res[j])
+				continue;
+
+			if (hw_mgr_res->hw_res[j]->hw_intf->hw_idx != base_idx)
+				continue;
+
+			res = hw_mgr_res->hw_res[j];
+			dual_isp_update_args.split_id = j;
+			dual_isp_update_args.res      = res;
+			dual_isp_update_args.dual_cfg = dual_config;
+			rc = res->hw_intf->hw_ops.process_cmd(
+				res->hw_intf->hw_priv,
+				CAM_VFE_HW_CMD_STRIPE_UPDATE,
+				&dual_isp_update_args,
+				sizeof(struct cam_isp_hw_dual_isp_update_args));
+			if (rc)
+				return rc;
+		}
+	}
+
+	return rc;
+}
 
 int cam_isp_add_command_buffers(
 	struct cam_hw_prepare_update_args  *prepare,
-	uint32_t                            split_id)
+	enum cam_isp_hw_split_id            split_id,
+	uint32_t                            base_idx,
+	struct cam_ife_hw_mgr_res          *res_list_isp_out,
+	uint32_t                            size_isp_out)
 {
 	int rc = 0;
 	uint32_t  cmd_meta_data, num_ent, i;
@@ -168,6 +224,14 @@
 
 			num_ent++;
 			break;
+		case CAM_ISP_PACKET_META_DUAL_CONFIG:
+			rc = cam_isp_update_dual_config(prepare,
+				&cmd_desc[i], split_id, base_idx,
+				res_list_isp_out, size_isp_out);
+
+			if (rc)
+				return rc;
+			break;
 		case CAM_ISP_PACKET_META_GENERIC_BLOB:
 			break;
 		default:
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h
index 24b532e..cce0071 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h
@@ -54,14 +54,19 @@
  *                         left or right VFE/IFE instance.
  *
  * @prepare:               Contain the  packet and HW update variables
- * @dual_type:             Left of right command buffers to be extracted
- *
+ * @split_id:              Left or right command buffers to be extracted
+ * @base_idx:              Base or dev index of the IFE/VFE HW instance
+ * @res_list_isp_out:      IFE /VFE out resource list
+ * @size_isp_out:          Size of the res_list_isp_out array
  * @return:                0 for success
  *                         -EINVAL for Fail
  */
 int cam_isp_add_command_buffers(
-	struct cam_hw_prepare_update_args    *prepare,
-	enum cam_isp_hw_split_id              split_id);
+	struct cam_hw_prepare_update_args  *prepare,
+	enum cam_isp_hw_split_id            split_id,
+	uint32_t                            base_idx,
+	struct cam_ife_hw_mgr_res          *res_list_isp_out,
+	uint32_t                            size_isp_out);
 
 /**
  * @brief                  Add io buffer configurations in the HW entries list
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h
index 8ff2a55..c68ddf7 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h
@@ -199,7 +199,7 @@
 	.csid_csi2_rx_captured_long_pkt_0_addr        = 0x130,
 	.csid_csi2_rx_captured_long_pkt_1_addr        = 0x134,
 	.csid_csi2_rx_captured_long_pkt_ftr_addr      = 0x138,
-	.csid_csi2_rx_captured_cphy_pkt_ftr_addr      = 0x13c,
+	.csid_csi2_rx_captured_cphy_pkt_hdr_addr      = 0x13c,
 	.csid_csi2_rx_lane0_misr_addr                 = 0x150,
 	.csid_csi2_rx_lane1_misr_addr                 = 0x154,
 	.csid_csi2_rx_lane2_misr_addr                 = 0x158,
@@ -213,6 +213,14 @@
 	.csi2_irq_mask_all                            = 0xFFFFFFF,
 	.csi2_misr_enable_shift_val                   = 6,
 	.csi2_vc_mode_shift_val                       = 2,
+	.csi2_capture_long_pkt_en_shift               = 0,
+	.csi2_capture_short_pkt_en_shift              = 1,
+	.csi2_capture_cphy_pkt_en_shift               = 2,
+	.csi2_capture_long_pkt_dt_shift               = 4,
+	.csi2_capture_long_pkt_vc_shift               = 10,
+	.csi2_capture_short_pkt_vc_shift              = 15,
+	.csi2_capture_cphy_pkt_dt_shift               = 20,
+	.csi2_capture_cphy_pkt_vc_shift               = 26,
 };
 
 static struct cam_ife_csid_csi2_tpg_reg_offset
@@ -236,9 +244,10 @@
 	.csid_tpg_cgen_n_y1_addr                      = 0x664,
 	.csid_tpg_cgen_n_y2_addr                      = 0x668,
 
-	/*configurations */
+	/* configurations */
 	.tpg_dtn_cfg_offset                           = 0xc,
 	.tpg_cgen_cfg_offset                          = 0x20,
+	.tpg_cpas_ife_reg_offset                      = 0x28,
 };
 
 static struct cam_ife_csid_common_reg_offset
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index 9103136..7d6e758 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -32,8 +32,6 @@
 #define CAM_IFE_CSID_TIMEOUT_SLEEP_US                  1000
 #define CAM_IFE_CSID_TIMEOUT_ALL_US                    1000000
 
-#define MEASURE_EN                                     0
-
 static int cam_ife_csid_is_ipp_format_supported(
 	uint32_t in_format)
 {
@@ -85,6 +83,7 @@
 	case CAM_FORMAT_MIPI_RAW_8:
 		switch (out_format) {
 		case CAM_FORMAT_MIPI_RAW_8:
+		case CAM_FORMAT_PLAIN128:
 			*decode_fmt = 0xf;
 			break;
 		case CAM_FORMAT_PLAIN8:
@@ -99,6 +98,7 @@
 	case CAM_FORMAT_MIPI_RAW_10:
 		switch (out_format) {
 		case CAM_FORMAT_MIPI_RAW_10:
+		case CAM_FORMAT_PLAIN128:
 			*decode_fmt = 0xf;
 			break;
 		case CAM_FORMAT_PLAIN16_10:
@@ -196,7 +196,7 @@
 	}
 
 	if (rc)
-		CAM_ERR(CAM_ISP, "Unsupported format pair in %d out %d\n",
+		CAM_ERR(CAM_ISP, "Unsupported format pair in %d out %d",
 			in_format, out_format);
 
 	return rc;
@@ -562,7 +562,7 @@
 	struct cam_ife_csid_cid_data       *cid_data;
 
 	CAM_DBG(CAM_ISP,
-		"CSID:%d res_sel:%d Lane type:%d lane_num:%d dt:%d vc:%d",
+		"CSID:%d res_sel:0x%x Lane type:%d lane_num:%d dt:%d vc:%d",
 		csid_hw->hw_intf->hw_idx,
 		cid_reserv->in_port->res_type,
 		cid_reserv->in_port->lane_type,
@@ -683,11 +683,24 @@
 			}
 			csid_hw->tpg_cfg.in_format =
 				cid_reserv->in_port->format;
-			csid_hw->tpg_cfg.width =
-				cid_reserv->in_port->left_width;
+			csid_hw->tpg_cfg.usage_type =
+				cid_reserv->in_port->usage_type;
+			if (cid_reserv->in_port->usage_type)
+				csid_hw->tpg_cfg.width =
+					(cid_reserv->in_port->right_stop + 1);
+			else
+				csid_hw->tpg_cfg.width =
+					cid_reserv->in_port->left_width;
+
 			csid_hw->tpg_cfg.height = cid_reserv->in_port->height;
 			csid_hw->tpg_cfg.test_pattern =
 				cid_reserv->in_port->test_pattern;
+
+			CAM_DBG(CAM_ISP, "CSID:%d TPG width:%d height:%d",
+				csid_hw->hw_intf->hw_idx,
+				csid_hw->tpg_cfg.width,
+				csid_hw->tpg_cfg.height);
+
 			cid_data->tpg_set = 1;
 		} else {
 			csid_hw->csi2_rx_cfg.phy_sel =
@@ -815,6 +828,7 @@
 	path_data->sync_mode = reserve->sync_mode;
 	path_data->height  = reserve->in_port->height;
 	path_data->start_line = reserve->in_port->line_start;
+	path_data->end_line = reserve->in_port->line_stop;
 	if (reserve->in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) {
 		path_data->dt = CAM_IFE_CSID_TPG_DT_VAL;
 		path_data->vc = CAM_IFE_CSID_TPG_VC_VAL;
@@ -826,18 +840,32 @@
 	if (reserve->sync_mode == CAM_ISP_HW_SYNC_MASTER) {
 		path_data->crop_enable = 1;
 		path_data->start_pixel = reserve->in_port->left_start;
+		path_data->end_pixel = reserve->in_port->left_stop;
 		path_data->width  = reserve->in_port->left_width;
+		CAM_DBG(CAM_ISP, "CSID:%dmaster:startpixel 0x%x endpixel:0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_pixel,
+			path_data->end_pixel);
+		CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_line,
+			path_data->end_line);
 	} else if (reserve->sync_mode == CAM_ISP_HW_SYNC_SLAVE) {
 		path_data->crop_enable = 1;
 		path_data->start_pixel = reserve->in_port->right_start;
+		path_data->end_pixel = reserve->in_port->right_stop;
 		path_data->width  = reserve->in_port->right_width;
+		CAM_DBG(CAM_ISP, "CSID:%d slave:start:0x%x end:0x%x width 0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_pixel,
+			path_data->end_pixel, path_data->width);
+		CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_line,
+			path_data->end_line);
 	} else {
 		path_data->crop_enable = 0;
 		path_data->width  = reserve->in_port->left_width;
 		path_data->start_pixel = reserve->in_port->left_start;
 	}
 
-	CAM_DBG(CAM_ISP, "Res %d width %d height %d\n", reserve->res_id,
+	CAM_DBG(CAM_ISP, "Res %d width %d height %d", reserve->res_id,
 		path_data->width, path_data->height);
 	reserve->node_res = res;
 
@@ -994,6 +1022,7 @@
 static int cam_ife_csid_tpg_start(struct cam_ife_csid_hw   *csid_hw,
 	struct cam_isp_resource_node       *res)
 {
+	int rc = 0;
 	uint32_t  val = 0;
 	struct cam_hw_soc_info    *soc_info;
 
@@ -1039,6 +1068,15 @@
 			}
 		}
 
+		/* Enable the IFE force clock on for dual isp case */
+		if (csid_hw->tpg_cfg.usage_type) {
+			rc = cam_ife_csid_enable_ife_force_clock_on(soc_info,
+				csid_hw->csid_info->csid_reg->tpg_reg->
+				tpg_cpas_ife_reg_offset);
+			if (rc)
+				return rc;
+		}
+
 		CAM_DBG(CAM_ISP, "============ TPG control ============");
 		val = (4 << 20);
 		val |= (0x80 << 8);
@@ -1058,6 +1096,7 @@
 static int cam_ife_csid_tpg_stop(struct cam_ife_csid_hw   *csid_hw,
 	struct cam_isp_resource_node       *res)
 {
+	int rc = 0;
 	struct cam_hw_soc_info    *soc_info;
 
 	if (csid_hw->tpg_start_cnt)
@@ -1073,6 +1112,12 @@
 		CAM_DBG(CAM_ISP, "CSID:%d stop CSID TPG",
 			csid_hw->hw_intf->hw_idx);
 
+		/* Disable the IFE force clock on for dual isp case */
+		if (csid_hw->tpg_cfg.usage_type)
+			rc = cam_ife_csid_disable_ife_force_clock_on(soc_info,
+				csid_hw->csid_info->csid_reg->tpg_reg->
+				tpg_cpas_ife_reg_offset);
+
 		/*stop the TPG */
 		cam_io_w_mb(0,  soc_info->reg_map[0].mem_base +
 		csid_hw->csid_info->csid_reg->tpg_reg->csid_tpg_ctrl_addr);
@@ -1100,8 +1145,8 @@
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->tpg_reg->csid_tpg_vc_cfg0_addr);
 
-	/* vertical blanking count = 0x740, horzontal blanking count = 0x740*/
-	val = (0x740 << 12) | 0x740;
+	/* vertical blanking count = 0x3FF, horzontal blanking count = 0x740*/
+	val = (0x3FF << 12) | 0x740;
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->tpg_reg->csid_tpg_vc_cfg1_addr);
 
@@ -1203,6 +1248,28 @@
 		CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION |
 		CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION |
 		CSID_CSI2_RX_ERROR_CPHY_PH_CRC;
+
+	/* Enable the interrupt based on csid debug info set */
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ)
+		val |= CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ)
+		val |= CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE)
+		val |= CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE)
+		val |= CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE)
+		val |= CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED;
+
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr);
 
@@ -1277,9 +1344,9 @@
 		(path_data->dt << csid_reg->cmn_reg->dt_shift_val) |
 		(path_data->cid << csid_reg->cmn_reg->dt_id_shift_val) |
 		(decode_format << csid_reg->cmn_reg->fmt_shift_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable <<
 		csid_reg->cmn_reg->crop_h_en_shift_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable <<
 		csid_reg->cmn_reg->crop_v_en_shift_val) |
 		(1 << 1) | 1;
 	val |= (1 << csid_reg->ipp_reg->pix_store_en_shift_val);
@@ -1291,21 +1358,21 @@
 		csid_reg->ipp_reg->csid_ipp_cfg1_addr);
 
 	if (path_data->crop_enable) {
-		val = ((path_data->width +
-			path_data->start_pixel) & 0xFFFF <<
+		val = (((path_data->end_pixel & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_pixel & 0xFFFF);
-
+			(path_data->start_pixel & 0xFFFF));
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->ipp_reg->csid_ipp_hcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Horizontal crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 
-		val = ((path_data->height +
-			path_data->start_line) & 0xFFFF <<
+		val = (((path_data->end_line & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_line & 0xFFFF);
-
+			(path_data->start_line & 0xFFFF));
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->ipp_reg->csid_ipp_vcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 	}
 
 	/* set frame drop pattern to 0 and period to 1 */
@@ -1350,6 +1417,34 @@
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->ipp_reg->csid_ipp_cfg0_addr);
 
+	/* configure the rx packet capture based on csid debug set */
+	val = 0;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE)
+		val = ((1 <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_en_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_vc_shift));
+
+	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
+		csid_reg->csi2_reg->csid_csi2_rx_capture_ctrl_addr);
+	CAM_DBG(CAM_ISP, "rx capture control value 0x%x", val);
+
 	res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW;
 
 	return rc;
@@ -1439,6 +1534,12 @@
 
 	/* Enable the required ipp interrupts */
 	val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_SOF;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_EOF;
+
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->ipp_reg->csid_ipp_irq_mask_addr);
 
@@ -1567,9 +1668,9 @@
 		(path_data->cid << csid_reg->cmn_reg->dt_id_shift_val) |
 		(path_format << csid_reg->cmn_reg->fmt_shift_val) |
 		(plain_fmt << csid_reg->cmn_reg->plain_fmt_shit_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable  <<
 			csid_reg->cmn_reg->crop_h_en_shift_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable  <<
 		csid_reg->cmn_reg->crop_v_en_shift_val) |
 		(1 << 2) | 3;
 
@@ -1581,21 +1682,23 @@
 			csid_reg->rdi_reg[id]->csid_rdi_cfg1_addr);
 
 	if (path_data->crop_enable) {
-		val = ((path_data->width +
-			path_data->start_pixel) & 0xFFFF <<
+		val = (((path_data->end_pixel & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_pixel & 0xFFFF);
+			(path_data->start_pixel & 0xFFFF));
 
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->rdi_reg[id]->csid_rdi_rpp_hcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Horizontal crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 
-		val = ((path_data->height +
-			path_data->start_line) & 0xFFFF <<
+		val = (((path_data->end_line & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_line & 0xFFFF);
+			(path_data->start_line & 0xFFFF));
 
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->rdi_reg[id]->csid_rdi_rpp_vcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 	}
 	/* set frame drop pattern to 0 and period to 1 */
 	cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
@@ -1627,21 +1730,35 @@
 	val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
 		csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr);
 	val |= (1 << csid_reg->cmn_reg->path_en_shift_val);
-#if MEASURE_EN
-	val |= 0x2;
-	cam_io_w_mb(0x3, soc_info->reg_map[0].mem_base +
-		csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg0_addr);
-	cam_io_w_mb(path_data->height << 16 | path_data->width,
-		soc_info->reg_map[0].mem_base +
-		csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg1_addr);
-	CAM_DBG(CAM_ISP, "measure_cfg1 0x%x offset 0x%x\n",
-		path_data->height << 16 | path_data->width,
-		csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg0_addr);
 
-#endif
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr);
 
+	/* configure the rx packet capture based on csid debug set */
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE)
+		val = ((1 <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_en_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_vc_shift));
+	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
+		csid_reg->csi2_reg->csid_csi2_rx_capture_ctrl_addr);
+
 	res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW;
 
 	return rc;
@@ -1708,11 +1825,13 @@
 			csid_reg->rdi_reg[id]->csid_rdi_ctrl_addr);
 
 	/* Enable the required RDI interrupts */
-	val = CSID_PATH_INFO_RST_DONE |
-#if MEASURE_EN
-		CSID_PATH_INFO_INPUT_SOF |
-#endif
-		CSID_PATH_ERROR_FIFO_OVERFLOW;
+	val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_SOF;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_EOF;
+
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr);
 
@@ -1844,6 +1963,20 @@
 
 	return 0;
 }
+
+static int cam_ife_csid_set_csid_debug(struct cam_ife_csid_hw   *csid_hw,
+	void *cmd_args)
+{
+	uint32_t  *csid_debug;
+
+	csid_debug = (uint32_t  *) cmd_args;
+	csid_hw->csid_debug = *csid_debug;
+	CAM_DBG(CAM_ISP, "CSID:%d set csid debug value:%d",
+		csid_hw->hw_intf->hw_idx, csid_hw->csid_debug);
+
+	return 0;
+}
+
 static int cam_ife_csid_res_wait_for_halt(
 	struct cam_ife_csid_hw          *csid_hw,
 	struct cam_isp_resource_node    *res)
@@ -2387,6 +2520,9 @@
 	case CAM_IFE_CSID_CMD_GET_TIME_STAMP:
 		rc = cam_ife_csid_get_time_stamp(csid_hw, cmd_args);
 		break;
+	case CAM_IFE_CSID_SET_CSID_DEBUG:
+		rc = cam_ife_csid_set_csid_debug(csid_hw, cmd_args);
+		break;
 	default:
 		CAM_ERR(CAM_ISP, "CSID:%d un supported cmd:%d",
 			csid_hw->hw_intf->hw_idx, cmd_type);
@@ -2405,9 +2541,7 @@
 	struct cam_ife_csid_reg_offset  *csid_reg;
 	uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0,
 		irq_status_rdi[4];
-#if MEASURE_EN
 	uint32_t val;
-#endif
 
 	csid_hw = (struct cam_ife_csid_hw *)data;
 
@@ -2501,6 +2635,93 @@
 			 csid_hw->hw_intf->hw_idx);
 	}
 
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ) {
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+	}
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ) {
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+	}
+
+	if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE) &&
+		(irq_status_rx & CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED)) {
+		CAM_ERR(CAM_ISP, "CSID:%d LONG_PKT_CAPTURED",
+			csid_hw->hw_intf->hw_idx);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_long_pkt_0_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d long packet VC :%d DT:%d WC:%d",
+			csid_hw->hw_intf->hw_idx,
+			(val >> 22), ((val >> 16) & 0x3F), (val & 0xFFFF));
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_long_pkt_1_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d long packet ECC :%d",
+			csid_hw->hw_intf->hw_idx, val);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_long_pkt_ftr_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d long pkt cal CRC:%d expected CRC:%d",
+			csid_hw->hw_intf->hw_idx, (val >> 16), (val & 0xFFFF));
+	}
+	if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE) &&
+		(irq_status_rx & CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED)) {
+		CAM_ERR(CAM_ISP, "CSID:%d SHORT_PKT_CAPTURED",
+			csid_hw->hw_intf->hw_idx);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_short_pkt_0_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d short pkt VC :%d DT:%d LC:%d",
+			csid_hw->hw_intf->hw_idx,
+			(val >> 22), ((val >> 16) & 0x1F), (val & 0xFFFF));
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_short_pkt_1_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d short packet ECC :%d", val);
+	}
+
+	if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE) &&
+		(irq_status_rx & CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED)) {
+		CAM_ERR(CAM_ISP, "CSID:%d CPHY_PKT_HDR_CAPTURED",
+			csid_hw->hw_intf->hw_idx);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_cphy_pkt_hdr_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d cphy packet VC :%d DT:%d WC:%d",
+			csid_hw->hw_intf->hw_idx,
+			(val >> 22), ((val >> 16) & 0x1F), (val & 0xFFFF));
+	}
+
 	/*read the IPP errors */
 	if (csid_reg->cmn_reg->no_pix) {
 		/* IPP reset done bit */
@@ -2509,14 +2730,16 @@
 			CAM_DBG(CAM_ISP, "CSID IPP reset complete");
 			complete(&csid_hw->csid_ipp_complete);
 		}
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_SOF)
-			CAM_DBG(CAM_ISP, "CSID IPP SOF received");
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_SOL)
-			CAM_DBG(CAM_ISP, "CSID IPP SOL received");
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_EOL)
-			CAM_DBG(CAM_ISP, "CSID IPP EOL received");
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_EOF)
-			CAM_DBG(CAM_ISP, "CSID IPP EOF received");
+
+		if ((irq_status_ipp & CSID_PATH_INFO_INPUT_SOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID:%d IPP SOF received",
+				csid_hw->hw_intf->hw_idx);
+
+		if ((irq_status_ipp & CSID_PATH_INFO_INPUT_EOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID:%d IPP EOF received",
+				csid_hw->hw_intf->hw_idx);
 
 		if (irq_status_ipp & CSID_PATH_INFO_INPUT_EOF)
 			complete(&csid_hw->csid_ipp_complete);
@@ -2534,21 +2757,17 @@
 	for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) {
 		if (irq_status_rdi[i] &
 			BIT(csid_reg->cmn_reg->path_rst_done_shift_val)) {
-			CAM_DBG(CAM_ISP, "CSID rdi%d reset complete", i);
+			CAM_DBG(CAM_ISP, "CSID RDI%d reset complete", i);
 			complete(&csid_hw->csid_rdin_complete[i]);
 		}
 
-		if (irq_status_rdi[i]  & CSID_PATH_INFO_INPUT_SOF) {
-			CAM_DBG(CAM_ISP, "CSID RDI SOF received");
-#if MEASURE_EN
-			val = cam_io_r(soc_info->reg_map[0].mem_base +
-				csid_reg->rdi_reg[i]->
-				csid_rdi_format_measure0_addr);
-			CAM_ERR(CAM_ISP, "measure 0x%x\n", val);
-#endif
-		}
-		if (irq_status_rdi[i]  & CSID_PATH_INFO_INPUT_EOF)
-			CAM_DBG(CAM_ISP, "CSID RDI EOF received");
+		if ((irq_status_rdi[i] & CSID_PATH_INFO_INPUT_SOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID RDI:%d SOF received", i);
+
+		if ((irq_status_rdi[i]  & CSID_PATH_INFO_INPUT_EOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID RDI:%d EOF received", i);
 
 		if (irq_status_rdi[i] & CSID_PATH_INFO_INPUT_EOF)
 			complete(&csid_hw->csid_rdin_complete[i]);
@@ -2676,6 +2895,7 @@
 		ife_csid_hw->rdi_res[i].res_priv = path_data;
 	}
 
+	ife_csid_hw->csid_debug = 0;
 	return 0;
 err:
 	if (rc) {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
index d5f032f..deef41f 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
@@ -22,7 +22,7 @@
 #define CAM_IFE_CSID_RDI_MAX         4
 
 #define CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED    BIT(0)
-#define CSID_CSI2_RX_NFO_PHY_DL1_EOT_CAPTURED     BIT(1)
+#define CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED    BIT(1)
 #define CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED    BIT(2)
 #define CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED    BIT(3)
 #define CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED    BIT(4)
@@ -65,6 +65,18 @@
 #define CSID_PATH_ERROR_PIX_COUNT                 BIT(13)
 #define CSID_PATH_ERROR_LINE_COUNT                BIT(14)
 
+/*
+ * Debug values enable the corresponding interrupts and debug logs provide
+ * necessary information
+ */
+#define CSID_DEBUG_ENABLE_SOF_IRQ                 BIT(0)
+#define CSID_DEBUG_ENABLE_EOF_IRQ                 BIT(1)
+#define CSID_DEBUG_ENABLE_SOT_IRQ                 BIT(2)
+#define CSID_DEBUG_ENABLE_EOT_IRQ                 BIT(3)
+#define CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE       BIT(4)
+#define CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE        BIT(5)
+#define CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE        BIT(6)
+
 /* enum cam_csid_path_halt_mode select the path halt mode control */
 enum cam_csid_path_halt_mode {
 	CSID_HALT_MODE_INTERNAL,
@@ -187,7 +199,7 @@
 	uint32_t csid_csi2_rx_captured_long_pkt_0_addr;
 	uint32_t csid_csi2_rx_captured_long_pkt_1_addr;
 	uint32_t csid_csi2_rx_captured_long_pkt_ftr_addr;
-	uint32_t csid_csi2_rx_captured_cphy_pkt_ftr_addr;
+	uint32_t csid_csi2_rx_captured_cphy_pkt_hdr_addr;
 	uint32_t csid_csi2_rx_lane0_misr_addr;
 	uint32_t csid_csi2_rx_lane1_misr_addr;
 	uint32_t csid_csi2_rx_lane2_misr_addr;
@@ -202,6 +214,14 @@
 	uint32_t csi2_irq_mask_all;
 	uint32_t csi2_misr_enable_shift_val;
 	uint32_t csi2_vc_mode_shift_val;
+	uint32_t csi2_capture_long_pkt_en_shift;
+	uint32_t csi2_capture_short_pkt_en_shift;
+	uint32_t csi2_capture_cphy_pkt_en_shift;
+	uint32_t csi2_capture_long_pkt_dt_shift;
+	uint32_t csi2_capture_long_pkt_vc_shift;
+	uint32_t csi2_capture_short_pkt_vc_shift;
+	uint32_t csi2_capture_cphy_pkt_dt_shift;
+	uint32_t csi2_capture_cphy_pkt_vc_shift;
 };
 
 struct cam_ife_csid_csi2_tpg_reg_offset {
@@ -226,6 +246,7 @@
 	/*configurations */
 	uint32_t tpg_dtn_cfg_offset;
 	uint32_t tpg_cgen_cfg_offset;
+	uint32_t tpg_cpas_ife_reg_offset;
 
 };
 struct cam_ife_csid_common_reg_offset {
@@ -321,6 +342,7 @@
  * @height:           height
  * @test_pattern :    pattern
  * @in_format:        decode format
+ * @usage_type:       whether dual isp is required
  *
  */
 struct cam_ife_csid_tpg_cfg  {
@@ -328,6 +350,7 @@
 	uint32_t                        height;
 	uint32_t                        test_pattern;
 	uint32_t                        in_format;
+	uint32_t                        usage_type;
 };
 
 /**
@@ -358,8 +381,10 @@
  * @crop_enable:    crop is enable or disabled, if enabled
  *                  then remaining parameters are valid.
  * @start_pixel:    start pixel
+ * @end_pixel:      end_pixel
  * @width:          width
  * @start_line:     start line
+ * @end_line:       end_line
  * @height:         heigth
  * @sync_mode:       Applicable for IPP/RDI path reservation
  *                  Reserving the path for master IPP or slave IPP
@@ -377,8 +402,10 @@
 	uint32_t                        out_format;
 	bool                            crop_enable;
 	uint32_t                        start_pixel;
+	uint32_t                        end_pixel;
 	uint32_t                        width;
 	uint32_t                        start_line;
+	uint32_t                        end_line;
 	uint32_t                        height;
 	enum cam_isp_hw_sync_mode       sync_mode;
 	uint32_t                        master_idx;
@@ -403,6 +430,8 @@
  * @csid_csi2_reset_complete: csi2 reset completion
  * @csid_ipp_reset_complete:  ipp reset completion
  * @csid_rdin_reset_complete: rdi n completion
+ * @csid_debug:               csid debug information to enable the SOT, EOT,
+ *                            SOF, EOF, measure etc in the csid hw
  *
  */
 struct cam_ife_csid_hw {
@@ -422,6 +451,7 @@
 	struct completion                csid_csi2_complete;
 	struct completion                csid_ipp_complete;
 	struct completion    csid_rdin_complete[CAM_IFE_CSID_RDI_MAX];
+	uint64_t                         csid_debug;
 };
 
 int cam_ife_csid_hw_probe_init(struct cam_hw_intf  *csid_hw_intf,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h
index e857f8b..952426d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h
@@ -210,7 +210,7 @@
 	.csid_csi2_rx_captured_long_pkt_0_addr        = 0x130,
 	.csid_csi2_rx_captured_long_pkt_1_addr        = 0x134,
 	.csid_csi2_rx_captured_long_pkt_ftr_addr      = 0x138,
-	.csid_csi2_rx_captured_cphy_pkt_ftr_addr      = 0x13c,
+	.csid_csi2_rx_captured_cphy_pkt_hdr_addr      = 0x13c,
 	.csid_csi2_rx_lane0_misr_addr                 = 0x150,
 	.csid_csi2_rx_lane1_misr_addr                 = 0x154,
 	.csid_csi2_rx_lane2_misr_addr                 = 0x158,
@@ -224,6 +224,14 @@
 	.csi2_irq_mask_all                            = 0xFFFFFFF,
 	.csi2_misr_enable_shift_val                   = 6,
 	.csi2_vc_mode_shift_val                       = 2,
+	.csi2_capture_long_pkt_en_shift               = 0,
+	.csi2_capture_short_pkt_en_shift              = 1,
+	.csi2_capture_cphy_pkt_en_shift               = 2,
+	.csi2_capture_long_pkt_dt_shift               = 4,
+	.csi2_capture_long_pkt_vc_shift               = 10,
+	.csi2_capture_short_pkt_vc_shift              = 15,
+	.csi2_capture_cphy_pkt_dt_shift               = 20,
+	.csi2_capture_cphy_pkt_vc_shift               = 26,
 };
 
 
@@ -252,6 +260,7 @@
 	/*configurations */
 	.tpg_dtn_cfg_offset                           = 0xc,
 	.tpg_cgen_cfg_offset                          = 0x20,
+	.tpg_cpas_ife_reg_offset                      = 0x28,
 };
 
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c
index 020599d..c036bca 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c
@@ -181,3 +181,59 @@
 	return rc;
 }
 
+int cam_ife_csid_enable_ife_force_clock_on(struct cam_hw_soc_info  *soc_info,
+	uint32_t cpas_ife_base_offset)
+{
+	int rc = 0;
+	struct cam_csid_soc_private       *soc_private;
+	uint32_t                           cpass_ife_force_clk_offset;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_ISP, "Error Invalid params");
+		return -EINVAL;
+	}
+
+	soc_private = soc_info->soc_private;
+	cpass_ife_force_clk_offset =
+		cpas_ife_base_offset + (0x4 * soc_info->index);
+	rc = cam_cpas_reg_write(soc_private->cpas_handle, CAM_CPAS_REG_CPASTOP,
+		cpass_ife_force_clk_offset, 1, 1);
+
+	if (rc)
+		CAM_ERR(CAM_ISP, "CPASS set IFE:%d Force clock On failed",
+			soc_info->index);
+	else
+		CAM_DBG(CAM_ISP, "CPASS set IFE:%d Force clock On",
+		soc_info->index);
+
+	return rc;
+}
+
+int cam_ife_csid_disable_ife_force_clock_on(struct cam_hw_soc_info *soc_info,
+	uint32_t cpas_ife_base_offset)
+{
+	int rc = 0;
+	struct cam_csid_soc_private       *soc_private;
+	uint32_t                           cpass_ife_force_clk_offset;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_ISP, "Error Invalid params");
+		return -EINVAL;
+	}
+
+	soc_private = soc_info->soc_private;
+	cpass_ife_force_clk_offset =
+		cpas_ife_base_offset + (0x4 * soc_info->index);
+	rc = cam_cpas_reg_write(soc_private->cpas_handle, CAM_CPAS_REG_CPASTOP,
+		cpass_ife_force_clk_offset,  1, 0);
+
+	if (rc)
+		CAM_ERR(CAM_ISP, "CPASS set IFE:%d Force clock Off failed",
+			soc_info->index);
+	else
+		CAM_DBG(CAM_ISP, "CPASS set IFE:%d Force clock off",
+		soc_info->index);
+
+	return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h
index 1a30722..8e963de 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h
@@ -82,4 +82,33 @@
  */
 int cam_ife_csid_disable_soc_resources(struct cam_hw_soc_info *soc_info);
 
+/**
+ * cam_ife_csid_enable_ife_force_clock()
+ *
+ * @brief:                 if csid testgen used for dual isp case, before
+ *                         starting csid test gen, enable ife force clock on
+ *                         through cpas
+ *
+ * @soc_info:              soc info structure pointer
+ * @cpas_ife_base_offset:  cpas ife force clock base reg offset value
+ *
+ */
+int cam_ife_csid_enable_ife_force_clock_on(struct cam_hw_soc_info  *soc_info,
+	uint32_t cpas_ife_base_offset);
+
+/**
+ * cam_ife_csid_disable_ife_force_clock_on()
+ *
+ * @brief:                 disable the IFE force clock on after dual ISP
+ *                         CSID test gen stop
+ *
+ * @soc_info:              soc info structure pointer
+ * @cpas_ife_base_offset:  cpas ife force clock base reg offset value
+ *
+ */
+int cam_ife_csid_disable_ife_force_clock_on(struct cam_hw_soc_info *soc_info,
+	uint32_t cpas_ife_base_offset);
+
+
+
 #endif
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
index a70707a..37e0ce3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
@@ -151,6 +151,7 @@
  */
 enum cam_ife_csid_cmd_type {
 	CAM_IFE_CSID_CMD_GET_TIME_STAMP,
+	CAM_IFE_CSID_SET_CSID_DEBUG,
 	CAM_IFE_CSID_CMD_MAX,
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
index 60f1c8b..3a0c6a7 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
@@ -17,6 +17,7 @@
 #include "cam_hw.h"
 #include "cam_soc_util.h"
 #include "cam_irq_controller.h"
+#include <uapi/media/cam_isp.h>
 
 /*
  * struct cam_isp_timestamp:
@@ -175,4 +176,19 @@
 	struct cam_isp_port_hfr_config *io_hfr_cfg;
 };
 
+/*
+ * struct cam_isp_hw_dual_isp_update_args:
+ *
+ * @Brief:        update the dual isp striping configuration.
+ *
+ * @ split_id:    spilt id to inform left or rifht
+ * @ res:         resource node
+ * @ dual_cfg:    dual isp configuration
+ *
+ */
+struct cam_isp_hw_dual_isp_update_args {
+	enum cam_isp_hw_split_id         split_id;
+	struct cam_isp_resource_node    *res;
+	struct cam_isp_dual_config      *dual_cfg;
+};
 #endif /* _CAM_ISP_HW_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
index a64379c..96263de 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
@@ -50,6 +50,7 @@
 	CAM_VFE_HW_CMD_GET_REG_UPDATE,
 	CAM_VFE_HW_CMD_GET_HFR_UPDATE,
 	CAM_VFE_HW_CMD_GET_SECURE_MODE,
+	CAM_VFE_HW_CMD_STRIPE_UPDATE,
 	CAM_VFE_HW_CMD_MAX,
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
index 89db01d..77b830c 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
@@ -179,6 +179,13 @@
 
 	CAM_DBG(CAM_ISP, "Enable soc done");
 
+	/* Do HW Reset */
+	rc = cam_vfe_reset(hw_priv, NULL, 0);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "Reset Failed rc=%d", rc);
+		goto disable_soc;
+	}
+
 	rc = core_info->vfe_bus->hw_ops.init(core_info->vfe_bus->bus_priv,
 		NULL, 0);
 	if (rc) {
@@ -186,18 +193,7 @@
 		goto disable_soc;
 	}
 
-	/* Do HW Reset */
-	rc = cam_vfe_reset(hw_priv, NULL, 0);
-	if (rc) {
-		CAM_ERR(CAM_ISP, "Reset Failed rc=%d", rc);
-		goto deinit_bus;
-	}
-
 	return 0;
-
-deinit_bus:
-	core_info->vfe_bus->hw_ops.deinit(core_info->vfe_bus->bus_priv,
-		NULL, 0);
 disable_soc:
 	cam_vfe_disable_soc_resources(soc_info);
 decrement_open_cnt:
@@ -561,6 +557,7 @@
 		break;
 	case CAM_VFE_HW_CMD_GET_BUF_UPDATE:
 	case CAM_VFE_HW_CMD_GET_HFR_UPDATE:
+	case CAM_VFE_HW_CMD_STRIPE_UPDATE:
 		rc = core_info->vfe_bus->hw_ops.process_cmd(
 			core_info->vfe_bus->bus_priv, cmd_type, cmd_args,
 			arg_size);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
index dea906e..e3a6f7b 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
@@ -223,6 +223,9 @@
 		.comp_ovrwr_status            = 0x00002070,
 		.dual_comp_error_status       = 0x00002074,
 		.dual_comp_error_status       = 0x00002078,
+		.addr_sync_cfg                = 0x0000207C,
+		.addr_sync_frame_hdr          = 0x00002080,
+		.addr_sync_no_sync            = 0x00002084,
 	},
 	.bus_client_reg = {
 		/* BUS Client 0 */
@@ -674,26 +677,34 @@
 		/* 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,
 		},
 	},
 	.vfe_out_hw_info = {
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 1472e09..005d7e0 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -12,6 +12,7 @@
 
 #include <linux/ratelimit.h>
 #include <linux/slab.h>
+#include <uapi/media/cam_isp.h>
 #include "cam_io_util.h"
 #include "cam_debug_util.h"
 #include "cam_cdm_util.h"
@@ -34,14 +35,18 @@
 
 #define CAM_VFE_BUS_VER2_PAYLOAD_MAX             256
 
-#define CAM_VFE_RDI_BUS_DEFAULT_WIDTH           0xFF01
-#define CAM_VFE_RDI_BUS_DEFAULT_STRIDE          0xFF01
+#define CAM_VFE_RDI_BUS_DEFAULT_WIDTH               0xFF01
+#define CAM_VFE_RDI_BUS_DEFAULT_STRIDE              0xFF01
+#define CAM_VFE_BUS_INTRA_CLIENT_MASK               0x3
+#define CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT    8
+#define CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL        0xFFFFF
 
 #define ALIGNUP(value, alignment) \
 	((value + alignment - 1) / alignment * alignment)
 
 #define MAX_BUF_UPDATE_REG_NUM   \
-	(sizeof(struct cam_vfe_bus_ver2_reg_offset_bus_client)/4)
+	((sizeof(struct cam_vfe_bus_ver2_reg_offset_bus_client) +  \
+	sizeof(struct cam_vfe_bus_ver2_reg_offset_ubwc_client))/4)
 #define MAX_REG_VAL_PAIR_SIZE    \
 	(MAX_BUF_UPDATE_REG_NUM * 2 * CAM_PACKET_MAX_PLANES)
 
@@ -97,6 +102,7 @@
 	struct mutex                                bus_mutex;
 	uint32_t                                    secure_mode;
 	uint32_t                                    num_sec_out;
+	uint32_t                                    addr_no_sync;
 };
 
 struct cam_vfe_bus_ver2_wm_resource_data {
@@ -133,6 +139,7 @@
 	uint32_t             framedrop_pattern;
 
 	uint32_t             en_cfg;
+	uint32_t             is_dual;
 };
 
 struct cam_vfe_bus_ver2_comp_grp_data {
@@ -148,6 +155,7 @@
 	uint32_t                         dual_slave_core;
 	uint32_t                         intra_client_mask;
 	uint32_t                         composite_mask;
+	uint32_t                         addr_sync_mode;
 
 	uint32_t                         acquire_dev_cnt;
 	uint32_t                         irq_trigger_cnt;
@@ -284,7 +292,7 @@
 	case CAM_VFE_BUS_VER2_VFE_CORE_0:
 		switch (dual_slave_core) {
 		case CAM_VFE_BUS_VER2_VFE_CORE_1:
-			*intra_client_mask = 0x1;
+			*intra_client_mask = 0x3;
 			break;
 		case CAM_VFE_BUS_VER2_VFE_CORE_2:
 			*intra_client_mask = 0x2;
@@ -770,10 +778,10 @@
 	void                                  *ctx,
 	enum cam_vfe_bus_ver2_vfe_out_type     vfe_out_res_id,
 	enum cam_vfe_bus_plane_type            plane,
-	enum cam_isp_hw_split_id               split_id,
 	uint32_t                               subscribe_irq,
 	struct cam_isp_resource_node         **wm_res,
-	uint32_t                              *client_done_mask)
+	uint32_t                              *client_done_mask,
+	uint32_t                               is_dual)
 {
 	uint32_t wm_idx = 0;
 	struct cam_isp_resource_node              *wm_res_local = NULL;
@@ -802,6 +810,9 @@
 
 	rsrc_data->width = out_port_info->width;
 	rsrc_data->height = out_port_info->height;
+	rsrc_data->is_dual = is_dual;
+	/* Set WM offset value to default */
+	rsrc_data->offset  = 0;
 	CAM_DBG(CAM_ISP, "WM %d width %d height %d", rsrc_data->index,
 		rsrc_data->width, rsrc_data->height);
 
@@ -815,6 +826,7 @@
 		case CAM_FORMAT_MIPI_RAW_14:
 		case CAM_FORMAT_MIPI_RAW_16:
 		case CAM_FORMAT_MIPI_RAW_20:
+		case CAM_FORMAT_PLAIN128:
 			rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH;
 			rsrc_data->height = 0;
 			rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE;
@@ -859,10 +871,6 @@
 			rsrc_data->en_cfg = 0x1;
 			rsrc_data->pack_fmt = 0xA;
 			break;
-		case CAM_FORMAT_PLAIN128:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x0;
-			break;
 		default:
 			CAM_ERR(CAM_ISP, "Unsupported RDI format %d",
 				rsrc_data->format);
@@ -990,6 +998,7 @@
 	rsrc_data->init_cfg_done = false;
 	rsrc_data->hfr_cfg_done = false;
 	rsrc_data->en_cfg = 0;
+	rsrc_data->is_dual = 0;
 
 	wm_res->tasklet_info = NULL;
 	wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
@@ -1280,6 +1289,7 @@
 	if (!comp_grp_local) {
 		/* First find a free group */
 		if (is_dual) {
+			CAM_DBG(CAM_ISP, "Acquire dual comp group");
 			if (list_empty(&ver2_bus_priv->free_dual_comp_grp)) {
 				CAM_ERR(CAM_ISP, "No Free Composite Group");
 				return -ENODEV;
@@ -1292,7 +1302,10 @@
 				dual_slave_core,
 				comp_grp_local->hw_intf->hw_idx,
 				&rsrc_data->intra_client_mask);
+			if (rc)
+				return rc;
 		} else {
+			CAM_DBG(CAM_ISP, "Acquire comp group");
 			if (list_empty(&ver2_bus_priv->free_comp_grp)) {
 				CAM_ERR(CAM_ISP, "No Free Composite Group");
 				return -ENODEV;
@@ -1312,6 +1325,11 @@
 		rsrc_data->unique_id = unique_id;
 		rsrc_data->comp_grp_local_idx = bus_comp_grp_id;
 
+		if (is_master)
+			rsrc_data->addr_sync_mode = 0;
+		else
+			rsrc_data->addr_sync_mode = 1;
+
 		list_add_tail(&comp_grp_local->list,
 			&ver2_bus_priv->used_comp_grp);
 
@@ -1327,6 +1345,8 @@
 		}
 	}
 
+	CAM_DBG(CAM_ISP, "Comp Grp type %u", rsrc_data->comp_grp_type);
+
 	rsrc_data->ctx = ctx;
 	rsrc_data->acquire_dev_cnt++;
 	*comp_grp = comp_grp_local;
@@ -1359,6 +1379,7 @@
 	}
 
 	in_rsrc_data = in_comp_grp->res_priv;
+	CAM_DBG(CAM_ISP, "Comp Grp type %u", in_rsrc_data->comp_grp_type);
 
 	list_for_each_entry(comp_grp, &ver2_bus_priv->used_comp_grp, list) {
 		if (comp_grp == in_comp_grp) {
@@ -1381,6 +1402,7 @@
 		in_rsrc_data->comp_grp_local_idx = CAM_VFE_BUS_COMP_GROUP_NONE;
 		in_rsrc_data->composite_mask = 0;
 		in_rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX;
+		in_rsrc_data->addr_sync_mode = 0;
 
 		comp_grp->tasklet_info = NULL;
 		comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
@@ -1405,55 +1427,103 @@
 static int cam_vfe_bus_start_comp_grp(struct cam_isp_resource_node *comp_grp)
 {
 	int rc = 0;
+	uint32_t addr_sync_cfg;
 	struct cam_vfe_bus_ver2_comp_grp_data      *rsrc_data =
 		comp_grp->res_priv;
 	struct cam_vfe_bus_ver2_common_data        *common_data =
 		rsrc_data->common_data;
 	uint32_t bus_irq_reg_mask[CAM_VFE_BUS_IRQ_MAX] = {0};
 
+	CAM_DBG(CAM_ISP, "comp group id:%d streaming state:%d",
+		rsrc_data->comp_grp_type, comp_grp->res_state);
+
 	cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
 		rsrc_data->hw_regs->comp_mask);
+	if (comp_grp->res_state == CAM_ISP_RESOURCE_STATE_STREAMING)
+		return 0;
 
 	CAM_DBG(CAM_ISP, "composite_mask is 0x%x", rsrc_data->composite_mask);
 	CAM_DBG(CAM_ISP, "composite_mask addr 0x%x",
 		rsrc_data->hw_regs->comp_mask);
 
 	if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
-		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 &&
-		rsrc_data->is_master) {
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) {
 		int dual_comp_grp = (rsrc_data->comp_grp_type -
 			CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
-		int intra_client_en = cam_io_r_mb(common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
 
-		/* 2 Bits per comp_grp. Hence left shift by comp_grp * 2 */
-		intra_client_en |=
-			(rsrc_data->intra_client_mask << dual_comp_grp * 2);
+		if (rsrc_data->is_master) {
+			int intra_client_en = cam_io_r_mb(
+				common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
 
-		cam_io_w_mb(intra_client_en, common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
+			/*
+			 * 2 Bits per comp_grp. Hence left shift by
+			 * comp_grp * 2
+			 */
+			intra_client_en |=
+				(rsrc_data->intra_client_mask <<
+					(dual_comp_grp * 2));
 
-		bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG2] = (1 << dual_comp_grp);
+			cam_io_w_mb(intra_client_en, common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
+
+			bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG2] =
+				(1 << dual_comp_grp);
+		}
+
+		CAM_DBG(CAM_ISP, "addr_sync_mask addr 0x%x",
+			rsrc_data->hw_regs->addr_sync_mask);
+		cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
+			rsrc_data->hw_regs->addr_sync_mask);
+
+		addr_sync_cfg = cam_io_r_mb(common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+		addr_sync_cfg |= (rsrc_data->addr_sync_mode << dual_comp_grp);
+		/*
+		 * 2 Bits per dual_comp_grp. dual_comp_grp stats at bit number
+		 * 8. Hence left shift cdual_comp_grp dual comp_grp * 2 and
+		 * add 8
+		 */
+		addr_sync_cfg |=
+			(rsrc_data->intra_client_mask <<
+				((dual_comp_grp * 2) +
+				CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT));
+		cam_io_w_mb(addr_sync_cfg, common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+
+		common_data->addr_no_sync &= ~(rsrc_data->composite_mask);
+		cam_io_w_mb(common_data->addr_no_sync, common_data->mem_base +
+			common_data->common_reg->addr_sync_no_sync);
+		CAM_DBG(CAM_ISP, "addr_sync_cfg: 0x%x addr_no_sync_cfg: 0x%x",
+			addr_sync_cfg, common_data->addr_no_sync);
 	} else {
 		/* IRQ bits for COMP GRP start at 5. So add 5 to the shift */
 		bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG0] =
 			(1 << (rsrc_data->comp_grp_type + 5));
 	}
 
-	/* Subscribe IRQ */
+	/*
+	 * For Dual composite subscribe IRQ only for master
+	 * For regular composite, subscribe IRQ always
+	 */
 	CAM_DBG(CAM_ISP, "Subscribe COMP_GRP%d IRQ", rsrc_data->comp_grp_type);
-	comp_grp->irq_handle = cam_irq_controller_subscribe_irq(
-		common_data->bus_irq_controller, CAM_IRQ_PRIORITY_1,
-		bus_irq_reg_mask, comp_grp,
-		comp_grp->top_half_handler,
-		cam_ife_mgr_do_tasklet_buf_done,
-		comp_grp->tasklet_info, cam_tasklet_enqueue_cmd);
-	if (comp_grp->irq_handle < 0) {
-		CAM_ERR(CAM_ISP, "Subscribe IRQ failed for comp_grp %d",
-			rsrc_data->comp_grp_type);
-		return -EFAULT;
+	if (((rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) &&
+		(rsrc_data->is_master)) ||
+		(rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)) {
+		comp_grp->irq_handle = cam_irq_controller_subscribe_irq(
+			common_data->bus_irq_controller, CAM_IRQ_PRIORITY_1,
+			bus_irq_reg_mask, comp_grp,
+			comp_grp->top_half_handler,
+			cam_ife_mgr_do_tasklet_buf_done,
+			comp_grp->tasklet_info, cam_tasklet_enqueue_cmd);
+		if (comp_grp->irq_handle < 0) {
+			CAM_ERR(CAM_ISP, "Subscribe IRQ failed for comp_grp %d",
+				rsrc_data->comp_grp_type);
+			return -EFAULT;
+		}
 	}
-
 	comp_grp->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
 
 	return rc;
@@ -1462,32 +1532,65 @@
 static int cam_vfe_bus_stop_comp_grp(struct cam_isp_resource_node *comp_grp)
 {
 	int rc = 0;
+	uint32_t addr_sync_cfg;
 	struct cam_vfe_bus_ver2_comp_grp_data      *rsrc_data =
 		comp_grp->res_priv;
 	struct cam_vfe_bus_ver2_common_data        *common_data =
 		rsrc_data->common_data;
 
 	/* Unsubscribe IRQ */
-	rc = cam_irq_controller_unsubscribe_irq(
-		common_data->bus_irq_controller,
-		comp_grp->irq_handle);
+	if (((rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) &&
+		(rsrc_data->is_master)) ||
+		(rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)) {
+		rc = cam_irq_controller_unsubscribe_irq(
+			common_data->bus_irq_controller,
+			comp_grp->irq_handle);
+	}
 
 	cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
 		rsrc_data->hw_regs->comp_mask);
 	if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
-		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 &&
-		rsrc_data->is_master) {
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) {
+
 		int dual_comp_grp = (rsrc_data->comp_grp_type -
 			CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
-		int intra_client_en = cam_io_r_mb(common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
 
-		/* 2 Bits per comp_grp. Hence left shift by comp_grp * 2 */
-		intra_client_en &=
-			~(rsrc_data->intra_client_mask << dual_comp_grp * 2);
+		if (rsrc_data->is_master) {
+			int intra_client_en = cam_io_r_mb(
+				common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
 
-		cam_io_w_mb(intra_client_en, common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
+			/*
+			 * 2 Bits per comp_grp. Hence left shift by
+			 * comp_grp * 2
+			 */
+			intra_client_en &=
+				~(rsrc_data->intra_client_mask <<
+					dual_comp_grp * 2);
+
+			cam_io_w_mb(intra_client_en, common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
+		}
+
+		addr_sync_cfg = cam_io_r_mb(common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+		addr_sync_cfg &= ~(1 << dual_comp_grp);
+		addr_sync_cfg &= ~(CAM_VFE_BUS_INTRA_CLIENT_MASK <<
+			((dual_comp_grp * 2) +
+			CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT));
+		cam_io_w_mb(addr_sync_cfg, common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+
+		cam_io_w_mb(0, common_data->mem_base +
+			rsrc_data->hw_regs->addr_sync_mask);
+		common_data->addr_no_sync |= rsrc_data->composite_mask;
+		cam_io_w_mb(common_data->addr_no_sync, common_data->mem_base +
+			common_data->common_reg->addr_sync_no_sync);
+		CAM_DBG(CAM_ISP, "addr_sync_cfg: 0x% addr_no_sync_cfg: 0x%x",
+			addr_sync_cfg, common_data->addr_no_sync);
+
 	}
 
 	comp_grp->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
@@ -1514,6 +1617,7 @@
 
 	CAM_DBG(CAM_ISP, "IRQ status_0 = %x", th_payload->evt_status_arr[0]);
 	CAM_DBG(CAM_ISP, "IRQ status_1 = %x", th_payload->evt_status_arr[1]);
+	CAM_DBG(CAM_ISP, "IRQ status_2 = %x", th_payload->evt_status_arr[2]);
 
 	rc  = cam_vfe_bus_get_evt_payload(rsrc_data->common_data, &evt_payload);
 	if (rc) {
@@ -1636,7 +1740,7 @@
 			if (rsrc_data->irq_trigger_cnt ==
 				rsrc_data->acquire_dev_cnt) {
 				cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2] &=
-					~BIT(comp_grp_id + 5);
+					~BIT(comp_grp_id);
 				rsrc_data->irq_trigger_cnt = 0;
 			}
 			rc = CAM_VFE_IRQ_STATUS_SUCCESS;
@@ -1818,10 +1922,12 @@
 	rsrc_data->cdm_util_ops = out_acquire_args->cdm_ops;
 
 	/* Reserve Composite Group */
-	if (num_wm > 1 || (out_acquire_args->out_port_info->comp_grp_id >
-		CAM_ISP_RES_COMP_GROUP_NONE &&
-		out_acquire_args->out_port_info->comp_grp_id <
-		CAM_ISP_RES_COMP_GROUP_ID_MAX)) {
+		if (num_wm > 1 || (out_acquire_args->is_dual) ||
+			(out_acquire_args->out_port_info->comp_grp_id >
+			CAM_ISP_RES_COMP_GROUP_NONE &&
+			out_acquire_args->out_port_info->comp_grp_id <
+			CAM_ISP_RES_COMP_GROUP_ID_MAX)) {
+
 		rc = cam_vfe_bus_acquire_comp_grp(ver2_bus_priv,
 			out_acquire_args->out_port_info,
 			acq_args->tasklet,
@@ -1852,10 +1958,10 @@
 			out_acquire_args->ctx,
 			vfe_out_res_id,
 			i,
-			out_acquire_args->split_id,
 			subscribe_irq,
 			&rsrc_data->wm_res[i],
-			&client_done_mask);
+			&client_done_mask,
+			out_acquire_args->is_dual);
 		if (rc) {
 			CAM_ERR(CAM_ISP,
 				"VFE%d WM acquire failed for Out %d rc=%d",
@@ -1980,28 +2086,6 @@
 	if (rsrc_data->comp_grp)
 		rc = cam_vfe_bus_start_comp_grp(rsrc_data->comp_grp);
 
-	/* BUS_WR_INPUT_IF_ADDR_SYNC_CFG */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000207C);
-	/*  BUS_WR_INPUT_IF_ADDR_SYNC_FRAME_HEADER */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002080);
-	/* BUS_WR_INPUT_IF_ADDR_SYNC_NO_SYNC */
-	cam_io_w_mb(0xFFFFF, rsrc_data->common_data->mem_base + 0x00002084);
-	/*  BUS_WR_INPUT_IF_ADDR_SYNC_0 */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002088);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000208c);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002090);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002094);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002098);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000209c);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020a0);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020a4);
-
-	/* no clock gating at bus input */
-	cam_io_w_mb(0xFFFFF, rsrc_data->common_data->mem_base + 0x0000200C);
-
-	/* BUS_WR_TEST_BUS_CTRL */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000211C);
-
 	vfe_out->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
 	return rc;
 }
@@ -2030,7 +2114,6 @@
 	for (i = 0; i < rsrc_data->num_wm; i++)
 		rc = cam_vfe_bus_stop_wm(rsrc_data->wm_res[i]);
 
-
 	vfe_out->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
 	return rc;
 }
@@ -2149,11 +2232,6 @@
 	uint32_t  i, j, size = 0;
 	uint32_t  frame_inc = 0;
 
-	/*
-	 * Need the entire buf io config so we can get the stride info
-	 * for the wm.
-	 */
-
 	bus_priv = (struct cam_vfe_bus_ver2_priv  *) priv;
 	update_buf =  (struct cam_isp_hw_get_buf_update *) cmd_args;
 
@@ -2185,6 +2263,12 @@
 
 		wm_data = vfe_out_data->wm_res[i]->res_priv;
 
+		/* update width register */
+		CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
+			wm_data->hw_regs->buffer_width_cfg,
+			wm_data->width);
+		CAM_DBG(CAM_ISP, "image width 0x%x", wm_data->width);
+
 		/* For initial configuration program all bus registers */
 		if ((wm_data->stride != io_cfg->planes[i].plane_stride ||
 			!wm_data->init_cfg_done) && (wm_data->index >= 3)) {
@@ -2255,7 +2339,12 @@
 			CAM_DBG(CAM_ISP, "packer cfg 0x%x",
 				wm_data->packer_cfg);
 
-			if (wm_data->tile_cfg != io_cfg->planes[i].tile_config
+			if (wm_data->is_dual) {
+				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
+					wm_data->hw_regs->ubwc_regs->tile_cfg,
+					wm_data->tile_cfg);
+			} else if ((wm_data->tile_cfg !=
+				io_cfg->planes[i].tile_config)
 				|| !wm_data->init_cfg_done) {
 				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 					wm_data->hw_regs->ubwc_regs->tile_cfg,
@@ -2265,7 +2354,22 @@
 			}
 			CAM_DBG(CAM_ISP, "tile cfg 0x%x", wm_data->tile_cfg);
 
-			if (wm_data->h_init != io_cfg->planes[i].h_init ||
+			if (wm_data->is_dual) {
+				if ((wm_data->h_init != wm_data->offset) ||
+					!wm_data->init_cfg_done) {
+				/*
+				 * For dual ife h init value need to take from
+				 * offset.Striping config update offset value
+				 */
+					CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair,
+						j,
+						wm_data->hw_regs->ubwc_regs->
+						h_init,
+						wm_data->offset);
+					wm_data->h_init = wm_data->offset;
+				}
+			} else if (wm_data->h_init !=
+				io_cfg->planes[i].h_init ||
 				!wm_data->init_cfg_done) {
 				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 					wm_data->hw_regs->ubwc_regs->h_init,
@@ -2337,8 +2441,9 @@
 				io_cfg->planes[i].meta_size));
 		else
 			CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
-				wm_data->hw_regs->image_addr,
-				update_buf->image_buf[i]);
+			wm_data->hw_regs->image_addr,
+			update_buf->image_buf[i] +
+			wm_data->offset);
 
 		CAM_DBG(CAM_ISP, "image address 0x%x", reg_val_pair[j-1]);
 
@@ -2478,6 +2583,50 @@
 	return 0;
 }
 
+static int cam_vfe_bus_update_stripe_cfg(void *priv, void *cmd_args,
+	uint32_t arg_size)
+{
+	struct cam_vfe_bus_ver2_priv                *bus_priv;
+	struct cam_isp_hw_dual_isp_update_args      *stripe_args;
+	struct cam_vfe_bus_ver2_vfe_out_data        *vfe_out_data = NULL;
+	struct cam_vfe_bus_ver2_wm_resource_data    *wm_data = NULL;
+	struct cam_isp_dual_stripe_config           *stripe_config;
+	uint32_t outport_id, ports_plane_idx, i;
+
+	bus_priv = (struct cam_vfe_bus_ver2_priv  *) priv;
+	stripe_args = (struct cam_isp_hw_dual_isp_update_args *)cmd_args;
+
+	vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *)
+		stripe_args->res->res_priv;
+
+	if (!vfe_out_data) {
+		CAM_ERR(CAM_ISP, "Failed! Invalid data");
+		return -EINVAL;
+	}
+
+	outport_id = stripe_args->res->res_id & 0xFF;
+	if (stripe_args->res->res_id < CAM_ISP_IFE_OUT_RES_BASE ||
+		stripe_args->res->res_id >= CAM_ISP_IFE_OUT_RES_MAX)
+		return 0;
+
+	ports_plane_idx = (stripe_args->split_id *
+	(stripe_args->dual_cfg->num_ports * CAM_PACKET_MAX_PLANES)) +
+	(outport_id * CAM_PACKET_MAX_PLANES);
+	for (i = 0; i < vfe_out_data->num_wm; i++) {
+		wm_data = vfe_out_data->wm_res[i]->res_priv;
+		stripe_config = (struct cam_isp_dual_stripe_config  *)
+			&stripe_args->dual_cfg->stripes[ports_plane_idx + i];
+		wm_data->width = stripe_config->width;
+		wm_data->offset = stripe_config->offset;
+		wm_data->tile_cfg = stripe_config->tileconfig;
+		CAM_DBG(CAM_ISP, "id:%x wm:%d width:0x%x offset:%x tilecfg:%x",
+			stripe_args->res->res_id, i, wm_data->width,
+			wm_data->offset, wm_data->tile_cfg);
+	}
+
+	return 0;
+}
+
 static int cam_vfe_bus_start_hw(void *hw_priv,
 	void *start_hw_args, uint32_t arg_size)
 {
@@ -2518,6 +2667,21 @@
 		return -EFAULT;
 	}
 
+	/* BUS_WR_INPUT_IF_ADDR_SYNC_FRAME_HEADER */
+	cam_io_w_mb(0x0, bus_priv->common_data.mem_base +
+		bus_priv->common_data.common_reg->addr_sync_frame_hdr);
+
+	/* no clock gating at bus input */
+	cam_io_w_mb(0xFFFFF, bus_priv->common_data.mem_base + 0x0000200C);
+
+	/* BUS_WR_TEST_BUS_CTRL */
+	cam_io_w_mb(0x0, bus_priv->common_data.mem_base + 0x0000211C);
+
+	/* if addr_no_sync has default value then config the addr no sync reg */
+	cam_io_w_mb(CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL,
+		bus_priv->common_data.mem_base +
+		bus_priv->common_data.common_reg->addr_sync_no_sync);
+
 	return 0;
 }
 
@@ -2568,6 +2732,9 @@
 	case CAM_VFE_HW_CMD_GET_SECURE_MODE:
 		rc = cam_vfe_bus_get_secure_mode(priv, cmd_args, arg_size);
 		break;
+	case CAM_VFE_HW_CMD_STRIPE_UPDATE:
+		rc = cam_vfe_bus_update_stripe_cfg(priv, cmd_args, arg_size);
+		break;
 	default:
 		CAM_ERR_RATE_LIMIT(CAM_ISP, "Invalid camif process command:%d",
 			cmd_type);
@@ -2624,6 +2791,9 @@
 	bus_priv->common_data.hw_intf            = hw_intf;
 	bus_priv->common_data.vfe_irq_controller = vfe_irq_controller;
 	bus_priv->common_data.common_reg         = &ver2_hw_info->common_reg;
+	bus_priv->common_data.addr_no_sync       =
+		CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL;
+
 	mutex_init(&bus_priv->common_data.bus_mutex);
 
 	rc = cam_irq_controller_init(drv_name, bus_priv->common_data.mem_base,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
index ba98077..c90d4ce 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
@@ -81,8 +81,8 @@
 	uint32_t dual_comp_error_status;
 	uint32_t dual_comp_ovrwr_status;
 	uint32_t addr_sync_cfg;
-	uint32_t addr_syn_frame_hdr;
-	uint32_t addr_syn_no_sync;
+	uint32_t addr_sync_frame_hdr;
+	uint32_t addr_sync_no_sync;
 };
 
 /*
@@ -130,9 +130,12 @@
  * struct cam_vfe_bus_ver2_reg_offset_comp_grp:
  *
  * @Brief:        Register offsets for Composite Group registers
+ * comp_mask:     Comp group register address
+ * addr_sync_mask:Address sync group register address
  */
 struct cam_vfe_bus_ver2_reg_offset_comp_grp {
 	uint32_t                            comp_mask;
+	uint32_t                            addr_sync_mask;
 };
 
 /*
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
index f255691..e81a9f2 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -158,8 +158,14 @@
 	/*config vfe core*/
 	val = (rsrc_data->pix_pattern <<
 			rsrc_data->reg_data->pixel_pattern_shift);
+	if (rsrc_data->sync_mode == CAM_ISP_HW_SYNC_SLAVE)
+		val |= (1 << rsrc_data->reg_data->extern_reg_update_shift);
+
 	cam_io_w_mb(val, rsrc_data->mem_base + rsrc_data->common_reg->core_cfg);
 
+	CAM_DBG(CAM_ISP, "hw id:%d core_cfg val:%d", camif_res->hw_intf->hw_idx,
+		val);
+
 	cam_io_w_mb(0x00400040, rsrc_data->mem_base +
 		rsrc_data->camif_reg->camif_config);
 	cam_io_w_mb(0x1, rsrc_data->mem_base +
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c
index 461e3c3..797873c 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c
@@ -67,7 +67,7 @@
 	rsrc_data = rdi_res->res_priv;
 	reg_val_pair[0] = rsrc_data->rdi_reg->reg_update_cmd;
 	reg_val_pair[1] = rsrc_data->reg_data->reg_update_cmd_data;
-	CAM_DBG(CAM_ISP, "Error - RDI%d reg_update_cmd %x",
+	CAM_DBG(CAM_ISP, "RDI%d reg_update_cmd %x",
 		rdi_res->res_id - CAM_ISP_HW_VFE_IN_RDI0, reg_val_pair[1]);
 
 	cdm_util_ops->cdm_write_regrandom(cdm_args->cmd_buf_addr,
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 681504d..8565ad1 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
@@ -180,6 +180,15 @@
 		tbl->pd, curr_idx, tbl->slot[curr_idx].state,
 		tbl->skip_traverse, traverse_data->in_q->slot[curr_idx].status);
 
+	if (tbl->inject_delay > 0) {
+		CAM_DBG(CAM_CRM, "Injecting Delay of one frame");
+		apply_data[tbl->pd].req_id = -1;
+		tbl->inject_delay--;
+		/* This pd table is not ready to proceed with asked idx */
+		SET_FAILURE_BIT(traverse_data->result, tbl->pd);
+		return -EAGAIN;
+	}
+
 	/* Check if req is ready or in skip mode or pd tbl is in skip mode */
 	if (tbl->slot[curr_idx].state == CRM_REQ_STATE_READY ||
 		traverse_data->in_q->slot[curr_idx].skip_idx == 1 ||
@@ -1210,6 +1219,10 @@
 		mutex_unlock(&link->req.lock);
 		goto end;
 	}
+
+	if (add_req->skip_before_applying > tbl->inject_delay)
+		tbl->inject_delay = add_req->skip_before_applying;
+
 	slot = &tbl->slot[idx];
 	if (slot->state != CRM_REQ_STATE_PENDING &&
 		slot->state != CRM_REQ_STATE_EMPTY) {
@@ -1452,6 +1465,7 @@
 	dev_req->req_id = add_req->req_id;
 	dev_req->link_hdl = add_req->link_hdl;
 	dev_req->dev_hdl = add_req->dev_hdl;
+	dev_req->skip_before_applying = add_req->skip_before_applying;
 	task->process_cb = &cam_req_mgr_process_add_req;
 	rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
 	CAM_DBG(CAM_CRM, "X: dev %x dev req %lld",
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 e45d634..3687a5a 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
@@ -173,6 +173,7 @@
  * @pd_delta      : differnce between this table's pipeline delay and next
  * @num_slots     : number of request slots present in the table
  * @slot          : array of slots tracking requests availability at devices
+ * @inject_delay  : insert extra bubbling for flash type of use cases
  */
 struct cam_req_mgr_req_tbl {
 	int32_t                     id;
@@ -184,6 +185,7 @@
 	int32_t                     pd_delta;
 	int32_t                     num_slots;
 	struct cam_req_mgr_tbl_slot slot[MAX_REQ_SLOTS];
+	uint32_t                    inject_delay;
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h
index f61c41e..475b640 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h
@@ -15,13 +15,9 @@
 #define CRM_TRACE_ENABLE 0
 #define CRM_DEBUG_MUTEX 0
 
-#define SET_SUCCESS_BIT(ret, pd)	{\
-	(ret) |= (1 << (pd));	\
-	}
+#define SET_SUCCESS_BIT(ret, pd)  (ret |= (1 << (pd)))
 
-#define SET_FAILURE_BIT(ret, pd)	{\
-	(ret) &= (0 << (pd));	\
-	}
+#define SET_FAILURE_BIT(ret, pd)  (ret &= (~(1 << (pd))))
 
 #define CRM_GET_REQ_ID(in_q, idx) in_q->slot[idx].req_id
 
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
index 6195b59..ce8dfa7 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
@@ -216,15 +216,18 @@
 
 /**
  * struct cam_req_mgr_add_request
- * @link_hdl : link identifier
- * @dev_hdl  : device handle which has sent this req id
- * @req_id   : req id which device is ready to process
- *
+ * @link_hdl             : link identifier
+ * @dev_hdl              : device handle which has sent this req id
+ * @req_id               : req id which device is ready to process
+ * @skip_before_applying : before applying req mgr introduce bubble
+ *                         by not sending request to device/s.
+ *                         ex: IFE and Flash
  */
 struct cam_req_mgr_add_request {
 	int32_t  link_hdl;
 	int32_t  dev_hdl;
 	uint64_t req_id;
+	uint32_t skip_before_applying;
 };
 
 
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index 8ffa0ff..c3475b6 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
@@ -93,6 +93,8 @@
 
 	i2c_info = (struct cam_cmd_i2c_info *)cmd_buf;
 	if (a_ctrl->io_master_info.master_type == CCI_MASTER) {
+		a_ctrl->io_master_info.cci_client->cci_i2c_master =
+			a_ctrl->cci_i2c_master;
 		a_ctrl->io_master_info.cci_client->i2c_freq_mode =
 			i2c_info->i2c_freq_mode;
 		a_ctrl->io_master_info.cci_client->sid =
@@ -370,6 +372,7 @@
 		add_req.link_hdl = a_ctrl->bridge_intf.link_hdl;
 		add_req.req_id = csl_packet->header.request_id;
 		add_req.dev_hdl = a_ctrl->bridge_intf.device_hdl;
+		add_req.skip_before_applying = 0;
 		if (a_ctrl->bridge_intf.crm_cb &&
 			a_ctrl->bridge_intf.crm_cb->add_req)
 			a_ctrl->bridge_intf.crm_cb->add_req(&add_req);
@@ -554,7 +557,7 @@
 	case CAM_QUERY_CAP: {
 		struct cam_actuator_query_cap actuator_cap = {0};
 
-		actuator_cap.slot_info = a_ctrl->id;
+		actuator_cap.slot_info = a_ctrl->soc_info.index;
 		if (copy_to_user((void __user *) cmd->handle, &actuator_cap,
 			sizeof(struct cam_actuator_query_cap))) {
 			CAM_ERR(CAM_ACTUATOR, "Failed Copy to User");
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 b58f5d8..45dbba1 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
@@ -286,8 +286,7 @@
 	}
 
 	/* Fill platform device id*/
-	a_ctrl->id = a_ctrl->soc_info.index;
-	pdev->id = a_ctrl->id;
+	pdev->id = a_ctrl->soc_info.index;
 
 	rc = cam_actuator_init_subdev(a_ctrl);
 	if (rc)
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
index fdf881f3..ad42a3d 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
@@ -75,7 +75,6 @@
  * @cci_i2c_master: I2C structure
  * @io_master_info: Information about the communication master
  * @actuator_mutex: Actuator mutex
- * @id: Cell Index
  * @act_apply_state: Actuator settings aRegulator config
  * @gconf: GPIO config
  * @pinctrl_info: Pinctrl information
@@ -91,7 +90,6 @@
 	struct camera_io_master io_master_info;
 	struct cam_hw_soc_info soc_info;
 	struct mutex actuator_mutex;
-	uint32_t id;
 	enum msm_actuator_state_t act_apply_state;
 	struct msm_camera_gpio_num_info *gpio_num_info;
 	uint8_t cam_pinctrl_status;
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 1a3f947..57d576a 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
@@ -18,11 +18,12 @@
 #include <soc/qcom/scm.h>
 #include <cam_mem_mgr.h>
 
-static int cam_csiphy_mem_dmp_param;
-module_param(cam_csiphy_mem_dmp_param, int, 0644);
 #define SCM_SVC_CAMERASS 0x18
 #define SECURE_SYSCALL_ID 0x6
 
+static int csiphy_dump;
+module_param(csiphy_dump, int, 0644);
+
 static int cam_csiphy_notify_secure_mode(int phy, bool protect)
 {
 	struct scm_desc desc = {0};
@@ -572,7 +573,7 @@
 			goto release_mutex;
 		}
 		rc = cam_csiphy_config_dev(csiphy_dev);
-		if (cam_csiphy_mem_dmp_param == 1)
+		if (csiphy_dump == 1)
 			cam_csiphy_mem_dmp(&csiphy_dev->soc_info);
 
 		if (rc < 0) {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
index 506fc0e..d2a8467 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
@@ -14,6 +14,47 @@
 #include "cam_csiphy_core.h"
 #include "include/cam_csiphy_1_0_hwreg.h"
 
+#define BYTES_PER_REGISTER           4
+#define NUM_REGISTER_PER_LINE        4
+#define REG_OFFSET(__start, __i)    ((__start) + ((__i) * BYTES_PER_REGISTER))
+
+static int cam_io_phy_dump(void __iomem *base_addr,
+	uint32_t start_offset, int size)
+{
+	char          line_str[128];
+	char         *p_str;
+	int           i;
+	uint32_t      data;
+
+	CAM_INFO(CAM_CSIPHY, "addr=%pK offset=0x%x size=%d",
+		base_addr, start_offset, size);
+
+	if (!base_addr || (size <= 0))
+		return -EINVAL;
+
+	line_str[0] = '\0';
+	p_str = line_str;
+	for (i = 0; i < size; i++) {
+		if (i % NUM_REGISTER_PER_LINE == 0) {
+			snprintf(p_str, 12, "0x%08x: ",
+				REG_OFFSET(start_offset, i));
+			p_str += 11;
+		}
+		data = readl_relaxed(base_addr + REG_OFFSET(start_offset, i));
+		snprintf(p_str, 9, "%08x ", data);
+		p_str += 8;
+		if ((i + 1) % NUM_REGISTER_PER_LINE == 0) {
+			CAM_ERR(CAM_CSIPHY, "%s", line_str);
+			line_str[0] = '\0';
+			p_str = line_str;
+		}
+	}
+	if (line_str[0] != '\0')
+		CAM_ERR(CAM_CSIPHY, "%s", line_str);
+
+	return 0;
+}
+
 int32_t cam_csiphy_mem_dmp(struct cam_hw_soc_info *soc_info)
 {
 	int32_t rc = 0;
@@ -27,7 +68,7 @@
 	}
 	addr = soc_info->reg_map[0].mem_base;
 	size = resource_size(soc_info->mem_block[0]);
-	rc = cam_io_dump(addr, 0, (size >> 2));
+	rc = cam_io_phy_dump(addr, 0, (size >> 2));
 	if (rc < 0) {
 		CAM_ERR(CAM_CSIPHY, "generating dump failed %d", rc);
 		return rc;
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 6eab59a..a173954 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
@@ -754,7 +754,7 @@
 	mutex_lock(&(e_ctrl->eeprom_mutex));
 	switch (cmd->op_code) {
 	case CAM_QUERY_CAP:
-		eeprom_cap.slot_info = e_ctrl->subdev_id;
+		eeprom_cap.slot_info = e_ctrl->soc_info.index;
 		if (e_ctrl->userspace_probe == false)
 			eeprom_cap.eeprom_kernel_probe = true;
 		else
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
index a98bf00..e06e22a 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
@@ -154,7 +154,6 @@
  * @cci_i2c_master  :   I2C structure
  * @v4l2_dev_str    :   V4L2 device structure
  * @bridge_intf     :   bridge interface params
- * @subdev_id       :   subdev id
  * @userspace_probe :   flag indicates userspace or kernel probe
  * @cal_data        :   Calibration data
  * @device_name     :   Device name
@@ -171,7 +170,6 @@
 	struct cam_subdev v4l2_dev_str;
 	struct cam_eeprom_intf_params bridge_intf;
 	enum msm_camera_device_type_t eeprom_device_type;
-	uint32_t subdev_id;
 	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 f455715..b0fe293 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
@@ -634,6 +634,13 @@
 		add_req.link_hdl = fctrl->bridge_intf.link_hdl;
 		add_req.req_id = csl_packet->header.request_id;
 		add_req.dev_hdl = fctrl->bridge_intf.device_hdl;
+
+		if ((csl_packet->header.op_code & 0xFFFFF) ==
+			CAM_FLASH_PACKET_OPCODE_SET_OPS)
+			add_req.skip_before_applying = 1;
+		else
+			add_req.skip_before_applying = 0;
+
 		if (fctrl->bridge_intf.crm_cb &&
 			fctrl->bridge_intf.crm_cb->add_req)
 			fctrl->bridge_intf.crm_cb->add_req(&add_req);
@@ -648,7 +655,7 @@
 	info->dev_id = CAM_REQ_MGR_DEVICE_FLASH;
 	strlcpy(info->name, CAM_FLASH_NAME, sizeof(info->name));
 	info->p_delay = CAM_FLASH_PIPELINE_DELAY;
-	info->trigger = CAM_TRIGGER_POINT_EOF;
+	info->trigger = CAM_TRIGGER_POINT_SOF;
 	return 0;
 }
 
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 72cabf1..0a283ab 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
@@ -496,7 +496,7 @@
 	mutex_lock(&(o_ctrl->ois_mutex));
 	switch (cmd->op_code) {
 	case CAM_QUERY_CAP:
-		ois_cap.slot_info = o_ctrl->subdev_id;
+		ois_cap.slot_info = o_ctrl->soc_info.index;
 
 		if (copy_to_user((void __user *) cmd->handle,
 			&ois_cap,
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 95ebed1..794b65a 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
@@ -95,7 +95,6 @@
  * @i2c_mode_data   :   ois i2c mode settings
  * @i2c_calib_data  :   ois i2c calib settings
  * @ois_device_type :   ois device type
- * @subdev_id       :   subdev id
  * @ois_name        :   ois name
  * @ois_fw_flag     :   flag for firmware download
  * @is_ois_calib    :   flag for Calibration data
@@ -116,7 +115,6 @@
 	struct i2c_settings_array i2c_calib_data;
 	struct i2c_settings_array i2c_mode_data;
 	enum msm_camera_device_type_t ois_device_type;
-	uint32_t subdev_id;
 	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_ois/cam_ois_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.c
index 916e699..5886413 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.c
@@ -90,13 +90,6 @@
 		return -EINVAL;
 	}
 
-	rc = of_property_read_u32(of_node, "cell-index",
-		&o_ctrl->subdev_id);
-	if (rc < 0) {
-		CAM_ERR(CAM_OIS, "failed rc %d", rc);
-		return rc;
-	}
-
 	if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
 		rc = of_property_read_u32(of_node, "cci-master",
 			&o_ctrl->cci_i2c_master);
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 f0c1bca..cd96129 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
@@ -118,6 +118,7 @@
 		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);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index 6e169cf..8eb04ec 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -173,6 +173,9 @@
 	if (io_master_info->master_type == CCI_MASTER) {
 		return cam_sensor_cci_i2c_util(io_master_info->cci_client,
 			MSM_CCI_RELEASE);
+	} else if ((io_master_info->master_type == I2C_MASTER) ||
+			(io_master_info->master_type == SPI_MASTER)) {
+		return 0;
 	} else {
 		CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d",
 			io_master_info->master_type);
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c
index 44d5d48..aecce12 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c
@@ -17,7 +17,7 @@
 #include "cam_packet_util.h"
 #include "cam_debug_util.h"
 
-static int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr,
+int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr,
 	size_t *len)
 {
 	int rc = 0;
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h
index 323a75a..94d2693 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h
@@ -38,6 +38,21 @@
 	uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data);
 
 /**
+ * cam_packet_util_get_cmd_mem_addr()
+ *
+ * @brief                  Get command buffer address
+ *
+ * @handle:                Command buffer memory handle
+ * @buf_addr:              Command buffer cpu mapped address
+ * @len:                   Command buffer length
+ *
+ * @return:                0 for success
+ *                         -EINVAL for Fail
+ */
+int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr,
+	size_t *len);
+
+/**
  * cam_packet_util_validate_packet()
  *
  * @brief                  Validate the packet
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 3d39fa2..3206844 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -37,6 +37,7 @@
 #include "sde_rotator_debug.h"
 
 #define RES_UHD              (3840*2160)
+#define MS_TO_US(t) ((t) * USEC_PER_MSEC)
 
 /* traffic shaping clock ticks = finish_time x 19.2MHz */
 #define TRAFFIC_SHAPE_CLKTICK_14MS   268800
@@ -48,7 +49,7 @@
 #define XIN_WRITEBACK		1
 
 /* wait for at most 2 vsync for lowest refresh rate (24hz) */
-#define KOFF_TIMEOUT		(42 * 32)
+#define KOFF_TIMEOUT		(42 * 8)
 
 /* default stream buffer headroom in lines */
 #define DEFAULT_SBUF_HEADROOM	20
@@ -65,35 +66,54 @@
 	do { \
 		SDEROT_DBG("SDEREG.W:[%s:0x%X] <= 0x%X\n", #off, (off),\
 				(u32)(data));\
-		*p++ = REGDMA_OP_REGWRITE | \
-			((off) & REGDMA_ADDR_OFFSET_MASK); \
-		*p++ = (data); \
+		writel_relaxed_no_log( \
+				(REGDMA_OP_REGWRITE | \
+				 ((off) & REGDMA_ADDR_OFFSET_MASK)), \
+				p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(data, p); \
+		p += sizeof(u32); \
 	} while (0)
 
 #define SDE_REGDMA_MODIFY(p, off, mask, data) \
 	do { \
 		SDEROT_DBG("SDEREG.M:[%s:0x%X] <= 0x%X\n", #off, (off),\
 				(u32)(data));\
-		*p++ = REGDMA_OP_REGMODIFY | \
-			((off) & REGDMA_ADDR_OFFSET_MASK); \
-		*p++ = (mask); \
-		*p++ = (data); \
+		writel_relaxed_no_log( \
+				(REGDMA_OP_REGMODIFY | \
+				 ((off) & REGDMA_ADDR_OFFSET_MASK)), \
+				p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(mask, p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(data, p); \
+		p += sizeof(u32); \
 	} while (0)
 
 #define SDE_REGDMA_BLKWRITE_INC(p, off, len) \
 	do { \
 		SDEROT_DBG("SDEREG.B:[%s:0x%X:0x%X]\n", #off, (off),\
 				(u32)(len));\
-		*p++ = REGDMA_OP_BLKWRITE_INC | \
-			((off) & REGDMA_ADDR_OFFSET_MASK); \
-		*p++ = (len); \
+		writel_relaxed_no_log( \
+				(REGDMA_OP_BLKWRITE_INC | \
+				 ((off) & REGDMA_ADDR_OFFSET_MASK)), \
+				p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(len, p); \
+		p += sizeof(u32); \
 	} while (0)
 
 #define SDE_REGDMA_BLKWRITE_DATA(p, data) \
 	do { \
 		SDEROT_DBG("SDEREG.I:[:] <= 0x%X\n", (u32)(data));\
-		*(p) = (data); \
-		(p)++; \
+		writel_relaxed_no_log(data, p); \
+		p += sizeof(u32); \
+	} while (0)
+
+#define SDE_REGDMA_READ(p, data) \
+	do { \
+		data = readl_relaxed_no_log(p); \
+		p += sizeof(u32); \
 	} while (0)
 
 /* Macro for directly accessing mapped registers */
@@ -647,12 +667,99 @@
 }
 
 /**
+ * sde_hw_rotator_reset - Reset rotator hardware
+ * @rot: pointer to hw rotator
+ * @ctx: pointer to current rotator context during the hw hang
+ */
+static int sde_hw_rotator_reset(struct sde_hw_rotator *rot,
+		struct sde_hw_rotator_context *ctx)
+{
+	struct sde_hw_rotator_context *rctx = NULL;
+	u32 int_mask = (REGDMA_INT_0_MASK | REGDMA_INT_1_MASK |
+			REGDMA_INT_2_MASK);
+	u32 last_ts[ROT_QUEUE_MAX] = {0,};
+	u32 latest_ts;
+	int elapsed_time, t;
+	int i, j;
+	unsigned long flags;
+
+	if (!rot || !ctx) {
+		SDEROT_ERR("NULL rotator context\n");
+		return -EINVAL;
+	}
+
+	if (ctx->q_id >= ROT_QUEUE_MAX) {
+		SDEROT_ERR("context q_id out of range: %d\n", ctx->q_id);
+		return -EINVAL;
+	}
+
+	/* sw reset the hw rotator */
+	SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_SW_RESET_OVERRIDE, 1);
+	usleep_range(MS_TO_US(10), MS_TO_US(20));
+	SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_SW_RESET_OVERRIDE, 0);
+
+	spin_lock_irqsave(&rot->rotisr_lock, flags);
+
+	/* update timestamp register with current context */
+	last_ts[ctx->q_id] = ctx->timestamp;
+	sde_hw_rotator_update_swts(rot, ctx, ctx->timestamp);
+	SDEROT_EVTLOG(ctx->timestamp);
+
+	/*
+	 * Search for any pending rot session, and look for last timestamp
+	 * per hw queue.
+	 */
+	for (i = 0; i < ROT_QUEUE_MAX; i++) {
+		latest_ts = atomic_read(&rot->timestamp[i]);
+		latest_ts &= SDE_REGDMA_SWTS_MASK;
+		elapsed_time = sde_hw_rotator_elapsed_swts(latest_ts,
+			last_ts[i]);
+
+		for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
+			rctx = rot->rotCtx[i][j];
+			if (rctx && rctx != ctx) {
+				rctx->last_regdma_isr_status = int_mask;
+				rctx->last_regdma_timestamp  = rctx->timestamp;
+
+				t = sde_hw_rotator_elapsed_swts(latest_ts,
+							rctx->timestamp);
+				if (t < elapsed_time) {
+					elapsed_time = t;
+					last_ts[i] = rctx->timestamp;
+					sde_hw_rotator_update_swts(rot, rctx,
+							last_ts[i]);
+				}
+
+				SDEROT_DBG("rotctx[%d][%d], ts:%d\n",
+						i, j, rctx->timestamp);
+				SDEROT_EVTLOG(i, j, rctx->timestamp,
+						last_ts[i]);
+			}
+		}
+	}
+
+	/* Finally wakeup all pending rotator context in queue */
+	for (i = 0; i < ROT_QUEUE_MAX; i++) {
+		for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
+			rctx = rot->rotCtx[i][j];
+			if (rctx && rctx != ctx)
+				wake_up_all(&rctx->regdma_waitq);
+		}
+	}
+
+	spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+
+	return 0;
+}
+
+/**
  * sde_hw_rotator_dump_status - Dump hw rotator status on error
  * @rot: Pointer to hw rotator
  */
-static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
+static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot, u32 *ubwcerr)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	u32 reg = 0;
 
 	SDEROT_ERR(
 		"op_mode = %x, int_en = %x, int_status = %x\n",
@@ -681,9 +788,11 @@
 		SDE_ROTREG_READ(rot->mdss_base,
 			REGDMA_CSR_REGDMA_FSM_STATE));
 
+	reg = SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS);
+	if (ubwcerr)
+		*ubwcerr = reg;
 	SDEROT_ERR(
-		"UBWC decode status = %x, UBWC encode status = %x\n",
-		SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS),
+		"UBWC decode status = %x, UBWC encode status = %x\n", reg,
 		SDE_ROTREG_READ(rot->mdss_base, ROT_WB_UBWC_ERROR_STATUS));
 
 	SDEROT_ERR("VBIF XIN HALT status = %x VBIF AXI HALT status = %x\n",
@@ -851,7 +960,7 @@
 static void sde_hw_rotator_setup_timestamp_packet(
 		struct sde_hw_rotator_context *ctx, u32 mask, u32 swts)
 {
-	u32 *wrptr;
+	char __iomem *wrptr;
 
 	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
@@ -908,7 +1017,7 @@
 		struct sde_rot_cdp_params *params)
 {
 	int reg_val;
-	u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+	char __iomem *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	if (!params->enable) {
 		SDE_REGDMA_WRITE(wrptr, params->offset, 0x0);
@@ -942,7 +1051,7 @@
 static void sde_hw_rotator_setup_qos_lut_wr(struct sde_hw_rotator_context *ctx)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-	u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+	char __iomem *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	/* Offline rotation setting */
 	if (!ctx->sbuf_mode) {
@@ -1000,7 +1109,7 @@
 static void sde_hw_rotator_setup_qos_lut_rd(struct sde_hw_rotator_context *ctx)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-	u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+	char __iomem *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	/* Offline rotation setting */
 	if (!ctx->sbuf_mode) {
@@ -1070,7 +1179,7 @@
 	struct sde_mdp_data *data;
 	struct sde_rot_cdp_params cdp_params = {0};
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-	u32 *wrptr;
+	char __iomem *wrptr;
 	u32 opmode = 0;
 	u32 chroma_samp = 0;
 	u32 src_format = 0;
@@ -1291,7 +1400,7 @@
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
 	struct sde_mdp_format_params *fmt;
 	struct sde_rot_cdp_params cdp_params = {0};
-	u32 *wrptr;
+	char __iomem *wrptr;
 	u32 pack = 0;
 	u32 dst_format = 0;
 	u32 no_partial_writes = 0;
@@ -1488,13 +1597,18 @@
 		enum sde_rot_queue_prio queue_id)
 {
 	struct sde_hw_rotator *rot = ctx->rot;
-	u32 *wrptr;
-	u32 *rdptr;
-	u8 *addr;
+	char __iomem *wrptr;
+	char __iomem *mem_rdptr;
+	char __iomem *addr;
 	u32 mask;
+	u32 cmd0, cmd1, cmd2;
 	u32 blksize;
 
-	rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
+	/*
+	 * when regdma is not using, the regdma segment is just a normal
+	 * DRAM, and not an iomem.
+	 */
+	mem_rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
 	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	if (rot->irq_num >= 0) {
@@ -1512,54 +1626,65 @@
 	SDEROT_DBG("BEGIN %d\n", ctx->timestamp);
 	/* Write all command stream to Rotator blocks */
 	/* Rotator will start right away after command stream finish writing */
-	while (rdptr < wrptr) {
-		u32 op = REGDMA_OP_MASK & *rdptr;
+	while (mem_rdptr < wrptr) {
+		u32 op = REGDMA_OP_MASK & readl_relaxed_no_log(mem_rdptr);
 
 		switch (op) {
 		case REGDMA_OP_NOP:
 			SDEROT_DBG("NOP\n");
-			rdptr++;
+			mem_rdptr += sizeof(u32);
 			break;
 		case REGDMA_OP_REGWRITE:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
 			SDEROT_DBG("REGW %6.6x %8.8x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			writel_relaxed(*rdptr++, addr);
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			writel_relaxed(cmd1, addr);
 			break;
 		case REGDMA_OP_REGMODIFY:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
+			SDE_REGDMA_READ(mem_rdptr, cmd2);
 			SDEROT_DBG("REGM %6.6x %8.8x %8.8x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1], rdptr[2]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1, cmd2);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			mask = *rdptr++;
-			writel_relaxed((readl_relaxed(addr) & mask) | *rdptr++,
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			mask = cmd1;
+			writel_relaxed((readl_relaxed(addr) & mask) | cmd2,
 					addr);
 			break;
 		case REGDMA_OP_BLKWRITE_SINGLE:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
 			SDEROT_DBG("BLKWS %6.6x %6.6x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			blksize = *rdptr++;
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			blksize = cmd1;
 			while (blksize--) {
-				SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
-				writel_relaxed(*rdptr++, addr);
+				SDE_REGDMA_READ(mem_rdptr, cmd0);
+				SDEROT_DBG("DATA %8.8x\n", cmd0);
+				writel_relaxed(cmd0, addr);
 			}
 			break;
 		case REGDMA_OP_BLKWRITE_INC:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
 			SDEROT_DBG("BLKWI %6.6x %6.6x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			blksize = *rdptr++;
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			blksize = cmd1;
 			while (blksize--) {
-				SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
-				writel_relaxed(*rdptr++, addr);
+				SDE_REGDMA_READ(mem_rdptr, cmd0);
+				SDEROT_DBG("DATA %8.8x\n", cmd0);
+				writel_relaxed(cmd0, addr);
 				addr += 4;
 			}
 			break;
@@ -1568,7 +1693,7 @@
 			 * Skip data for now for unregonized OP mode
 			 */
 			SDEROT_DBG("UNDEFINED\n");
-			rdptr++;
+			mem_rdptr += sizeof(u32);
 			break;
 		}
 	}
@@ -1586,11 +1711,11 @@
 		enum sde_rot_queue_prio queue_id)
 {
 	struct sde_hw_rotator *rot = ctx->rot;
-	u32 *wrptr;
+	char __iomem *wrptr;
 	u32  regdmaSlot;
 	u32  offset;
-	long length;
-	long ts_length;
+	u32  length;
+	u32  ts_length;
 	u32  enableInt;
 	u32  swts = 0;
 	u32  mask = 0;
@@ -1609,15 +1734,15 @@
 	 * Start REGDMA with command offset and size
 	 */
 	regdmaSlot = sde_hw_rotator_get_regdma_ctxidx(ctx);
-	length = ((long)wrptr - (long)ctx->regdma_base) / 4;
-	offset = (u32)(ctx->regdma_base - (u32 *)(rot->mdss_base +
-				REGDMA_RAM_REGDMA_CMD_RAM));
+	length = (wrptr - ctx->regdma_base) / 4;
+	offset = (ctx->regdma_base - (rot->mdss_base +
+				REGDMA_RAM_REGDMA_CMD_RAM)) / sizeof(u32);
 	enableInt = ((ctx->timestamp & 1) + 1) << 30;
 	trig_sel = ctx->sbuf_mode ? REGDMA_CMD_TRIG_SEL_MDP_FLUSH :
 			REGDMA_CMD_TRIG_SEL_SW_START;
 
 	SDEROT_DBG(
-		"regdma(%d)[%d] <== INT:0x%X|length:%ld|offset:0x%X, ts:%X\n",
+		"regdma(%d)[%d] <== INT:0x%X|length:%d|offset:0x%X, ts:%X\n",
 		queue_id, regdmaSlot, enableInt, length, offset,
 		ctx->timestamp);
 
@@ -1647,6 +1772,7 @@
 		sde_hw_rotator_setup_timestamp_packet(ctx, mask, swts);
 		offset += length;
 		ts_length = sde_hw_rotator_get_regdma_segment(ctx) - wrptr;
+		ts_length /= sizeof(u32);
 		WARN_ON((length + ts_length) > SDE_HW_ROT_REGDMA_SEG_SIZE);
 
 		/* ensure command packet is issue before the submit command */
@@ -1752,6 +1878,7 @@
 	u32 int_id;
 	u32 swts;
 	u32 sts = 0;
+	u32 ubwcerr = 0;
 	unsigned long flags;
 
 	if (rot->irq_num >= 0) {
@@ -1788,8 +1915,26 @@
 			else if (status & REGDMA_INVALID_CMD)
 				SDEROT_ERR("REGDMA invalid command\n");
 
-			sde_hw_rotator_dump_status(rot);
-			status = ROT_ERROR_BIT;
+			sde_hw_rotator_dump_status(rot, &ubwcerr);
+
+			if (ubwcerr) {
+				/*
+				 * Perform recovery for ROT SSPP UBWC decode
+				 * error.
+				 * - SW reset rotator hw block
+				 * - reset TS logic so all pending rotation
+				 *   in hw queue got done signalled
+				 */
+				spin_unlock_irqrestore(&rot->rotisr_lock,
+						flags);
+				if (!sde_hw_rotator_reset(rot, ctx))
+					status = REGDMA_INCOMPLETE_CMD;
+				else
+					status = ROT_ERROR_BIT;
+				spin_lock_irqsave(&rot->rotisr_lock, flags);
+			} else {
+				status = ROT_ERROR_BIT;
+			}
 		} else {
 			if (rc == 1)
 				SDEROT_WARN(
@@ -1815,12 +1960,12 @@
 		if (last_isr & REGDMA_INT_ERR_MASK) {
 			SDEROT_ERR("Rotator error, ts:0x%X/0x%X status:%x\n",
 				ctx->timestamp, swts, last_isr);
-			sde_hw_rotator_dump_status(rot);
+			sde_hw_rotator_dump_status(rot, NULL);
 			status = ROT_ERROR_BIT;
 		} else if (pending) {
 			SDEROT_ERR("Rotator timeout, ts:0x%X/0x%X status:%x\n",
 				ctx->timestamp, swts, last_isr);
-			sde_hw_rotator_dump_status(rot);
+			sde_hw_rotator_dump_status(rot, NULL);
 			status = ROT_ERROR_BIT;
 		} else {
 			status = 0;
@@ -1830,7 +1975,7 @@
 				last_isr);
 	}
 
-	sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+	sts = (status & (ROT_ERROR_BIT | REGDMA_INCOMPLETE_CMD)) ? -ENODEV : 0;
 
 	if (status & ROT_ERROR_BIT)
 		SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
@@ -2323,7 +2468,7 @@
 				if (status & BIT(0)) {
 					SDEROT_ERR("rotator busy 0x%x\n",
 							status);
-					sde_hw_rotator_dump_status(rot);
+					sde_hw_rotator_dump_status(rot, NULL);
 					SDEROT_EVTLOG_TOUT_HANDLER("rot",
 							"vbif_dbg_bus",
 							"panic");
@@ -3435,21 +3580,22 @@
 	/* REGDMA initialization */
 	if (rot->mode == ROT_REGDMA_OFF) {
 		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
-			rot->cmd_wr_ptr[0][i] = &rot->cmd_queue[
-				SDE_HW_ROT_REGDMA_SEG_SIZE * i];
+			rot->cmd_wr_ptr[0][i] = (char __iomem *)(
+					&rot->cmd_queue[
+					SDE_HW_ROT_REGDMA_SEG_SIZE * i]);
 	} else {
 		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
 			rot->cmd_wr_ptr[ROT_QUEUE_HIGH_PRIORITY][i] =
-				(u32 *)(rot->mdss_base +
+				rot->mdss_base +
 					REGDMA_RAM_REGDMA_CMD_RAM +
-					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i);
+					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i;
 
 		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
 			rot->cmd_wr_ptr[ROT_QUEUE_LOW_PRIORITY][i] =
-				(u32 *)(rot->mdss_base +
+				rot->mdss_base +
 					REGDMA_RAM_REGDMA_CMD_RAM +
 					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 *
-					(i + SDE_HW_ROT_REGDMA_TOTAL_CTX));
+					(i + SDE_HW_ROT_REGDMA_TOTAL_CTX);
 	}
 
 	for (i = 0; i < ROT_QUEUE_MAX; i++) {
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
index 4c1316c..8b391ea 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -218,8 +218,8 @@
 	enum   sde_rot_queue_prio q_id;
 	u32    session_id;
 	u32    sequence_id;
-	u32    *regdma_base;
-	u32    *regdma_wrptr;
+	char __iomem *regdma_base;
+	char __iomem *regdma_wrptr;
 	u32    timestamp;
 	struct completion rot_comp;
 	wait_queue_head_t regdma_waitq;
@@ -280,7 +280,7 @@
 	u32    cmd_queue[SDE_HW_ROT_REGDMA_RAM_SIZE];
 
 	/* Cmd Queue Write Ptr */
-	u32   *cmd_wr_ptr[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
+	char __iomem *cmd_wr_ptr[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
 
 	/* Rotator Context */
 	struct sde_hw_rotator_context
@@ -349,7 +349,7 @@
  * @ctx: Rotator Context
  * return: base segment address
  */
-static inline u32 *sde_hw_rotator_get_regdma_segment_base(
+static inline char __iomem *sde_hw_rotator_get_regdma_segment_base(
 		struct sde_hw_rotator_context *ctx)
 {
 	SDEROT_DBG("regdma base @slot[%d]: %p\n",
@@ -365,11 +365,11 @@
  * @ctx: Rotator Context
  * return: segment address
  */
-static inline u32 *sde_hw_rotator_get_regdma_segment(
+static inline char __iomem *sde_hw_rotator_get_regdma_segment(
 		struct sde_hw_rotator_context *ctx)
 {
 	u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
-	u32 *addr = ctx->regdma_wrptr;
+	char __iomem *addr = ctx->regdma_wrptr;
 
 	SDEROT_DBG("regdma slot[%d] ==> %p\n", idx, addr);
 	return addr;
@@ -383,7 +383,7 @@
  */
 static inline void sde_hw_rotator_put_regdma_segment(
 		struct sde_hw_rotator_context *ctx,
-		u32 *wrptr)
+		char __iomem *wrptr)
 {
 	u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
 
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 22c0df1..266c50e 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1317,6 +1317,18 @@
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
 	{
+		struct v4l2_ctrl *hybrid_hp = TRY_GET_CTRL(
+			V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
+		if ((ctrl->val ==
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR
+			|| ctrl->val ==
+			 V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR)
+			&& hybrid_hp->val) {
+			dprintk(VIDC_ERR,
+				"CBR_VFR/VBR_VFR not allowed with Hybrid HP\n");
+			rc = -ENOTSUPP;
+			break;
+		}
 		property_id = HAL_PARAM_VENC_RATE_CONTROL;
 		property_val = ctrl->val;
 		pdata = &property_val;
@@ -1756,10 +1768,26 @@
 		pdata = &enable;
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE:
+	{
+		struct v4l2_ctrl *rate_control;
+
+		rate_control =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL);
+		if ((rate_control->val ==
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR ||
+			rate_control->val ==
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR)
+			&& ctrl->val) {
+			dprintk(VIDC_ERR,
+				"Hybrid HP not allowed with CBR_VFR/VBR_VFR\n");
+			rc = -ENOTSUPP;
+			break;
+		}
 		property_id = HAL_PARAM_VENC_HIER_P_HYBRID_MODE;
 		hyb_hierp.layers = ctrl->val;
 		pdata = &hyb_hierp;
 		break;
+	}
 	case V4L2_CID_VIDC_QBUF_MODE:
 		property_id = HAL_PARAM_SYNC_BASED_INTERRUPT;
 		enable.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index ae2a2c6..68611b6 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);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index c6d31f7..f2f6d58 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -708,9 +708,9 @@
 
 	if (inst->clk_data.buffer_counter < DCVS_FTB_WINDOW ||
 		!msm_vidc_clock_scaling)
-		inst->clk_data.curr_freq = msm_vidc_max_freq(inst->core);
+		inst->clk_data.min_freq = msm_vidc_max_freq(inst->core);
 	else
-		inst->clk_data.curr_freq = freq;
+		inst->clk_data.min_freq = freq;
 
 	msm_vidc_set_clocks(inst->core);
 
@@ -1106,6 +1106,16 @@
 	lp_cycles = inst->session_type == MSM_VIDC_ENCODER ?
 			inst->clk_data.entry->low_power_cycles :
 			inst->clk_data.entry->vpp_cycles;
+	/*
+	 * Incase there is only 1 core enabled, mark it as the core
+	 * with min load. This ensures that this core is selected and
+	 * video session is set to run on the enabled core.
+	 */
+	if (inst->capability.max_video_cores.max <= VIDC_CORE_ID_1) {
+		min_core_id = min_lp_core_id = VIDC_CORE_ID_1;
+		min_load = core0_load;
+		min_lp_load = core0_lp_load;
+	}
 
 	current_inst_load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS) *
 		inst->clk_data.entry->vpp_cycles;
@@ -1129,7 +1139,8 @@
 		V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
 
 	/* Try for preferred core based on settings. */
-	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode) {
+	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode &&
+		inst->capability.max_video_cores.max >= VIDC_CORE_ID_3) {
 		if (current_inst_load / 2 + core0_load <= max_freq &&
 			current_inst_load / 2 + core1_load <= max_freq) {
 			if (inst->clk_data.work_mode == VIDC_WORK_MODE_2) {
@@ -1140,7 +1151,8 @@
 		}
 	}
 
-	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode) {
+	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode &&
+		inst->capability.max_video_cores.max >= VIDC_CORE_ID_3) {
 		if (current_inst_lp_load / 2 +
 				core0_lp_load <= max_freq &&
 			current_inst_lp_load / 2 +
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 2beb90c..f8c5798 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1686,6 +1686,11 @@
 		event_notify->crop_data.height;
 	inst->prop.crop_info.width =
 		event_notify->crop_data.width;
+	/* HW returns progressive_only flag in pic_struct. */
+	inst->pic_struct =
+		event_notify->pic_struct ?
+		MSM_VIDC_PIC_STRUCT_PROGRESSIVE :
+		MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED;
 
 	ptr = (u32 *)seq_changed_event.u.data;
 	ptr[0] = event_notify->height;
@@ -3384,7 +3389,7 @@
 	enum hal_buffer buffer_type)
 {
 	int rc = 0;
-	struct internal_buf *binfo;
+	struct internal_buf *binfo = NULL;
 	u32 smem_flags = 0, buffer_size;
 	struct hal_buffer_requirements *output_buf, *extradata_buf;
 	int i;
@@ -3493,10 +3498,10 @@
 	}
 	return rc;
 fail_set_buffers:
-	kfree(binfo);
-fail_kzalloc:
 	msm_comm_smem_free(inst, &binfo->smem);
 err_no_mem:
+	kfree(binfo);
+fail_kzalloc:
 	return rc;
 }
 
@@ -6329,10 +6334,26 @@
 	}
 
 	mutex_lock(&inst->registeredbufs.lock);
-	list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
-		if (msm_comm_compare_dma_planes(inst, mbuf, dma_planes)) {
-			found = true;
-			break;
+	if (inst->session_type == MSM_VIDC_DECODER) {
+		list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
+			if (msm_comm_compare_dma_planes(inst, mbuf,
+					dma_planes)) {
+				found = true;
+				break;
+			}
+		}
+	} else {
+		/*
+		 * for encoder, client may queue the same buffer with different
+		 * fd before driver returned old buffer to the client. This
+		 * buffer should be treated as new buffer. Search the list with
+		 * fd so that it will be treated as new msm_vidc_buffer.
+		 */
+		list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
+			if (msm_comm_compare_vb2_planes(inst, mbuf, vb2)) {
+				found = true;
+				break;
+			}
 		}
 	}
 
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
index a82b598..56524ccd4 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
@@ -289,6 +289,7 @@
 		.compatible = "qcom,sdm670-vidc",
 		.data = &sdm670_data,
 	},
+	{},
 };
 
 MODULE_DEVICE_TABLE(of, msm_vidc_dt_match);
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 5d2ed29..e260886 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1693,6 +1693,12 @@
 
 	dev->bus_vote.data =
 		kzalloc(sizeof(struct vidc_bus_vote_data), GFP_KERNEL);
+	if (!dev->bus_vote.data) {
+		dprintk(VIDC_ERR, "Bus vote data memory is not allocated\n");
+		rc = -ENOMEM;
+		goto err_no_mem;
+	}
+
 	dev->bus_vote.data_count = 1;
 	dev->bus_vote.data->power_mode = VIDC_POWER_TURBO;
 
@@ -1769,6 +1775,7 @@
 	__set_state(dev, VENUS_STATE_DEINIT);
 	__unload_fw(dev);
 err_load_fw:
+err_no_mem:
 	dprintk(VIDC_ERR, "Core init failed\n");
 	mutex_unlock(&dev->lock);
 	return rc;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index af409aa..eb14f0f 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3908,6 +3908,7 @@
 	struct mmc_host *host = card->host;
 	struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
 	u8 part_config = card->ext_csd.part_config;
+	int ret = 0, err = 0;
 
 	if ((main_md->part_curr == md->part_type) &&
 	    (card->part_curr == md->part_type))
@@ -3917,40 +3918,70 @@
 		 card->ext_csd.cmdq_support &&
 		 (md->flags & MMC_BLK_CMD_QUEUE)));
 
-	if (!test_bit(CMDQ_STATE_HALT, &ctx->curr_state))
-		WARN_ON(mmc_cmdq_halt(host, true));
+	if (!test_bit(CMDQ_STATE_HALT, &ctx->curr_state)) {
+		ret = mmc_cmdq_halt(host, true);
+		if (ret) {
+			pr_err("%s: %s: halt: failed: %d\n",
+				mmc_hostname(host), __func__,  ret);
+			goto out;
+		}
+	}
 
 	/* disable CQ mode in card */
 	if (mmc_card_cmdq(card)) {
-		WARN_ON(mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+		ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 				 EXT_CSD_CMDQ, 0,
-				  card->ext_csd.generic_cmd6_time));
+				 card->ext_csd.generic_cmd6_time);
+		if (ret) {
+			pr_err("%s: %s: cmdq mode disable failed %d\n",
+				mmc_hostname(host), __func__, ret);
+			goto cmdq_unhalt;
+		}
 		mmc_card_clr_cmdq(card);
 	}
 
 	part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
 	part_config |= md->part_type;
 
-	WARN_ON(mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+	ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 			 EXT_CSD_PART_CONFIG, part_config,
-			  card->ext_csd.part_time));
+			 card->ext_csd.part_time);
+	if (ret) {
+		pr_err("%s: %s: mmc_switch failure, %d -> %d , err = %d\n",
+			mmc_hostname(host), __func__, main_md->part_curr,
+			md->part_type, ret);
+		goto cmdq_switch;
+	}
 
 	card->ext_csd.part_config = part_config;
 	card->part_curr = md->part_type;
 
 	main_md->part_curr = md->part_type;
 
-	WARN_ON(mmc_blk_cmdq_switch(card, md, true));
-	WARN_ON(mmc_cmdq_halt(host, false));
-
-	return 0;
+cmdq_switch:
+	err = mmc_blk_cmdq_switch(card, md, true);
+	if (err) {
+		pr_err("%s: %s: mmc_blk_cmdq_switch failed: %d\n",
+			mmc_hostname(host), __func__,  err);
+		ret = err;
+	}
+cmdq_unhalt:
+	err = mmc_cmdq_halt(host, false);
+	if (err) {
+		pr_err("%s: %s: unhalt: failed: %d\n",
+			mmc_hostname(host), __func__,  err);
+		ret = err;
+	}
+out:
+	return ret;
 }
 
 static int mmc_blk_cmdq_issue_rq(struct mmc_queue *mq, struct request *req)
 {
-	int ret;
+	int ret, err = 0;
 	struct mmc_blk_data *md = mq->data;
 	struct mmc_card *card = md->queue.card;
+	struct mmc_host *host = card->host;
 
 	mmc_get_card(card);
 
@@ -3971,9 +4002,20 @@
 
 	ret = mmc_blk_cmdq_part_switch(card, md);
 	if (ret) {
-		pr_err("%s: %s: partition switch failed %d\n",
+		pr_err("%s: %s: partition switch failed %d, resetting cmdq\n",
 				md->disk->disk_name, __func__, ret);
-		goto out;
+
+		mmc_blk_cmdq_reset(host, false);
+		err = mmc_blk_cmdq_part_switch(card, md);
+		if (!err) {
+			pr_err("%s: %s: partition switch success err = %d\n",
+				md->disk->disk_name, __func__, err);
+		} else {
+			pr_err("%s: %s: partition switch failed err = %d\n",
+				md->disk->disk_name, __func__, err);
+			ret = err;
+			goto out;
+		}
 	}
 
 	if (req) {
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 36295f5..bc89472 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2863,10 +2863,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/net/wireless/ath/wil6210/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c
index f94c894..17939ce 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.c
+++ b/drivers/net/wireless/ath/wil6210/ftm.c
@@ -38,6 +38,9 @@
 /* initial token to use on non-secure FTM measurement */
 #define WIL_TOF_FTM_DEFAULT_INITIAL_TOKEN	2
 
+/* maximum AOA burst period, limited by FW */
+#define WIL_AOA_MAX_BURST_PERIOD	255
+
 #define WIL_TOF_FTM_MAX_LCI_LENGTH		(240)
 #define WIL_TOF_FTM_MAX_LCR_LENGTH		(240)
 
@@ -62,6 +65,7 @@
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS] = { .type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS] = { .type = NLA_NESTED },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID] = { .type = NLA_U8 },
+	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD] = { .type = NLA_U16 },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ] = { .type = NLA_U32 },
 };
 
@@ -315,8 +319,8 @@
 	struct wmi_tof_session_start_cmd *cmd;
 
 	mutex_lock(&wil->ftm.lock);
-	if (wil->ftm.session_started) {
-		wil_err(wil, "FTM session already running\n");
+	if (wil->ftm.session_started || wil->ftm.aoa_started) {
+		wil_err(wil, "FTM or AOA session already running\n");
 		rc = -EAGAIN;
 		goto out;
 	}
@@ -360,6 +364,7 @@
 	}
 
 	cmd->session_id = cpu_to_le32(WIL_FTM_FW_SESSION_ID);
+	cmd->aoa_type = request->aoa_type;
 	cmd->num_of_dest = cpu_to_le16(request->n_peers);
 	for (i = 0; i < request->n_peers; i++) {
 		ether_addr_copy(cmd->ftm_dest_info[i].dst_mac,
@@ -402,6 +407,8 @@
 			request->peers[i].params.burst_duration;
 		cmd->ftm_dest_info[i].burst_period =
 			cpu_to_le16(request->peers[i].params.burst_period);
+		cmd->ftm_dest_info[i].num_burst_per_aoa_meas =
+			request->peers[i].aoa_burst_period;
 	}
 
 	rc = wmi_send(wil, WMI_TOF_SESSION_START_CMDID, cmd, cmd_len);
@@ -487,8 +494,8 @@
 
 	mutex_lock(&wil->ftm.lock);
 
-	if (wil->ftm.aoa_started) {
-		wil_err(wil, "AOA measurement already running\n");
+	if (wil->ftm.aoa_started || wil->ftm.session_started) {
+		wil_err(wil, "AOA or FTM measurement already running\n");
 		rc = -EAGAIN;
 		goto out;
 	}
@@ -529,8 +536,8 @@
 
 	mutex_lock(&wil->ftm.lock);
 
-	if (!wil->ftm.aoa_started) {
-		wil_info(wil, "AOA not started, not sending result\n");
+	if (!wil->ftm.aoa_started && !wil->ftm.session_started) {
+		wil_info(wil, "AOA/FTM not started, not sending result\n");
 		goto out;
 	}
 
@@ -683,6 +690,10 @@
 	int data_len = len - offsetof(struct wmi_aoa_meas_event, meas_data);
 	struct wil_aoa_meas_result *res;
 
+	if (data_len < 0) {
+		wil_err(wil, "AOA event too short (%d)\n", len);
+		return;
+	}
 	data_len = min_t(int, le16_to_cpu(evt->length), data_len);
 
 	res = kmalloc(sizeof(*res) + data_len, GFP_KERNEL);
@@ -754,6 +765,7 @@
 	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1];
 	struct nlattr *peer;
 	int rc, n_peers = 0, index = 0, tmp;
+	u32 aoa_type = 0;
 
 	if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
 		return -ENOTSUPP;
@@ -775,6 +787,14 @@
 		return -EINVAL;
 	}
 
+	if (tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]) {
+		aoa_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]);
+		if (aoa_type >= QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX) {
+			wil_err(wil, "invalid AOA type: %d\n", aoa_type);
+			return -EINVAL;
+		}
+	}
+
 	nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
 			    tmp)
 		n_peers++;
@@ -798,6 +818,7 @@
 
 	request->session_cookie =
 		nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE]);
+	request->aoa_type = aoa_type;
 	request->n_peers = n_peers;
 	nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
 			    tmp) {
@@ -826,6 +847,18 @@
 		if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID])
 			request->peers[index].secure_token_id = nla_get_u8(
 			   tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID]);
+		if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]) {
+			request->peers[index].aoa_burst_period = nla_get_u16(
+			  tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]);
+			if (request->peers[index].aoa_burst_period >
+			    WIL_AOA_MAX_BURST_PERIOD) {
+				wil_err(wil, "Invalid AOA burst period at index: %d\n",
+					index);
+				rc = -EINVAL;
+				goto out;
+			}
+		}
+
 		rc = wil_ftm_parse_meas_params(
 			wil,
 			tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS],
diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h
index 8efa292..21923c2 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.h
+++ b/drivers/net/wireless/ath/wil6210/ftm.h
@@ -437,12 +437,14 @@
 	u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_meas_flags */
 	struct wil_ftm_meas_params params;
 	u8 secure_token_id;
+	u16 aoa_burst_period; /* 0 if no AOA, >0 every <value> bursts */
 };
 
 /* session request, passed to wil_ftm_cfg80211_start_session */
 struct wil_ftm_session_request {
 	u64 session_cookie;
 	u32 n_peers;
+	u32 aoa_type; /* enum qca_wlan_vendor_attr_aoa_type */
 	/* keep last, variable size according to n_peers */
 	struct wil_ftm_meas_peer_info peers[0];
 };
diff --git a/drivers/net/wireless/ath/wil6210/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/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/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index c9f5c11..d92db11 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -16,6 +16,6 @@
 obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o
 obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o
 obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
-obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o
+obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o pinctrl-sdm845-v2.o
 obj-$(CONFIG_PINCTRL_SDM670) += pinctrl-sdm670.o
 obj-$(CONFIG_PINCTRL_SDXPOORWILLS)	+= pinctrl-sdxpoorwills.o
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 166935ec..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>
@@ -39,7 +40,6 @@
 
 #define MAX_NR_GPIO 300
 #define PS_HOLD_OFFSET 0x820
-#define STATUS_OFFSET 0x10
 
 /**
  * struct msm_pinctrl - state for a pinctrl-msm device
@@ -72,35 +72,6 @@
 	void __iomem *regs;
 };
 
-static u32 msm_pinctrl_find_base(const struct msm_pinctrl *pctrl, u32 gpio_id)
-{
-	int i;
-	u32 val;
-	const struct msm_pinctrl_soc_data *soc_data = pctrl->soc;
-
-	if (gpio_id >= soc_data->ngpios || !soc_data->pin_base)
-		return 0;
-
-	if (soc_data->pin_base[gpio_id])
-		return soc_data->pin_base[gpio_id];
-
-	for (i = 0; i < soc_data->n_tile_offsets; i++) {
-		val = readl_relaxed(pctrl->regs +
-			soc_data->tile_offsets[i] + STATUS_OFFSET
-			+ soc_data->reg_size * gpio_id);
-		if (val) {
-			soc_data->pin_base[gpio_id] = soc_data->tile_offsets[i];
-			return soc_data->tile_offsets[i];
-		}
-	}
-
-	/* In the case that the soc_data does not support dynamic base
-	 * detection, we return 0 here.
-	 */
-	WARN_ONCE(1, "%s:Dynamic base detection is not supported\n", __func__);
-	return 0;
-}
-
 static int msm_get_groups_count(struct pinctrl_dev *pctldev)
 {
 	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
@@ -170,11 +141,10 @@
 	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, mask, base;
+	u32 val, mask;
 	int i;
 
 	g = &pctrl->soc->groups[group];
-	base = msm_pinctrl_find_base(pctrl, group);
 	mask = GENMASK(g->mux_bit + order_base_2(g->nfuncs) - 1, g->mux_bit);
 
 	for (i = 0; i < g->nfuncs; i++) {
@@ -187,10 +157,10 @@
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	val &= ~mask;
 	val |= i << g->mux_bit;
-	writel(val, pctrl->regs + base + g->ctl_reg);
+	writel(val, pctrl->regs + g->ctl_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -255,16 +225,15 @@
 	unsigned arg;
 	unsigned bit;
 	int ret;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[group];
-	base = msm_pinctrl_find_base(pctrl, group);
 
 	ret = msm_config_reg(pctrl, g, param, &mask, &bit);
 	if (ret < 0)
 		return ret;
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	arg = (val >> bit) & mask;
 
 	/* Convert register value to pinconf value */
@@ -289,7 +258,7 @@
 		if (!arg)
 			return -EINVAL;
 
-		val = readl(pctrl->regs + base + g->io_reg);
+		val = readl(pctrl->regs + g->io_reg);
 		arg = !!(val & BIT(g->in_bit));
 		break;
 	case PIN_CONFIG_INPUT_ENABLE:
@@ -320,12 +289,11 @@
 	unsigned arg;
 	unsigned bit;
 	int ret;
-	u32 val, base;
+	u32 val;
 	int i;
 
 	g = &pctrl->soc->groups[group];
 
-	base = msm_pinctrl_find_base(pctrl, group);
 	for (i = 0; i < num_configs; i++) {
 		param = pinconf_to_config_param(configs[i]);
 		arg = pinconf_to_config_argument(configs[i]);
@@ -358,12 +326,12 @@
 		case PIN_CONFIG_OUTPUT:
 			/* set output value */
 			spin_lock_irqsave(&pctrl->lock, flags);
-			val = readl(pctrl->regs + base + g->io_reg);
+			val = readl(pctrl->regs + g->io_reg);
 			if (arg)
 				val |= BIT(g->out_bit);
 			else
 				val &= ~BIT(g->out_bit);
-			writel(val, pctrl->regs + base + g->io_reg);
+			writel(val, pctrl->regs + g->io_reg);
 			spin_unlock_irqrestore(&pctrl->lock, flags);
 
 			/* enable output */
@@ -386,10 +354,10 @@
 		}
 
 		spin_lock_irqsave(&pctrl->lock, flags);
-		val = readl(pctrl->regs + base + g->ctl_reg);
+		val = readl(pctrl->regs + g->ctl_reg);
 		val &= ~(mask << bit);
 		val |= arg << bit;
-		writel(val, pctrl->regs + base + g->ctl_reg);
+		writel(val, pctrl->regs + g->ctl_reg);
 		spin_unlock_irqrestore(&pctrl->lock, flags);
 	}
 
@@ -414,16 +382,15 @@
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	val &= ~BIT(g->oe_bit);
-	writel(val, pctrl->regs + base + g->ctl_reg);
+	writel(val, pctrl->regs + g->ctl_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -435,23 +402,22 @@
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->io_reg);
+	val = readl(pctrl->regs + g->io_reg);
 	if (value)
 		val |= BIT(g->out_bit);
 	else
 		val &= ~BIT(g->out_bit);
-	writel(val, pctrl->regs + base + g->io_reg);
+	writel(val, pctrl->regs + g->io_reg);
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	val |= BIT(g->oe_bit);
-	writel(val, pctrl->regs + base + g->ctl_reg);
+	writel(val, pctrl->regs + g->ctl_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -462,12 +428,11 @@
 {
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
-	val = readl(pctrl->regs + base + g->io_reg);
+	val = readl(pctrl->regs + g->io_reg);
 	return !!(val & BIT(g->in_bit));
 }
 
@@ -476,19 +441,18 @@
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->io_reg);
+	val = readl(pctrl->regs + g->io_reg);
 	if (value)
 		val |= BIT(g->out_bit);
 	else
 		val &= ~BIT(g->out_bit);
-	writel(val, pctrl->regs + base + g->io_reg);
+	writel(val, pctrl->regs + g->io_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 }
@@ -508,7 +472,7 @@
 	int is_out;
 	int drive;
 	int pull;
-	u32 ctl_reg, base;
+	u32 ctl_reg;
 
 	static const char * const pulls[] = {
 		"no pull",
@@ -518,9 +482,7 @@
 	};
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
-
-	ctl_reg = readl(pctrl->regs + base + g->ctl_reg);
+	ctl_reg = readl(pctrl->regs + g->ctl_reg);
 
 	is_out = !!(ctl_reg & BIT(g->oe_bit));
 	func = (ctl_reg >> g->mux_bit) & 7;
@@ -579,21 +541,21 @@
  */
 static void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl,
 					  const struct msm_pingroup *g,
-					  struct irq_data *d, u32 base)
+					  struct irq_data *d)
 {
 	int loop_limit = 100;
 	unsigned val, val2, intstat;
 	unsigned pol;
 
 	do {
-		val = readl(pctrl->regs + base + g->io_reg) & BIT(g->in_bit);
+		val = readl(pctrl->regs + g->io_reg) & BIT(g->in_bit);
 
-		pol = readl(pctrl->regs + base + g->intr_cfg_reg);
+		pol = readl(pctrl->regs + g->intr_cfg_reg);
 		pol ^= BIT(g->intr_polarity_bit);
-		writel(pol, pctrl->regs + base + g->intr_cfg_reg);
+		writel(pol, pctrl->regs + g->intr_cfg_reg);
 
-		val2 = readl(pctrl->regs + base + g->io_reg) & BIT(g->in_bit);
-		intstat = readl(pctrl->regs + base + g->intr_status_reg);
+		val2 = readl(pctrl->regs + g->io_reg) & BIT(g->in_bit);
+		intstat = readl(pctrl->regs + g->intr_status_reg);
 		if (intstat || (val == val2))
 			return;
 	} while (loop_limit-- > 0);
@@ -607,16 +569,15 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->intr_cfg_reg);
+	val = readl(pctrl->regs + g->intr_cfg_reg);
 	val &= ~BIT(g->intr_enable_bit);
-	writel(val, pctrl->regs + base + g->intr_cfg_reg);
+	writel(val, pctrl->regs + g->intr_cfg_reg);
 
 	clear_bit(d->hwirq, pctrl->enabled_irqs);
 
@@ -629,16 +590,15 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->intr_cfg_reg);
+	val = readl(pctrl->regs + g->intr_cfg_reg);
 	val |= BIT(g->intr_enable_bit);
-	writel(val, pctrl->regs + base + g->intr_cfg_reg);
+	writel(val, pctrl->regs + g->intr_cfg_reg);
 
 	set_bit(d->hwirq, pctrl->enabled_irqs);
 
@@ -651,22 +611,21 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->intr_status_reg);
+	val = readl(pctrl->regs + g->intr_status_reg);
 	if (g->intr_ack_high)
 		val |= BIT(g->intr_status_bit);
 	else
 		val &= ~BIT(g->intr_status_bit);
-	writel(val, pctrl->regs + base + g->intr_status_reg);
+	writel(val, pctrl->regs + g->intr_status_reg);
 
 	if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
-		msm_gpio_update_dual_edge_pos(pctrl, g, d, base);
+		msm_gpio_update_dual_edge_pos(pctrl, g, d);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 }
@@ -677,10 +636,10 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
+
 	spin_lock_irqsave(&pctrl->lock, flags);
 
 	/*
@@ -692,17 +651,17 @@
 		clear_bit(d->hwirq, pctrl->dual_edge_irqs);
 
 	/* Route interrupts to application cpu */
-	val = readl(pctrl->regs + base + g->intr_target_reg);
+	val = readl(pctrl->regs + g->intr_target_reg);
 	val &= ~(7 << g->intr_target_bit);
 	val |= g->intr_target_kpss_val << g->intr_target_bit;
-	writel(val, pctrl->regs + base + g->intr_target_reg);
+	writel(val, pctrl->regs + g->intr_target_reg);
 
 	/* Update configuration for gpio.
 	 * RAW_STATUS_EN is left on for all gpio irqs. Due to the
 	 * internal circuitry of TLMM, toggling the RAW_STATUS
 	 * could cause the INTR_STATUS to be set for EDGE interrupts.
 	 */
-	val = readl(pctrl->regs + base + g->intr_cfg_reg);
+	val = readl(pctrl->regs + g->intr_cfg_reg);
 	val |= BIT(g->intr_raw_status_bit);
 	if (g->intr_detection_width == 2) {
 		val &= ~(3 << g->intr_detection_bit);
@@ -750,10 +709,10 @@
 	} else {
 		BUG();
 	}
-	writel(val, pctrl->regs + base + g->intr_cfg_reg);
+	writel(val, pctrl->regs + g->intr_cfg_reg);
 
 	if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
-		msm_gpio_update_dual_edge_pos(pctrl, g, d, base);
+		msm_gpio_update_dual_edge_pos(pctrl, g, d);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -789,11 +748,46 @@
 	.irq_set_wake   = msm_gpio_irq_set_wake,
 };
 
+static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq)
+{
+	struct irq_desc *desc = irq_data_to_desc(d);
+	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+	int i;
+
+	if (!parent_data)
+		return false;
+
+	for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
+		const struct msm_dir_conn *dir_conn = &pctrl->soc->dir_conn[i];
+
+		if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32)
+				!= parent_data->hwirq) {
+			*dir_conn_irq = dir_conn->hwirq + 32;
+			return true;
+		}
+	}
+	return false;
+}
+
 static void msm_dirconn_irq_mask(struct irq_data *d)
 {
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	irq_hw_number_t dir_conn_irq = 0;
 
+	if (!parent_data)
+		return;
+
+	if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+		struct irq_data *dir_conn_data =
+			irq_get_irq_data(irq_find_mapping(parent_data->domain,
+						dir_conn_irq));
+
+		if (dir_conn_data && dir_conn_data->chip->irq_mask)
+			dir_conn_data->chip->irq_mask(dir_conn_data);
+	}
 	if (parent_data->chip->irq_mask)
 		parent_data->chip->irq_mask(parent_data);
 }
@@ -802,7 +796,19 @@
 {
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	irq_hw_number_t dir_conn_irq = 0;
 
+	if (!parent_data)
+		return;
+
+	if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+		struct irq_data *dir_conn_data =
+			irq_get_irq_data(irq_find_mapping(parent_data->domain,
+						dir_conn_irq));
+
+		if (dir_conn_data && dir_conn_data->chip->irq_unmask)
+			dir_conn_data->chip->irq_unmask(dir_conn_data);
+	}
 	if (parent_data->chip->irq_unmask)
 		parent_data->chip->irq_unmask(parent_data);
 }
@@ -831,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);
@@ -849,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);
@@ -882,7 +1039,7 @@
 	struct irq_chip *chip = irq_desc_get_chip(desc);
 	int irq_pin;
 	int handled = 0;
-	u32 val, base;
+	u32 val;
 	int i;
 
 	chained_irq_enter(chip, desc);
@@ -893,8 +1050,7 @@
 	 */
 	for_each_set_bit(i, pctrl->enabled_irqs, pctrl->chip.ngpio) {
 		g = &pctrl->soc->groups[i];
-		base = msm_pinctrl_find_base(pctrl, i);
-		val = readl(pctrl->regs + base + g->intr_status_reg);
+		val = readl(pctrl->regs + g->intr_status_reg);
 		if (val & BIT(g->intr_status_bit)) {
 			irq_pin = irq_find_mapping(gc->irqdomain, i);
 			generic_handle_irq(irq_pin);
@@ -947,14 +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 7ffc2e3..1c6df2f 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -43,6 +43,8 @@
  * @intr_status_reg:      Offset of the register holding the status bits for this group.
  * @intr_target_reg:      Offset of the register specifying routing of the interrupts
  *                        from this group.
+ * @dir_conn_reg:         Offset of the register specifying direct connect
+ *                        setup of this group.
  * @mux_bit:              Offset in @ctl_reg for the pinmux function selection.
  * @pull_bit:             Offset in @ctl_reg for the bias configuration.
  * @drv_bit:              Offset in @ctl_reg for the drive strength configuration.
@@ -75,6 +77,7 @@
 	u32 intr_cfg_reg;
 	u32 intr_status_reg;
 	u32 intr_target_reg;
+	u32 dir_conn_reg;
 
 	unsigned mux_bit:5;
 
@@ -95,6 +98,7 @@
 	unsigned intr_polarity_bit:5;
 	unsigned intr_detection_bit:5;
 	unsigned intr_detection_width:5;
+	unsigned dir_conn_en_bit:8;
 }
 
 /**
@@ -118,6 +122,8 @@
  * @ngpio:      The number of pingroups the driver should expose as GPIOs.
  * @dir_conn:   An array describing all the pins directly connected to GIC.
  * @ndirconns:  The number of pins directly connected to GIC
+ * @dir_conn_offsets:   Direct connect register offsets for each tile.
+ * @dir_conn_irq_base:  Direct connect interrupt base register for kpss.
  */
 struct msm_pinctrl_soc_data {
 	const struct pinctrl_pin_desc *pins;
@@ -129,10 +135,7 @@
 	unsigned ngpios;
 	const struct msm_dir_conn *dir_conn;
 	unsigned int n_dir_conns;
-	const u32 *tile_offsets;
-	unsigned int n_tile_offsets;
-	u32 *pin_base;
-	unsigned int reg_size;
+	unsigned int dir_conn_irq_base;
 };
 
 int msm_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670.c b/drivers/pinctrl/qcom/pinctrl-sdm670.c
index b454cc442..8749653 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm670.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c
@@ -25,8 +25,13 @@
 		.ngroups = ARRAY_SIZE(fname##_groups),	\
 	}
 
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
+#define WEST	0x00100000
+
+#define DUMMY	0x0
 #define REG_SIZE 0x1000
-#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
 	{						\
 		.name = "gpio" #id,			\
 		.pins = gpio##id##_pins,		\
@@ -44,11 +49,11 @@
 			msm_mux_##f9			\
 		},					\
 		.nfuncs = 10,				\
-		.ctl_reg = REG_SIZE * id,		\
-		.io_reg = 0x4 + REG_SIZE * id,		\
-		.intr_cfg_reg = 0x8 + REG_SIZE * id,	\
-		.intr_status_reg = 0xc + REG_SIZE * id,	\
-		.intr_target_reg = 0x8 + REG_SIZE * id,	\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
 		.mux_bit = 2,			\
 		.pull_bit = 0,			\
 		.drv_bit = 6,			\
@@ -114,10 +119,6 @@
 		.intr_detection_bit = -1,		\
 		.intr_detection_width = -1,		\
 	}
-
-static const u32 sdm670_tile_offsets[] = {0x100000, 0x500000, 0x900000};
-static u32 sdm670_pin_base[150];
-
 static const struct pinctrl_pin_desc sdm670_pins[] = {
 	PINCTRL_PIN(0, "GPIO_0"),
 	PINCTRL_PIN(1, "GPIO_1"),
@@ -1332,259 +1333,258 @@
  * Clients would not be able to request these dummy pin groups.
  */
 static const struct msm_pingroup sdm670_groups[] = {
-	[0] = PINGROUP(0, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[1] = PINGROUP(1, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[2] = PINGROUP(2, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[3] = PINGROUP(3, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[4] = PINGROUP(4, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[5] = PINGROUP(5, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[6] = PINGROUP(6, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA),
-	[7] = PINGROUP(7, qup9, ddr_bist, NA, atest_tsens2,
+	[0] = PINGROUP(0, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[1] = PINGROUP(1, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[2] = PINGROUP(2, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[3] = PINGROUP(3, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA),
+	[7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2,
 		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA),
-	[8] = PINGROUP(8, qup_l4, GP_PDM1, ddr_bist, NA, NA, NA, NA, NA,
+	[8] = PINGROUP(8, WEST, qup_l4, GP_PDM1, ddr_bist, NA, NA, NA, NA, NA,
 		       NA),
-	[9] = PINGROUP(9, qup_l5, ddr_bist, NA, NA, NA, NA, NA, NA, NA),
-	[10] = PINGROUP(10, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+	[9] = PINGROUP(9, WEST, qup_l5, ddr_bist, NA, NA, NA, NA, NA, NA, NA),
+	[10] = PINGROUP(10, NORTH, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
 			atest_usb11, ddr_pxi2, NA, NA, NA),
-	[11] = PINGROUP(11, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+	[11] = PINGROUP(11, NORTH, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
 			atest_usb10, ddr_pxi2, NA, NA, NA),
-	[12] = PINGROUP(12, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
+	[12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
 			NA, NA, NA, NA),
-	[13] = PINGROUP(13, cam_mclk, pll_bypassnl, qdss_gpio0, ddr_pxi3,
+	[13] = PINGROUP(13, WEST, cam_mclk, pll_bypassnl, qdss_gpio0, ddr_pxi3,
 			NA, NA, NA, NA, NA),
-	[14] = PINGROUP(14, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
+	[14] = PINGROUP(14, WEST, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
 			NA, NA, NA),
-	[15] = PINGROUP(15, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA, NA,
+	[15] = PINGROUP(15, WEST, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA, NA,
 			NA),
-	[16] = PINGROUP(16, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA, NA,
+	[16] = PINGROUP(16, WEST, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA, NA,
 			NA),
-	[17] = PINGROUP(17, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
+	[17] = PINGROUP(17, WEST, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
 			NA, NA),
-	[18] = PINGROUP(18, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
+	[18] = PINGROUP(18, WEST, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
 			NA, NA),
-	[19] = PINGROUP(19, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
+	[19] = PINGROUP(19, WEST, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
 			NA, NA),
-	[20] = PINGROUP(20, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
+	[20] = PINGROUP(20, WEST, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
 			NA, NA),
-	[21] = PINGROUP(21, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
+	[21] = PINGROUP(21, WEST, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
 			NA, NA, NA),
-	[22] = PINGROUP(22, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
+	[22] = PINGROUP(22, WEST, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
 			NA, NA, NA),
-	[23] = PINGROUP(23, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
+	[23] = PINGROUP(23, WEST, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
 			NA, NA),
-	[24] = PINGROUP(24, cci_timer3, cci_async, qdss_gpio10, NA, NA,
+	[24] = PINGROUP(24, WEST, cci_timer3, cci_async, qdss_gpio10, NA, NA,
 			NA, NA, NA, NA),
-	[25] = PINGROUP(25, cci_timer4, cci_async, qdss_gpio11, NA, NA,
+	[25] = PINGROUP(25, WEST, cci_timer4, cci_async, qdss_gpio11, NA, NA,
 			NA, NA, NA, NA),
-	[26] = PINGROUP(26, cci_async, qdss_gpio12, JITTER_BIST, NA, NA,
+	[26] = PINGROUP(26, WEST, cci_async, qdss_gpio12, JITTER_BIST, NA, NA,
 			NA, NA, NA, NA),
-	[27] = PINGROUP(27, qup2, qdss_gpio13, PLL_BIST, NA, NA, NA, NA,
+	[27] = PINGROUP(27, WEST, qup2, qdss_gpio13, PLL_BIST, NA, NA, NA, NA,
 			NA, NA),
-	[28] = PINGROUP(28, qup2, qdss_gpio14, AGERA_PLL, NA, NA, NA, NA,
+	[28] = PINGROUP(28, WEST, qup2, qdss_gpio14, AGERA_PLL, NA, NA, NA, NA,
 			NA, NA),
-	[29] = PINGROUP(29, qup2, NA, phase_flag1, qdss_gpio15,
+	[29] = PINGROUP(29, WEST, qup2, NA, phase_flag1, qdss_gpio15,
 			atest_tsens, NA, NA, NA, NA),
-	[30] = PINGROUP(30, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
+	[30] = PINGROUP(30, WEST, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
 			NA, NA),
-	[31] = PINGROUP(31, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[32] = PINGROUP(32, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[33] = PINGROUP(33, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[34] = PINGROUP(34, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[35] = PINGROUP(35, pci_e0, QUP_L4, JITTER_BIST, NA, NA, NA, NA,
+	[31] = PINGROUP(31, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[32] = PINGROUP(32, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[33] = PINGROUP(33, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[34] = PINGROUP(34, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[35] = PINGROUP(35, NORTH, pci_e0, QUP_L4, JITTER_BIST, NA, NA, NA, NA,
 			NA, NA),
-	[36] = PINGROUP(36, pci_e0, QUP_L5, PLL_BIST, NA, NA, NA, NA,
+	[36] = PINGROUP(36, NORTH, pci_e0, QUP_L5, PLL_BIST, NA, NA, NA, NA,
 			NA, NA),
-	[37] = PINGROUP(37, QUP_L6, AGERA_PLL, NA, NA, NA, NA, NA, NA,
+	[37] = PINGROUP(37, NORTH, QUP_L6, AGERA_PLL, NA, NA, NA, NA, NA, NA,
 			NA),
-	[38] = PINGROUP(38, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA),
-	[39] = PINGROUP(39, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
+	[38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, NORTH, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
 			NA),
-	[40] = PINGROUP(40, sd_write, tsif1_error, NA, NA, NA, NA, NA,
+	[40] = PINGROUP(40, NORTH, sd_write, tsif1_error, NA, NA, NA, NA, NA,
 			NA, NA),
-	[41] = PINGROUP(41, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA,
+	[41] = PINGROUP(41, SOUTH, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA,
 			NA),
-	[42] = PINGROUP(42, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA,
+	[42] = PINGROUP(42, SOUTH, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA,
 			NA),
-	[43] = PINGROUP(43, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
+	[43] = PINGROUP(43, SOUTH, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
 			NA),
-	[44] = PINGROUP(44, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
+	[44] = PINGROUP(44, SOUTH, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
 			NA),
-	[45] = PINGROUP(45, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[46] = PINGROUP(46, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[47] = PINGROUP(47, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[48] = PINGROUP(48, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[49] = PINGROUP(49, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[50] = PINGROUP(50, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[51] = PINGROUP(51, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[52] = PINGROUP(52, qup12, phase_flag16, qdss_cti, NA, NA, NA,
+	[45] = PINGROUP(45, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA,
 			NA, NA, NA),
-	[53] = PINGROUP(53, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
+	[53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
 			NA),
-	[54] = PINGROUP(54, qup10, GP_PDM0, phase_flag12, NA,
+	[54] = PINGROUP(54, NORTH, qup10, GP_PDM0, phase_flag12, NA,
 			wlan1_adc1, atest_usb13, ddr_pxi1, NA, NA),
-	[55] = PINGROUP(55, qup10, phase_flag13, NA, wlan1_adc0,
+	[55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, wlan1_adc0,
 			atest_usb12, ddr_pxi1, NA, NA, NA),
-	[56] = PINGROUP(56, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
+	[56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
 			NA),
-	[57] = PINGROUP(57, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
+	[57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
 			NA, NA, NA),
-	[58] = PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[59] = PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[60] = PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[61] = PINGROUP(61, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[62] = PINGROUP(62, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[63] = PINGROUP(63, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[64] = PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[65] = PINGROUP(65, pri_mi2s, qup8, wsa_clk, NA, NA, NA, NA, NA,
+	[58] = PINGROUP(58, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[59] = PINGROUP(59, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[60] = PINGROUP(60, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[61] = PINGROUP(61, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[62] = PINGROUP(62, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[63] = PINGROUP(63, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[64] = PINGROUP(64, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[65] = PINGROUP(65, NORTH, pri_mi2s, qup8, wsa_clk, NA, NA, NA, NA, NA,
 			NA),
-	[66] = PINGROUP(66, pri_mi2s_ws, qup8, wsa_data, GP_PDM1, NA,
+	[66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, wsa_data, GP_PDM1, NA,
 			NA, NA, NA, NA),
-	[67] = PINGROUP(67, pri_mi2s, qup8, NA, atest_usb2, NA, NA, NA,
+	[67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, atest_usb2, NA, NA, NA,
 			NA, NA),
-	[68] = PINGROUP(68, pri_mi2s, qup8, NA, atest_usb23, NA, NA, NA,
+	[68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, atest_usb23, NA, NA, NA,
 			NA, NA),
-	[69] = PINGROUP(69, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[70] = PINGROUP(70, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[71] = PINGROUP(71, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[72] = PINGROUP(72, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[73] = PINGROUP(73, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[74] = PINGROUP(74, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[75] = PINGROUP(75, ter_mi2s, phase_flag8, qdss_gpio8,
+	[69] = PINGROUP(69, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[70] = PINGROUP(70, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[71] = PINGROUP(71, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[72] = PINGROUP(72, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[73] = PINGROUP(73, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[74] = PINGROUP(74, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[75] = PINGROUP(75, NORTH, ter_mi2s, phase_flag8, qdss_gpio8,
 			atest_usb22, QUP_L4, NA, NA, NA, NA),
-	[76] = PINGROUP(76, ter_mi2s, phase_flag9, qdss_gpio9,
+	[76] = PINGROUP(76, NORTH, ter_mi2s, phase_flag9, qdss_gpio9,
 			atest_usb21, QUP_L5, NA, NA, NA, NA),
-	[77] = PINGROUP(77, ter_mi2s, phase_flag4, qdss_gpio10,
+	[77] = PINGROUP(77, NORTH, ter_mi2s, phase_flag4, qdss_gpio10,
 			atest_usb20, QUP_L6, NA, NA, NA, NA),
-	[78] = PINGROUP(78, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
+	[78] = PINGROUP(78, NORTH, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
 			NA),
-	[79] = PINGROUP(79, sec_mi2s, GP_PDM2, NA, qdss_gpio11, NA, NA,
+	[79] = PINGROUP(79, NORTH, sec_mi2s, GP_PDM2, NA, qdss_gpio11, NA, NA,
 			NA, NA, NA),
-	[80] = PINGROUP(80, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
+	[80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
 			NA, NA),
-	[81] = PINGROUP(81, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[82] = PINGROUP(82, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[83] = PINGROUP(83, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[84] = PINGROUP(84, qup15, NA, NA, NA, NA, NA, NA, NA, NA),
-	[85] = PINGROUP(85, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[86] = PINGROUP(86, qup5, copy_gp, NA, NA, NA, NA, NA, NA, NA),
-	[87] = PINGROUP(87, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[88] = PINGROUP(88, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[89] = PINGROUP(89, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
+	[81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
+	[82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
+	[83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
+	[84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, SOUTH, qup5, copy_gp, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
 			NA, NA, NA, NA),
-	[90] = PINGROUP(90, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
+	[90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
 			mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti),
-	[91] = PINGROUP(91, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
+	[91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
 			qdss_cti, NA, NA, NA),
-	[92] = PINGROUP(92, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
+	[92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
 			NA, NA, NA, NA),
-	[93] = PINGROUP(93, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
+	[93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
 			NA, NA, NA, NA),
-	[94] = PINGROUP(94, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
+	[94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
 			NA),
-	[95] = PINGROUP(95, tsif2_data, sdc41, qup7, GP_PDM0, NA, NA,
+	[95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, GP_PDM0, NA, NA,
 			NA, NA, NA),
-	[96] = PINGROUP(96, tsif2_sync, sdc40, qup7, phase_flag3, NA,
+	[96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA,
 			NA, NA, NA, NA),
-	[97] = PINGROUP(97, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
+	[97] = PINGROUP(97, WEST, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
 			NA),
-	[98] = PINGROUP(98, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+	[98] = PINGROUP(98, WEST, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
 			NA, NA),
-	[99] = PINGROUP(99, phase_flag14, prng_rosc, NA, NA, NA, NA, NA,
+	[99] = PINGROUP(99, NORTH, phase_flag14, prng_rosc, NA, NA, NA, NA, NA,
 			NA, NA),
-	[100] = PINGROUP(100, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
+	[100] = PINGROUP(100, WEST, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[101] = PINGROUP(101, NA, phase_flag5, NA, NA, NA, NA, NA, NA,
+	[101] = PINGROUP(101, WEST, NA, phase_flag5, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[102] = PINGROUP(102, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
+	[102] = PINGROUP(102, WEST, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[103] = PINGROUP(103, pci_e1, COPY_PHASE, NA, NA, NA, NA, NA, NA,
+	[103] = PINGROUP(103, WEST, pci_e1, COPY_PHASE, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[104] = PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[105] = PINGROUP(105, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+	[104] = PINGROUP(104, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
 			 NA, NA),
-	[106] = PINGROUP(106, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
+	[106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
 			 NA, NA),
-	[107] = PINGROUP(107, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
+	[107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
 			 NA, NA),
-	[108] = PINGROUP(108, uim2_present, qup13, NA, NA, NA, NA, NA,
+	[108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA,
 			 NA, NA),
-	[109] = PINGROUP(109, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
-	[110] = PINGROUP(110, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
-	[111] = PINGROUP(111, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
+	[109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
+	[110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+	[111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[112] = PINGROUP(112, uim1_present, NA, NA, NA, NA, NA, NA, NA,
+	[112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[113] = PINGROUP(113, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
+	[113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[114] = PINGROUP(114, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
+	[114] = PINGROUP(114, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
 			 NA, NA),
-	[115] = PINGROUP(115, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
+	[115] = PINGROUP(115, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
 			 NA, NA),
-	[116] = PINGROUP(116, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[117] = PINGROUP(117, NA, qdss_gpio0, atest_char, NA, NA, NA,
+	[116] = PINGROUP(116, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA,
 			 NA, NA, NA),
-	[118] = PINGROUP(118, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+	[118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
 			 NA, NA, NA, NA),
-	[119] = PINGROUP(119, NA, qdss_gpio2, atest_char2, NA, NA, NA,
+	[119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA,
 			 NA, NA, NA),
-	[120] = PINGROUP(120, NA, qdss_gpio3, atest_char1, NA, NA, NA,
+	[120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA,
 			 NA, NA, NA),
-	[121] = PINGROUP(121, NA, qdss_gpio4, atest_char0, NA, NA, NA,
+	[121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA,
 			 NA, NA, NA),
-	[122] = PINGROUP(122, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA,
+	[122] = PINGROUP(122, NORTH, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[123] = PINGROUP(123, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
+	[123] = PINGROUP(123, NORTH, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
 			 NA),
-	[124] = PINGROUP(124, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
+	[124] = PINGROUP(124, NORTH, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
 			 NA),
-	[125] = PINGROUP(125, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[126] = PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[127] = PINGROUP(127, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[128] = PINGROUP(128, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
+	[125] = PINGROUP(125, NORTH, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[126] = PINGROUP(126, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[127] = PINGROUP(127, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[128] = PINGROUP(128, WEST, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
 			 NA, NA),
-	[129] = PINGROUP(129, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
+	[129] = PINGROUP(129, WEST, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
 			 NA, NA),
-	[130] = PINGROUP(130, qlink_request, NA, NA, NA, NA, NA, NA, NA,
+	[130] = PINGROUP(130, WEST, qlink_request, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[131] = PINGROUP(131, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
+	[131] = PINGROUP(131, WEST, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[132] = PINGROUP(132, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[133] = PINGROUP(133, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[134] = PINGROUP(134, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[135] = PINGROUP(135, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
+	[132] = PINGROUP(132, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[135] = PINGROUP(135, WEST, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[136] = PINGROUP(136, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[137] = PINGROUP(137, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
+	[136] = PINGROUP(136, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[137] = PINGROUP(137, WEST, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
 			 NA),
-	[138] = PINGROUP(138, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
+	[138] = PINGROUP(138, WEST, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
 			 NA),
-	[139] = PINGROUP(139, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
+	[139] = PINGROUP(139, WEST, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[140] = PINGROUP(140, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
+	[140] = PINGROUP(140, WEST, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
 			 NA),
-	[141] = PINGROUP(141, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
+	[141] = PINGROUP(141, WEST, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[142] = PINGROUP(142, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
+	[142] = PINGROUP(142, WEST, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[143] = PINGROUP(143, NA, NAV_PPS, NAV_PPS, GPS_TX, phase_flag31,
+	[143] = PINGROUP(143, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, phase_flag31,
 			 NA, NA, NA, NA),
-	[144] = PINGROUP(144, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
-	[145] = PINGROUP(145, mss_lte, GPS_TX, NA, NA, NA, NA, NA, NA,
+	[144] = PINGROUP(144, SOUTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
+	[145] = PINGROUP(145, SOUTH, mss_lte, GPS_TX, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[146] = PINGROUP(146, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[147] = PINGROUP(147, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[148] = PINGROUP(148, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[149] = PINGROUP(149, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[150] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x599000, 15, 0),
-	[151] = SDC_QDSD_PINGROUP(sdc1_clk, 0x599000, 13, 6),
-	[152] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x599000, 11, 3),
-	[153] = SDC_QDSD_PINGROUP(sdc1_data, 0x599000, 9, 0),
-	[154] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
-	[155] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
-	[156] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
-	[157] = UFS_RESET(ufs_reset, 0x99f000),
+	[146] = PINGROUP(146, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[147] = PINGROUP(147, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[148] = PINGROUP(148, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[149] = PINGROUP(149, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[150] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x99000, 15, 0),
+	[151] = SDC_QDSD_PINGROUP(sdc1_clk, 0x99000, 13, 6),
+	[152] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x99000, 11, 3),
+	[153] = SDC_QDSD_PINGROUP(sdc1_data, 0x99000, 9, 0),
+	[154] = SDC_QDSD_PINGROUP(sdc2_clk, 0x9a000, 14, 6),
+	[155] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x9a000, 11, 3),
+	[156] = SDC_QDSD_PINGROUP(sdc2_data, 0x9a000, 9, 0),
+	[157] = UFS_RESET(ufs_reset, 0x9f000),
 };
-
 static const struct msm_dir_conn sdm670_dir_conn[] = {
 	{1, 510},
 	{3, 511},
@@ -1663,10 +1663,6 @@
 	.ngpios = 150,
 	.dir_conn = sdm670_dir_conn,
 	.n_dir_conns = ARRAY_SIZE(sdm670_dir_conn),
-	.tile_offsets = sdm670_tile_offsets,
-	.n_tile_offsets = ARRAY_SIZE(sdm670_tile_offsets),
-	.pin_base = sdm670_pin_base,
-	.reg_size = REG_SIZE,
 };
 
 static int sdm670_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
new file mode 100644
index 0000000..ed47e11
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
@@ -0,0 +1,1818 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname)					\
+	[msm_mux_##fname] = {				\
+		.name = #fname,				\
+		.groups = fname##_groups,		\
+		.ngroups = ARRAY_SIZE(fname##_groups),	\
+	}
+
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
+#define EAST	0x00100000
+#define REG_SIZE 0x1000
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)	\
+	{						\
+		.name = "gpio" #id,			\
+		.pins = gpio##id##_pins,		\
+		.npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins),	\
+		.funcs = (int[]){			\
+			msm_mux_gpio, /* gpio mode */	\
+			msm_mux_##f1,			\
+			msm_mux_##f2,			\
+			msm_mux_##f3,			\
+			msm_mux_##f4,			\
+			msm_mux_##f5,			\
+			msm_mux_##f6,			\
+			msm_mux_##f7,			\
+			msm_mux_##f8,			\
+			msm_mux_##f9,			\
+			msm_mux_##f10			\
+		},					\
+		.nfuncs = 11,				\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
+		.dir_conn_reg = (base == NORTH) ? base + 0xa4000 : \
+			((base == SOUTH) ? base + 0xa8000 : base + 0x9e000), \
+		.mux_bit = 2,			\
+		.pull_bit = 0,			\
+		.drv_bit = 6,			\
+		.oe_bit = 9,			\
+		.in_bit = 0,			\
+		.out_bit = 1,			\
+		.intr_enable_bit = 0,		\
+		.intr_status_bit = 0,		\
+		.intr_target_bit = 5,		\
+		.intr_target_kpss_val = 3,	\
+		.intr_raw_status_bit = 4,	\
+		.intr_polarity_bit = 1,		\
+		.intr_detection_bit = 2,	\
+		.intr_detection_width = 2,	\
+		.dir_conn_en_bit = 8,	        \
+	}
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
+	{						\
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = ctl,				\
+		.io_reg = 0,				\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = pull,			\
+		.drv_bit = drv,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = -1,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+
+#define UFS_RESET(pg_name, offset)				\
+	{						\
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = offset,			\
+		.io_reg = offset + 0x4,			\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = 3,				\
+		.drv_bit = 0,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = 0,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+static const struct pinctrl_pin_desc sdm845_pins[] = {
+	PINCTRL_PIN(0, "GPIO_0"),
+	PINCTRL_PIN(1, "GPIO_1"),
+	PINCTRL_PIN(2, "GPIO_2"),
+	PINCTRL_PIN(3, "GPIO_3"),
+	PINCTRL_PIN(4, "GPIO_4"),
+	PINCTRL_PIN(5, "GPIO_5"),
+	PINCTRL_PIN(6, "GPIO_6"),
+	PINCTRL_PIN(7, "GPIO_7"),
+	PINCTRL_PIN(8, "GPIO_8"),
+	PINCTRL_PIN(9, "GPIO_9"),
+	PINCTRL_PIN(10, "GPIO_10"),
+	PINCTRL_PIN(11, "GPIO_11"),
+	PINCTRL_PIN(12, "GPIO_12"),
+	PINCTRL_PIN(13, "GPIO_13"),
+	PINCTRL_PIN(14, "GPIO_14"),
+	PINCTRL_PIN(15, "GPIO_15"),
+	PINCTRL_PIN(16, "GPIO_16"),
+	PINCTRL_PIN(17, "GPIO_17"),
+	PINCTRL_PIN(18, "GPIO_18"),
+	PINCTRL_PIN(19, "GPIO_19"),
+	PINCTRL_PIN(20, "GPIO_20"),
+	PINCTRL_PIN(21, "GPIO_21"),
+	PINCTRL_PIN(22, "GPIO_22"),
+	PINCTRL_PIN(23, "GPIO_23"),
+	PINCTRL_PIN(24, "GPIO_24"),
+	PINCTRL_PIN(25, "GPIO_25"),
+	PINCTRL_PIN(26, "GPIO_26"),
+	PINCTRL_PIN(27, "GPIO_27"),
+	PINCTRL_PIN(28, "GPIO_28"),
+	PINCTRL_PIN(29, "GPIO_29"),
+	PINCTRL_PIN(30, "GPIO_30"),
+	PINCTRL_PIN(31, "GPIO_31"),
+	PINCTRL_PIN(32, "GPIO_32"),
+	PINCTRL_PIN(33, "GPIO_33"),
+	PINCTRL_PIN(34, "GPIO_34"),
+	PINCTRL_PIN(35, "GPIO_35"),
+	PINCTRL_PIN(36, "GPIO_36"),
+	PINCTRL_PIN(37, "GPIO_37"),
+	PINCTRL_PIN(38, "GPIO_38"),
+	PINCTRL_PIN(39, "GPIO_39"),
+	PINCTRL_PIN(40, "GPIO_40"),
+	PINCTRL_PIN(41, "GPIO_41"),
+	PINCTRL_PIN(42, "GPIO_42"),
+	PINCTRL_PIN(43, "GPIO_43"),
+	PINCTRL_PIN(44, "GPIO_44"),
+	PINCTRL_PIN(45, "GPIO_45"),
+	PINCTRL_PIN(46, "GPIO_46"),
+	PINCTRL_PIN(47, "GPIO_47"),
+	PINCTRL_PIN(48, "GPIO_48"),
+	PINCTRL_PIN(49, "GPIO_49"),
+	PINCTRL_PIN(50, "GPIO_50"),
+	PINCTRL_PIN(51, "GPIO_51"),
+	PINCTRL_PIN(52, "GPIO_52"),
+	PINCTRL_PIN(53, "GPIO_53"),
+	PINCTRL_PIN(54, "GPIO_54"),
+	PINCTRL_PIN(55, "GPIO_55"),
+	PINCTRL_PIN(56, "GPIO_56"),
+	PINCTRL_PIN(57, "GPIO_57"),
+	PINCTRL_PIN(58, "GPIO_58"),
+	PINCTRL_PIN(59, "GPIO_59"),
+	PINCTRL_PIN(60, "GPIO_60"),
+	PINCTRL_PIN(61, "GPIO_61"),
+	PINCTRL_PIN(62, "GPIO_62"),
+	PINCTRL_PIN(63, "GPIO_63"),
+	PINCTRL_PIN(64, "GPIO_64"),
+	PINCTRL_PIN(65, "GPIO_65"),
+	PINCTRL_PIN(66, "GPIO_66"),
+	PINCTRL_PIN(67, "GPIO_67"),
+	PINCTRL_PIN(68, "GPIO_68"),
+	PINCTRL_PIN(69, "GPIO_69"),
+	PINCTRL_PIN(70, "GPIO_70"),
+	PINCTRL_PIN(71, "GPIO_71"),
+	PINCTRL_PIN(72, "GPIO_72"),
+	PINCTRL_PIN(73, "GPIO_73"),
+	PINCTRL_PIN(74, "GPIO_74"),
+	PINCTRL_PIN(75, "GPIO_75"),
+	PINCTRL_PIN(76, "GPIO_76"),
+	PINCTRL_PIN(77, "GPIO_77"),
+	PINCTRL_PIN(78, "GPIO_78"),
+	PINCTRL_PIN(79, "GPIO_79"),
+	PINCTRL_PIN(80, "GPIO_80"),
+	PINCTRL_PIN(81, "GPIO_81"),
+	PINCTRL_PIN(82, "GPIO_82"),
+	PINCTRL_PIN(83, "GPIO_83"),
+	PINCTRL_PIN(84, "GPIO_84"),
+	PINCTRL_PIN(85, "GPIO_85"),
+	PINCTRL_PIN(86, "GPIO_86"),
+	PINCTRL_PIN(87, "GPIO_87"),
+	PINCTRL_PIN(88, "GPIO_88"),
+	PINCTRL_PIN(89, "GPIO_89"),
+	PINCTRL_PIN(90, "GPIO_90"),
+	PINCTRL_PIN(91, "GPIO_91"),
+	PINCTRL_PIN(92, "GPIO_92"),
+	PINCTRL_PIN(93, "GPIO_93"),
+	PINCTRL_PIN(94, "GPIO_94"),
+	PINCTRL_PIN(95, "GPIO_95"),
+	PINCTRL_PIN(96, "GPIO_96"),
+	PINCTRL_PIN(97, "GPIO_97"),
+	PINCTRL_PIN(98, "GPIO_98"),
+	PINCTRL_PIN(99, "GPIO_99"),
+	PINCTRL_PIN(100, "GPIO_100"),
+	PINCTRL_PIN(101, "GPIO_101"),
+	PINCTRL_PIN(102, "GPIO_102"),
+	PINCTRL_PIN(103, "GPIO_103"),
+	PINCTRL_PIN(104, "GPIO_104"),
+	PINCTRL_PIN(105, "GPIO_105"),
+	PINCTRL_PIN(106, "GPIO_106"),
+	PINCTRL_PIN(107, "GPIO_107"),
+	PINCTRL_PIN(108, "GPIO_108"),
+	PINCTRL_PIN(109, "GPIO_109"),
+	PINCTRL_PIN(110, "GPIO_110"),
+	PINCTRL_PIN(111, "GPIO_111"),
+	PINCTRL_PIN(112, "GPIO_112"),
+	PINCTRL_PIN(113, "GPIO_113"),
+	PINCTRL_PIN(114, "GPIO_114"),
+	PINCTRL_PIN(115, "GPIO_115"),
+	PINCTRL_PIN(116, "GPIO_116"),
+	PINCTRL_PIN(117, "GPIO_117"),
+	PINCTRL_PIN(118, "GPIO_118"),
+	PINCTRL_PIN(119, "GPIO_119"),
+	PINCTRL_PIN(120, "GPIO_120"),
+	PINCTRL_PIN(121, "GPIO_121"),
+	PINCTRL_PIN(122, "GPIO_122"),
+	PINCTRL_PIN(123, "GPIO_123"),
+	PINCTRL_PIN(124, "GPIO_124"),
+	PINCTRL_PIN(125, "GPIO_125"),
+	PINCTRL_PIN(126, "GPIO_126"),
+	PINCTRL_PIN(127, "GPIO_127"),
+	PINCTRL_PIN(128, "GPIO_128"),
+	PINCTRL_PIN(129, "GPIO_129"),
+	PINCTRL_PIN(130, "GPIO_130"),
+	PINCTRL_PIN(131, "GPIO_131"),
+	PINCTRL_PIN(132, "GPIO_132"),
+	PINCTRL_PIN(133, "GPIO_133"),
+	PINCTRL_PIN(134, "GPIO_134"),
+	PINCTRL_PIN(135, "GPIO_135"),
+	PINCTRL_PIN(136, "GPIO_136"),
+	PINCTRL_PIN(137, "GPIO_137"),
+	PINCTRL_PIN(138, "GPIO_138"),
+	PINCTRL_PIN(139, "GPIO_139"),
+	PINCTRL_PIN(140, "GPIO_140"),
+	PINCTRL_PIN(141, "GPIO_141"),
+	PINCTRL_PIN(142, "GPIO_142"),
+	PINCTRL_PIN(143, "GPIO_143"),
+	PINCTRL_PIN(144, "GPIO_144"),
+	PINCTRL_PIN(145, "GPIO_145"),
+	PINCTRL_PIN(146, "GPIO_146"),
+	PINCTRL_PIN(147, "GPIO_147"),
+	PINCTRL_PIN(148, "GPIO_148"),
+	PINCTRL_PIN(149, "GPIO_149"),
+	PINCTRL_PIN(150, "SDC2_CLK"),
+	PINCTRL_PIN(151, "SDC2_CMD"),
+	PINCTRL_PIN(152, "SDC2_DATA"),
+	PINCTRL_PIN(153, "UFS_RESET"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+	static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(58);
+DECLARE_MSM_GPIO_PINS(59);
+DECLARE_MSM_GPIO_PINS(60);
+DECLARE_MSM_GPIO_PINS(61);
+DECLARE_MSM_GPIO_PINS(62);
+DECLARE_MSM_GPIO_PINS(63);
+DECLARE_MSM_GPIO_PINS(64);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(69);
+DECLARE_MSM_GPIO_PINS(70);
+DECLARE_MSM_GPIO_PINS(71);
+DECLARE_MSM_GPIO_PINS(72);
+DECLARE_MSM_GPIO_PINS(73);
+DECLARE_MSM_GPIO_PINS(74);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(104);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+DECLARE_MSM_GPIO_PINS(134);
+DECLARE_MSM_GPIO_PINS(135);
+DECLARE_MSM_GPIO_PINS(136);
+DECLARE_MSM_GPIO_PINS(137);
+DECLARE_MSM_GPIO_PINS(138);
+DECLARE_MSM_GPIO_PINS(139);
+DECLARE_MSM_GPIO_PINS(140);
+DECLARE_MSM_GPIO_PINS(141);
+DECLARE_MSM_GPIO_PINS(142);
+DECLARE_MSM_GPIO_PINS(143);
+DECLARE_MSM_GPIO_PINS(144);
+DECLARE_MSM_GPIO_PINS(145);
+DECLARE_MSM_GPIO_PINS(146);
+DECLARE_MSM_GPIO_PINS(147);
+DECLARE_MSM_GPIO_PINS(148);
+DECLARE_MSM_GPIO_PINS(149);
+
+static const unsigned int sdc2_clk_pins[] = { 150 };
+static const unsigned int sdc2_cmd_pins[] = { 151 };
+static const unsigned int sdc2_data_pins[] = { 152 };
+static const unsigned int ufs_reset_pins[] = { 153 };
+
+enum sdm845_functions {
+	msm_mux_ddr_pxi3,
+	msm_mux_cam_mclk,
+	msm_mux_pll_bypassnl,
+	msm_mux_qdss_gpio0,
+	msm_mux_pll_reset,
+	msm_mux_qdss_gpio1,
+	msm_mux_qdss_gpio2,
+	msm_mux_qdss_gpio3,
+	msm_mux_cci_i2c,
+	msm_mux_qup1,
+	msm_mux_qdss_gpio4,
+	msm_mux_qdss_gpio5,
+	msm_mux_qdss_gpio6,
+	msm_mux_qdss_gpio7,
+	msm_mux_cci_timer0,
+	msm_mux_gcc_gp2,
+	msm_mux_qdss_gpio8,
+	msm_mux_cci_timer1,
+	msm_mux_gcc_gp3,
+	msm_mux_qdss_gpio,
+	msm_mux_cci_timer2,
+	msm_mux_qdss_gpio9,
+	msm_mux_cci_timer3,
+	msm_mux_cci_async,
+	msm_mux_qdss_gpio10,
+	msm_mux_cci_timer4,
+	msm_mux_qdss_gpio11,
+	msm_mux_qdss_gpio12,
+	msm_mux_qup2,
+	msm_mux_qdss_gpio13,
+	msm_mux_qdss_gpio14,
+	msm_mux_phase_flag1,
+	msm_mux_qdss_gpio15,
+	msm_mux_phase_flag2,
+	msm_mux_qup11,
+	msm_mux_qup14,
+	msm_mux_pci_e0,
+	msm_mux_jitter_bist,
+	msm_mux_pll_bist,
+	msm_mux_atest_tsens,
+	msm_mux_agera_pll,
+	msm_mux_usb_phy,
+	msm_mux_lpass_slimbus,
+	msm_mux_sd_write,
+	msm_mux_tsif1_error,
+	msm_mux_qup3,
+	msm_mux_qup6,
+	msm_mux_qup12,
+	msm_mux_phase_flag16,
+	msm_mux_qup10,
+	msm_mux_phase_flag11,
+	msm_mux_phase_flag12,
+	msm_mux_phase_flag13,
+	msm_mux_phase_flag17,
+	msm_mux_qua_mi2s,
+	msm_mux_gcc_gp1,
+	msm_mux_phase_flag18,
+	msm_mux_phase_flag19,
+	msm_mux_phase_flag20,
+	msm_mux_cri_trng0,
+	msm_mux_phase_flag21,
+	msm_mux_cri_trng1,
+	msm_mux_phase_flag22,
+	msm_mux_cri_trng,
+	msm_mux_phase_flag23,
+	msm_mux_phase_flag24,
+	msm_mux_pri_mi2s,
+	msm_mux_sp_cmu,
+	msm_mux_phase_flag25,
+	msm_mux_qup8,
+	msm_mux_pri_mi2s_ws,
+	msm_mux_spkr_i2s,
+	msm_mux_audio_ref,
+	msm_mux_tsense_pwm1,
+	msm_mux_tsense_pwm2,
+	msm_mux_btfm_slimbus,
+	msm_mux_atest_usb2,
+	msm_mux_ter_mi2s,
+	msm_mux_phase_flag7,
+	msm_mux_atest_usb23,
+	msm_mux_phase_flag8,
+	msm_mux_atest_usb22,
+	msm_mux_phase_flag9,
+	msm_mux_atest_usb21,
+	msm_mux_phase_flag4,
+	msm_mux_atest_usb20,
+	msm_mux_sec_mi2s,
+	msm_mux_qup15,
+	msm_mux_qup5,
+	msm_mux_tsif1_clk,
+	msm_mux_qup4,
+	msm_mux_qspi_cs,
+	msm_mux_tgu_ch3,
+	msm_mux_phase_flag10,
+	msm_mux_tsif1_en,
+	msm_mux_mdp_vsync0,
+	msm_mux_mdp_vsync1,
+	msm_mux_mdp_vsync2,
+	msm_mux_mdp_vsync3,
+	msm_mux_tgu_ch0,
+	msm_mux_phase_flag0,
+	msm_mux_tsif1_data,
+	msm_mux_sdc4_cmd,
+	msm_mux_qspi0,
+	msm_mux_tgu_ch1,
+	msm_mux_tsif2_error,
+	msm_mux_sdc43,
+	msm_mux_qspi1,
+	msm_mux_vfr_1,
+	msm_mux_tgu_ch2,
+	msm_mux_tsif2_clk,
+	msm_mux_sdc4_clk,
+	msm_mux_qup7,
+	msm_mux_qspi2,
+	msm_mux_tsif2_en,
+	msm_mux_sdc42,
+	msm_mux_qspi3,
+	msm_mux_tsif2_data,
+	msm_mux_sdc41,
+	msm_mux_qspi_clk,
+	msm_mux_tsif2_sync,
+	msm_mux_sdc40,
+	msm_mux_phase_flag3,
+	msm_mux_ldo_en,
+	msm_mux_ldo_update,
+	msm_mux_phase_flag14,
+	msm_mux_phase_flag15,
+	msm_mux_pci_e1,
+	msm_mux_prng_rosc,
+	msm_mux_phase_flag5,
+	msm_mux_uim2_data,
+	msm_mux_qup13,
+	msm_mux_uim2_clk,
+	msm_mux_uim2_reset,
+	msm_mux_uim2_present,
+	msm_mux_uim1_data,
+	msm_mux_uim1_clk,
+	msm_mux_uim1_reset,
+	msm_mux_uim1_present,
+	msm_mux_uim_batt,
+	msm_mux_edp_hot,
+	msm_mux_nav_pps,
+	msm_mux_atest_char,
+	msm_mux_adsp_ext,
+	msm_mux_atest_char3,
+	msm_mux_atest_char2,
+	msm_mux_atest_char1,
+	msm_mux_atest_char0,
+	msm_mux_qlink_request,
+	msm_mux_qlink_enable,
+	msm_mux_pa_indicator,
+	msm_mux_phase_flag26,
+	msm_mux_phase_flag27,
+	msm_mux_phase_flag28,
+	msm_mux_phase_flag6,
+	msm_mux_phase_flag29,
+	msm_mux_phase_flag30,
+	msm_mux_phase_flag31,
+	msm_mux_mss_lte,
+	msm_mux_qup0,
+	msm_mux_gpio,
+	msm_mux_qup9,
+	msm_mux_qdss_cti,
+	msm_mux_ddr_pxi0,
+	msm_mux_ddr_bist,
+	msm_mux_atest_tsens2,
+	msm_mux_vsense_trigger,
+	msm_mux_atest_usb1,
+	msm_mux_qup_l4,
+	msm_mux_wlan1_adc1,
+	msm_mux_atest_usb13,
+	msm_mux_ddr_pxi1,
+	msm_mux_qup_l5,
+	msm_mux_wlan1_adc0,
+	msm_mux_atest_usb12,
+	msm_mux_mdp_vsync,
+	msm_mux_qup_l6,
+	msm_mux_wlan2_adc1,
+	msm_mux_atest_usb11,
+	msm_mux_ddr_pxi2,
+	msm_mux_edp_lcd,
+	msm_mux_dbg_out,
+	msm_mux_wlan2_adc0,
+	msm_mux_atest_usb10,
+	msm_mux_m_voc,
+	msm_mux_tsif1_sync,
+	msm_mux_NA,
+};
+
+static const char * const ddr_pxi3_groups[] = {
+	"gpio12", "gpio13",
+};
+static const char * const cam_mclk_groups[] = {
+	"gpio13", "gpio14", "gpio15", "gpio16",
+};
+static const char * const pll_bypassnl_groups[] = {
+	"gpio13",
+};
+static const char * const qdss_gpio0_groups[] = {
+	"gpio13", "gpio117",
+};
+static const char * const pll_reset_groups[] = {
+	"gpio14",
+};
+static const char * const qdss_gpio1_groups[] = {
+	"gpio14", "gpio118",
+};
+static const char * const qdss_gpio2_groups[] = {
+	"gpio15", "gpio119",
+};
+static const char * const qdss_gpio3_groups[] = {
+	"gpio16", "gpio120",
+};
+static const char * const cci_i2c_groups[] = {
+	"gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qup1_groups[] = {
+	"gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qdss_gpio4_groups[] = {
+	"gpio17", "gpio121",
+};
+static const char * const qdss_gpio5_groups[] = {
+	"gpio18", "gpio122",
+};
+static const char * const qdss_gpio6_groups[] = {
+	"gpio19", "gpio41",
+};
+static const char * const qdss_gpio7_groups[] = {
+	"gpio20", "gpio42",
+};
+static const char * const cci_timer0_groups[] = {
+	"gpio21",
+};
+static const char * const gcc_gp2_groups[] = {
+	"gpio21", "gpio58",
+};
+static const char * const qdss_gpio8_groups[] = {
+	"gpio21", "gpio75",
+};
+static const char * const cci_timer1_groups[] = {
+	"gpio22",
+};
+static const char * const gcc_gp3_groups[] = {
+	"gpio22", "gpio59",
+};
+static const char * const qdss_gpio_groups[] = {
+	"gpio22", "gpio30", "gpio123", "gpio124",
+};
+static const char * const cci_timer2_groups[] = {
+	"gpio23",
+};
+static const char * const qdss_gpio9_groups[] = {
+	"gpio23", "gpio76",
+};
+static const char * const cci_timer3_groups[] = {
+	"gpio24",
+};
+static const char * const cci_async_groups[] = {
+	"gpio24", "gpio25", "gpio26",
+};
+static const char * const qdss_gpio10_groups[] = {
+	"gpio24", "gpio77",
+};
+static const char * const cci_timer4_groups[] = {
+	"gpio25",
+};
+static const char * const qdss_gpio11_groups[] = {
+	"gpio25", "gpio79",
+};
+static const char * const qdss_gpio12_groups[] = {
+	"gpio26", "gpio80",
+};
+static const char * const qup2_groups[] = {
+	"gpio27", "gpio28", "gpio29", "gpio30",
+};
+static const char * const qdss_gpio13_groups[] = {
+	"gpio27", "gpio93",
+};
+static const char * const qdss_gpio14_groups[] = {
+	"gpio28", "gpio43",
+};
+static const char * const phase_flag1_groups[] = {
+	"gpio29",
+};
+static const char * const qdss_gpio15_groups[] = {
+	"gpio29", "gpio44",
+};
+static const char * const phase_flag2_groups[] = {
+	"gpio30",
+};
+static const char * const qup11_groups[] = {
+	"gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const qup14_groups[] = {
+	"gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const pci_e0_groups[] = {
+	"gpio35", "gpio36",
+};
+static const char * const jitter_bist_groups[] = {
+	"gpio35",
+};
+static const char * const pll_bist_groups[] = {
+	"gpio36",
+};
+static const char * const atest_tsens_groups[] = {
+	"gpio36",
+};
+static const char * const agera_pll_groups[] = {
+	"gpio37",
+};
+static const char * const usb_phy_groups[] = {
+	"gpio38",
+};
+static const char * const lpass_slimbus_groups[] = {
+	"gpio39", "gpio70", "gpio71", "gpio72",
+};
+static const char * const sd_write_groups[] = {
+	"gpio40",
+};
+static const char * const tsif1_error_groups[] = {
+	"gpio40",
+};
+static const char * const qup3_groups[] = {
+	"gpio41", "gpio42", "gpio43", "gpio44",
+};
+static const char * const qup6_groups[] = {
+	"gpio45", "gpio46", "gpio47", "gpio48",
+};
+static const char * const qup12_groups[] = {
+	"gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const phase_flag16_groups[] = {
+	"gpio52",
+};
+static const char * const qup10_groups[] = {
+	"gpio53", "gpio54", "gpio55", "gpio56",
+};
+static const char * const phase_flag11_groups[] = {
+	"gpio53",
+};
+static const char * const phase_flag12_groups[] = {
+	"gpio54",
+};
+static const char * const phase_flag13_groups[] = {
+	"gpio55",
+};
+static const char * const phase_flag17_groups[] = {
+	"gpio56",
+};
+static const char * const qua_mi2s_groups[] = {
+	"gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+};
+static const char * const gcc_gp1_groups[] = {
+	"gpio57", "gpio78",
+};
+static const char * const phase_flag18_groups[] = {
+	"gpio57",
+};
+static const char * const phase_flag19_groups[] = {
+	"gpio58",
+};
+static const char * const phase_flag20_groups[] = {
+	"gpio59",
+};
+static const char * const cri_trng0_groups[] = {
+	"gpio60",
+};
+static const char * const phase_flag21_groups[] = {
+	"gpio60",
+};
+static const char * const cri_trng1_groups[] = {
+	"gpio61",
+};
+static const char * const phase_flag22_groups[] = {
+	"gpio61",
+};
+static const char * const cri_trng_groups[] = {
+	"gpio62",
+};
+static const char * const phase_flag23_groups[] = {
+	"gpio62",
+};
+static const char * const phase_flag24_groups[] = {
+	"gpio63",
+};
+static const char * const pri_mi2s_groups[] = {
+	"gpio64", "gpio65", "gpio67", "gpio68",
+};
+static const char * const sp_cmu_groups[] = {
+	"gpio64",
+};
+static const char * const phase_flag25_groups[] = {
+	"gpio64",
+};
+static const char * const qup8_groups[] = {
+	"gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const pri_mi2s_ws_groups[] = {
+	"gpio66",
+};
+static const char * const spkr_i2s_groups[] = {
+	"gpio69", "gpio70", "gpio71", "gpio72",
+};
+static const char * const audio_ref_groups[] = {
+	"gpio69",
+};
+static const char * const tsense_pwm1_groups[] = {
+	"gpio71",
+};
+static const char * const tsense_pwm2_groups[] = {
+	"gpio71",
+};
+static const char * const btfm_slimbus_groups[] = {
+	"gpio73", "gpio74",
+};
+static const char * const atest_usb2_groups[] = {
+	"gpio73",
+};
+static const char * const ter_mi2s_groups[] = {
+	"gpio74", "gpio75", "gpio76", "gpio77", "gpio78",
+};
+static const char * const phase_flag7_groups[] = {
+	"gpio74",
+};
+static const char * const atest_usb23_groups[] = {
+	"gpio74",
+};
+static const char * const phase_flag8_groups[] = {
+	"gpio75",
+};
+static const char * const atest_usb22_groups[] = {
+	"gpio75",
+};
+static const char * const phase_flag9_groups[] = {
+	"gpio76",
+};
+static const char * const atest_usb21_groups[] = {
+	"gpio76",
+};
+static const char * const phase_flag4_groups[] = {
+	"gpio77",
+};
+static const char * const atest_usb20_groups[] = {
+	"gpio77",
+};
+static const char * const sec_mi2s_groups[] = {
+	"gpio79", "gpio80", "gpio81", "gpio82", "gpio83",
+};
+static const char * const qup15_groups[] = {
+	"gpio81", "gpio82", "gpio83", "gpio84",
+};
+static const char * const qup5_groups[] = {
+	"gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const tsif1_clk_groups[] = {
+	"gpio89",
+};
+static const char * const qup4_groups[] = {
+	"gpio89", "gpio90", "gpio91", "gpio92",
+};
+static const char * const qspi_cs_groups[] = {
+	"gpio89", "gpio90",
+};
+static const char * const tgu_ch3_groups[] = {
+	"gpio89",
+};
+static const char * const phase_flag10_groups[] = {
+	"gpio89",
+};
+static const char * const tsif1_en_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync0_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync1_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync2_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync3_groups[] = {
+	"gpio90",
+};
+static const char * const tgu_ch0_groups[] = {
+	"gpio90",
+};
+static const char * const phase_flag0_groups[] = {
+	"gpio90",
+};
+static const char * const tsif1_data_groups[] = {
+	"gpio91",
+};
+static const char * const sdc4_cmd_groups[] = {
+	"gpio91",
+};
+static const char * const qspi0_groups[] = {
+	"gpio91",
+};
+static const char * const tgu_ch1_groups[] = {
+	"gpio91",
+};
+static const char * const tsif2_error_groups[] = {
+	"gpio92",
+};
+static const char * const sdc43_groups[] = {
+	"gpio92",
+};
+static const char * const qspi1_groups[] = {
+	"gpio92",
+};
+static const char * const vfr_1_groups[] = {
+	"gpio92",
+};
+static const char * const tgu_ch2_groups[] = {
+	"gpio92",
+};
+static const char * const tsif2_clk_groups[] = {
+	"gpio93",
+};
+static const char * const sdc4_clk_groups[] = {
+	"gpio93",
+};
+static const char * const qup7_groups[] = {
+	"gpio93", "gpio94", "gpio95", "gpio96",
+};
+static const char * const qspi2_groups[] = {
+	"gpio93",
+};
+static const char * const tsif2_en_groups[] = {
+	"gpio94",
+};
+static const char * const sdc42_groups[] = {
+	"gpio94",
+};
+static const char * const qspi3_groups[] = {
+	"gpio94",
+};
+static const char * const tsif2_data_groups[] = {
+	"gpio95",
+};
+static const char * const sdc41_groups[] = {
+	"gpio95",
+};
+static const char * const qspi_clk_groups[] = {
+	"gpio95",
+};
+static const char * const tsif2_sync_groups[] = {
+	"gpio96",
+};
+static const char * const sdc40_groups[] = {
+	"gpio96",
+};
+static const char * const phase_flag3_groups[] = {
+	"gpio96",
+};
+static const char * const ldo_en_groups[] = {
+	"gpio97",
+};
+static const char * const ldo_update_groups[] = {
+	"gpio98",
+};
+static const char * const phase_flag14_groups[] = {
+	"gpio99",
+};
+static const char * const phase_flag15_groups[] = {
+	"gpio100",
+};
+static const char * const pci_e1_groups[] = {
+	"gpio102", "gpio103",
+};
+static const char * const prng_rosc_groups[] = {
+	"gpio102",
+};
+static const char * const phase_flag5_groups[] = {
+	"gpio103",
+};
+static const char * const uim2_data_groups[] = {
+	"gpio105",
+};
+static const char * const qup13_groups[] = {
+	"gpio105", "gpio106", "gpio107", "gpio108",
+};
+static const char * const uim2_clk_groups[] = {
+	"gpio106",
+};
+static const char * const uim2_reset_groups[] = {
+	"gpio107",
+};
+static const char * const uim2_present_groups[] = {
+	"gpio108",
+};
+static const char * const uim1_data_groups[] = {
+	"gpio109",
+};
+static const char * const uim1_clk_groups[] = {
+	"gpio110",
+};
+static const char * const uim1_reset_groups[] = {
+	"gpio111",
+};
+static const char * const uim1_present_groups[] = {
+	"gpio112",
+};
+static const char * const uim_batt_groups[] = {
+	"gpio113",
+};
+static const char * const edp_hot_groups[] = {
+	"gpio113",
+};
+static const char * const nav_pps_groups[] = {
+	"gpio114", "gpio114", "gpio115", "gpio115", "gpio128", "gpio128",
+	"gpio129", "gpio129", "gpio143", "gpio143",
+};
+static const char * const atest_char_groups[] = {
+	"gpio117",
+};
+static const char * const adsp_ext_groups[] = {
+	"gpio118",
+};
+static const char * const atest_char3_groups[] = {
+	"gpio118",
+};
+static const char * const atest_char2_groups[] = {
+	"gpio119",
+};
+static const char * const atest_char1_groups[] = {
+	"gpio120",
+};
+static const char * const atest_char0_groups[] = {
+	"gpio121",
+};
+static const char * const qlink_request_groups[] = {
+	"gpio130",
+};
+static const char * const qlink_enable_groups[] = {
+	"gpio131",
+};
+static const char * const pa_indicator_groups[] = {
+	"gpio135",
+};
+static const char * const phase_flag26_groups[] = {
+	"gpio137",
+};
+static const char * const phase_flag27_groups[] = {
+	"gpio138",
+};
+static const char * const phase_flag28_groups[] = {
+	"gpio139",
+};
+static const char * const phase_flag6_groups[] = {
+	"gpio140",
+};
+static const char * const phase_flag29_groups[] = {
+	"gpio141",
+};
+static const char * const phase_flag30_groups[] = {
+	"gpio142",
+};
+static const char * const phase_flag31_groups[] = {
+	"gpio143",
+};
+static const char * const mss_lte_groups[] = {
+	"gpio144", "gpio145",
+};
+static const char * const qup0_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const gpio_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+	"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+	"gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+	"gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+	"gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+	"gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+	"gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+	"gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+	"gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+	"gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+	"gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+	"gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+	"gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+	"gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+	"gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+	"gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+	"gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+	"gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128",
+	"gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134",
+	"gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140",
+	"gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146",
+	"gpio147", "gpio148", "gpio149",
+};
+static const char * const qup9_groups[] = {
+	"gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const qdss_cti_groups[] = {
+	"gpio4", "gpio5", "gpio51", "gpio52", "gpio62", "gpio63", "gpio90",
+	"gpio91",
+};
+static const char * const ddr_pxi0_groups[] = {
+	"gpio6", "gpio7",
+};
+static const char * const ddr_bist_groups[] = {
+	"gpio7", "gpio8", "gpio9", "gpio10",
+};
+static const char * const atest_tsens2_groups[] = {
+	"gpio7",
+};
+static const char * const vsense_trigger_groups[] = {
+	"gpio7",
+};
+static const char * const atest_usb1_groups[] = {
+	"gpio7",
+};
+static const char * const qup_l4_groups[] = {
+	"gpio8", "gpio35", "gpio105", "gpio123",
+};
+static const char * const wlan1_adc1_groups[] = {
+	"gpio8",
+};
+static const char * const atest_usb13_groups[] = {
+	"gpio8",
+};
+static const char * const ddr_pxi1_groups[] = {
+	"gpio8", "gpio9",
+};
+static const char * const qup_l5_groups[] = {
+	"gpio9", "gpio36", "gpio106", "gpio124",
+};
+static const char * const wlan1_adc0_groups[] = {
+	"gpio9",
+};
+static const char * const atest_usb12_groups[] = {
+	"gpio9",
+};
+static const char * const mdp_vsync_groups[] = {
+	"gpio10", "gpio11", "gpio12", "gpio97", "gpio98",
+};
+static const char * const qup_l6_groups[] = {
+	"gpio10", "gpio37", "gpio107", "gpio125",
+};
+static const char * const wlan2_adc1_groups[] = {
+	"gpio10",
+};
+static const char * const atest_usb11_groups[] = {
+	"gpio10",
+};
+static const char * const ddr_pxi2_groups[] = {
+	"gpio10", "gpio11",
+};
+static const char * const edp_lcd_groups[] = {
+	"gpio11",
+};
+static const char * const dbg_out_groups[] = {
+	"gpio11",
+};
+static const char * const wlan2_adc0_groups[] = {
+	"gpio11",
+};
+static const char * const atest_usb10_groups[] = {
+	"gpio11",
+};
+static const char * const m_voc_groups[] = {
+	"gpio12",
+};
+static const char * const tsif1_sync_groups[] = {
+	"gpio12",
+};
+
+static const struct msm_function sdm845_functions[] = {
+	FUNCTION(ddr_pxi3),
+	FUNCTION(cam_mclk),
+	FUNCTION(pll_bypassnl),
+	FUNCTION(qdss_gpio0),
+	FUNCTION(pll_reset),
+	FUNCTION(qdss_gpio1),
+	FUNCTION(qdss_gpio2),
+	FUNCTION(qdss_gpio3),
+	FUNCTION(cci_i2c),
+	FUNCTION(qup1),
+	FUNCTION(qdss_gpio4),
+	FUNCTION(qdss_gpio5),
+	FUNCTION(qdss_gpio6),
+	FUNCTION(qdss_gpio7),
+	FUNCTION(cci_timer0),
+	FUNCTION(gcc_gp2),
+	FUNCTION(qdss_gpio8),
+	FUNCTION(cci_timer1),
+	FUNCTION(gcc_gp3),
+	FUNCTION(qdss_gpio),
+	FUNCTION(cci_timer2),
+	FUNCTION(qdss_gpio9),
+	FUNCTION(cci_timer3),
+	FUNCTION(cci_async),
+	FUNCTION(qdss_gpio10),
+	FUNCTION(cci_timer4),
+	FUNCTION(qdss_gpio11),
+	FUNCTION(qdss_gpio12),
+	FUNCTION(qup2),
+	FUNCTION(qdss_gpio13),
+	FUNCTION(qdss_gpio14),
+	FUNCTION(phase_flag1),
+	FUNCTION(qdss_gpio15),
+	FUNCTION(phase_flag2),
+	FUNCTION(qup11),
+	FUNCTION(qup14),
+	FUNCTION(pci_e0),
+	FUNCTION(jitter_bist),
+	FUNCTION(pll_bist),
+	FUNCTION(atest_tsens),
+	FUNCTION(agera_pll),
+	FUNCTION(usb_phy),
+	FUNCTION(lpass_slimbus),
+	FUNCTION(sd_write),
+	FUNCTION(tsif1_error),
+	FUNCTION(qup3),
+	FUNCTION(qup6),
+	FUNCTION(qup12),
+	FUNCTION(phase_flag16),
+	FUNCTION(qup10),
+	FUNCTION(phase_flag11),
+	FUNCTION(phase_flag12),
+	FUNCTION(phase_flag13),
+	FUNCTION(phase_flag17),
+	FUNCTION(qua_mi2s),
+	FUNCTION(gcc_gp1),
+	FUNCTION(phase_flag18),
+	FUNCTION(phase_flag19),
+	FUNCTION(phase_flag20),
+	FUNCTION(cri_trng0),
+	FUNCTION(phase_flag21),
+	FUNCTION(cri_trng1),
+	FUNCTION(phase_flag22),
+	FUNCTION(cri_trng),
+	FUNCTION(phase_flag23),
+	FUNCTION(phase_flag24),
+	FUNCTION(pri_mi2s),
+	FUNCTION(sp_cmu),
+	FUNCTION(phase_flag25),
+	FUNCTION(qup8),
+	FUNCTION(pri_mi2s_ws),
+	FUNCTION(spkr_i2s),
+	FUNCTION(audio_ref),
+	FUNCTION(tsense_pwm1),
+	FUNCTION(tsense_pwm2),
+	FUNCTION(btfm_slimbus),
+	FUNCTION(atest_usb2),
+	FUNCTION(ter_mi2s),
+	FUNCTION(phase_flag7),
+	FUNCTION(atest_usb23),
+	FUNCTION(phase_flag8),
+	FUNCTION(atest_usb22),
+	FUNCTION(phase_flag9),
+	FUNCTION(atest_usb21),
+	FUNCTION(phase_flag4),
+	FUNCTION(atest_usb20),
+	FUNCTION(sec_mi2s),
+	FUNCTION(qup15),
+	FUNCTION(qup5),
+	FUNCTION(tsif1_clk),
+	FUNCTION(qup4),
+	FUNCTION(qspi_cs),
+	FUNCTION(tgu_ch3),
+	FUNCTION(phase_flag10),
+	FUNCTION(tsif1_en),
+	FUNCTION(mdp_vsync0),
+	FUNCTION(mdp_vsync1),
+	FUNCTION(mdp_vsync2),
+	FUNCTION(mdp_vsync3),
+	FUNCTION(tgu_ch0),
+	FUNCTION(phase_flag0),
+	FUNCTION(tsif1_data),
+	FUNCTION(sdc4_cmd),
+	FUNCTION(qspi0),
+	FUNCTION(tgu_ch1),
+	FUNCTION(tsif2_error),
+	FUNCTION(sdc43),
+	FUNCTION(qspi1),
+	FUNCTION(vfr_1),
+	FUNCTION(tgu_ch2),
+	FUNCTION(tsif2_clk),
+	FUNCTION(sdc4_clk),
+	FUNCTION(qup7),
+	FUNCTION(qspi2),
+	FUNCTION(tsif2_en),
+	FUNCTION(sdc42),
+	FUNCTION(qspi3),
+	FUNCTION(tsif2_data),
+	FUNCTION(sdc41),
+	FUNCTION(qspi_clk),
+	FUNCTION(tsif2_sync),
+	FUNCTION(sdc40),
+	FUNCTION(phase_flag3),
+	FUNCTION(ldo_en),
+	FUNCTION(ldo_update),
+	FUNCTION(phase_flag14),
+	FUNCTION(phase_flag15),
+	FUNCTION(pci_e1),
+	FUNCTION(prng_rosc),
+	FUNCTION(phase_flag5),
+	FUNCTION(uim2_data),
+	FUNCTION(qup13),
+	FUNCTION(uim2_clk),
+	FUNCTION(uim2_reset),
+	FUNCTION(uim2_present),
+	FUNCTION(uim1_data),
+	FUNCTION(uim1_clk),
+	FUNCTION(uim1_reset),
+	FUNCTION(uim1_present),
+	FUNCTION(uim_batt),
+	FUNCTION(edp_hot),
+	FUNCTION(nav_pps),
+	FUNCTION(atest_char),
+	FUNCTION(adsp_ext),
+	FUNCTION(atest_char3),
+	FUNCTION(atest_char2),
+	FUNCTION(atest_char1),
+	FUNCTION(atest_char0),
+	FUNCTION(qlink_request),
+	FUNCTION(qlink_enable),
+	FUNCTION(pa_indicator),
+	FUNCTION(phase_flag26),
+	FUNCTION(phase_flag27),
+	FUNCTION(phase_flag28),
+	FUNCTION(phase_flag6),
+	FUNCTION(phase_flag29),
+	FUNCTION(phase_flag30),
+	FUNCTION(phase_flag31),
+	FUNCTION(mss_lte),
+	FUNCTION(qup0),
+	FUNCTION(gpio),
+	FUNCTION(qup9),
+	FUNCTION(qdss_cti),
+	FUNCTION(ddr_pxi0),
+	FUNCTION(ddr_bist),
+	FUNCTION(atest_tsens2),
+	FUNCTION(vsense_trigger),
+	FUNCTION(atest_usb1),
+	FUNCTION(qup_l4),
+	FUNCTION(wlan1_adc1),
+	FUNCTION(atest_usb13),
+	FUNCTION(ddr_pxi1),
+	FUNCTION(qup_l5),
+	FUNCTION(wlan1_adc0),
+	FUNCTION(atest_usb12),
+	FUNCTION(mdp_vsync),
+	FUNCTION(qup_l6),
+	FUNCTION(wlan2_adc1),
+	FUNCTION(atest_usb11),
+	FUNCTION(ddr_pxi2),
+	FUNCTION(edp_lcd),
+	FUNCTION(dbg_out),
+	FUNCTION(wlan2_adc0),
+	FUNCTION(atest_usb10),
+	FUNCTION(m_voc),
+	FUNCTION(tsif1_sync),
+};
+
+/* Every pin is maintained as a single group, and missing or non-existing pin
+ * would be maintained as dummy group to synchronize pin group index with
+ * pin descriptor registered with pinctrl core.
+ * Clients would not be able to request these dummy pin groups.
+ */
+static const struct msm_pingroup sdm845_groups[] = {
+	[0] = PINGROUP(0, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[1] = PINGROUP(1, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[2] = PINGROUP(2, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[3] = PINGROUP(3, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2,
+		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA, NA),
+	[8] = PINGROUP(8, EAST, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1,
+		       atest_usb13, ddr_pxi1, NA, NA),
+	[9] = PINGROUP(9, EAST, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12,
+		       ddr_pxi1, NA, NA, NA, NA),
+	[10] = PINGROUP(10, EAST, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+			atest_usb11, ddr_pxi2, NA, NA, NA, NA),
+	[11] = PINGROUP(11, EAST, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+			atest_usb10, ddr_pxi2, NA, NA, NA, NA),
+	[12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
+			NA, NA, NA, NA, NA),
+	[13] = PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss_gpio0,
+			ddr_pxi3, NA, NA, NA, NA, NA, NA),
+	[14] = PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
+			NA, NA, NA, NA),
+	[15] = PINGROUP(15, SOUTH, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[16] = PINGROUP(16, SOUTH, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[17] = PINGROUP(17, SOUTH, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
+			NA, NA, NA),
+	[18] = PINGROUP(18, SOUTH, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
+			NA, NA, NA),
+	[19] = PINGROUP(19, SOUTH, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
+			NA, NA, NA),
+	[20] = PINGROUP(20, SOUTH, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
+			NA, NA, NA),
+	[21] = PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
+			NA, NA, NA, NA),
+	[22] = PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
+			NA, NA, NA, NA),
+	[23] = PINGROUP(23, SOUTH, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[24] = PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss_gpio10, NA, NA,
+			NA, NA, NA, NA, NA),
+	[25] = PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss_gpio11, NA, NA,
+			NA, NA, NA, NA, NA),
+	[26] = PINGROUP(26, SOUTH, cci_async, qdss_gpio12, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[27] = PINGROUP(27, EAST, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[28] = PINGROUP(28, EAST, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[29] = PINGROUP(29, EAST, qup2, NA, phase_flag1, qdss_gpio15, NA, NA,
+			NA, NA, NA, NA),
+	[30] = PINGROUP(30, EAST, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
+			NA, NA, NA),
+	[31] = PINGROUP(31, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[32] = PINGROUP(32, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[33] = PINGROUP(33, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[34] = PINGROUP(34, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[35] = PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA,
+			NA, NA, NA),
+	[36] = PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, NA, atest_tsens,
+			NA, NA, NA, NA, NA),
+	[37] = PINGROUP(37, SOUTH, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, EAST, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[40] = PINGROUP(40, SOUTH, sd_write, tsif1_error, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[41] = PINGROUP(41, EAST, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA,
+			NA),
+	[42] = PINGROUP(42, EAST, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA,
+			NA),
+	[43] = PINGROUP(43, EAST, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
+			NA, NA),
+	[44] = PINGROUP(44, EAST, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
+			NA, NA),
+	[45] = PINGROUP(45, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA,
+			NA, NA, NA, NA),
+	[53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[54] = PINGROUP(54, NORTH, qup10, NA, phase_flag12, NA, NA, NA, NA, NA,
+			NA, NA),
+	[55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
+			NA, NA, NA, NA),
+	[58] = PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA,
+			NA, NA, NA, NA),
+	[59] = PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA,
+			NA, NA, NA, NA),
+	[60] = PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag21, NA, NA,
+			NA, NA, NA, NA, NA),
+	[61] = PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag22, NA, NA,
+			NA, NA, NA, NA, NA),
+	[62] = PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag23, qdss_cti,
+			NA, NA, NA, NA, NA, NA),
+	[63] = PINGROUP(63, NORTH, qua_mi2s, NA, phase_flag24, qdss_cti, NA,
+			NA, NA, NA, NA, NA),
+	[64] = PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA,
+			NA, NA, NA, NA),
+	[65] = PINGROUP(65, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[69] = PINGROUP(69, EAST, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[70] = PINGROUP(70, EAST, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[71] = PINGROUP(71, EAST, lpass_slimbus, spkr_i2s, tsense_pwm1,
+			tsense_pwm2, NA, NA, NA, NA, NA, NA),
+	[72] = PINGROUP(72, EAST, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[73] = PINGROUP(73, EAST, btfm_slimbus, atest_usb2, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[74] = PINGROUP(74, EAST, btfm_slimbus, ter_mi2s, phase_flag7,
+			atest_usb23, NA, NA, NA, NA, NA, NA),
+	[75] = PINGROUP(75, EAST, ter_mi2s, phase_flag8, qdss_gpio8,
+			atest_usb22, NA, NA, NA, NA, NA, NA),
+	[76] = PINGROUP(76, EAST, ter_mi2s, phase_flag9, qdss_gpio9,
+			atest_usb21, NA, NA, NA, NA, NA, NA),
+	[77] = PINGROUP(77, EAST, ter_mi2s, phase_flag4, qdss_gpio10,
+			atest_usb20, NA, NA, NA, NA, NA, NA),
+	[78] = PINGROUP(78, EAST, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[79] = PINGROUP(79, NORTH, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA,
+			NA, NA, NA),
+	[80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
+			NA, NA, NA),
+	[81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, qspi_cs, tgu_ch3,
+			phase_flag10, NA, NA, NA, NA, NA),
+	[90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, qspi_cs,
+			mdp_vsync1, mdp_vsync2, mdp_vsync3, tgu_ch0,
+			phase_flag0, qdss_cti),
+	[91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, qspi0, tgu_ch1,
+			NA, qdss_cti, NA, NA, NA),
+	[92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, qspi1, vfr_1,
+			tgu_ch2, NA, NA, NA, NA),
+	[93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, qspi2, NA,
+			qdss_gpio13, NA, NA, NA, NA),
+	[94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, qspi3, NA, NA, NA,
+			NA, NA, NA),
+	[95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, qspi_clk, NA, NA,
+			NA, NA, NA, NA),
+	[96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA,
+			NA, NA, NA, NA, NA),
+	[97] = PINGROUP(97, NORTH, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
+			NA, NA),
+	[98] = PINGROUP(98, NORTH, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+			NA, NA, NA),
+	[99] = PINGROUP(99, NORTH, phase_flag14, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[100] = PINGROUP(100, NORTH, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[101] = PINGROUP(101, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[102] = PINGROUP(102, NORTH, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[103] = PINGROUP(103, NORTH, pci_e1, phase_flag5, NA, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[104] = PINGROUP(104, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[114] = PINGROUP(114, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[115] = PINGROUP(115, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[116] = PINGROUP(116, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+			 NA, NA, NA, NA, NA),
+	[119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[122] = PINGROUP(122, EAST, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[123] = PINGROUP(123, EAST, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[124] = PINGROUP(124, EAST, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[125] = PINGROUP(125, EAST, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[126] = PINGROUP(126, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[127] = PINGROUP(127, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[128] = PINGROUP(128, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[129] = PINGROUP(129, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[130] = PINGROUP(130, NORTH, qlink_request, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[131] = PINGROUP(131, NORTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[132] = PINGROUP(132, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[135] = PINGROUP(135, NORTH, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[136] = PINGROUP(136, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[137] = PINGROUP(137, NORTH, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[138] = PINGROUP(138, NORTH, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[139] = PINGROUP(139, NORTH, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[140] = PINGROUP(140, NORTH, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[141] = PINGROUP(141, NORTH, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[142] = PINGROUP(142, NORTH, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[143] = PINGROUP(143, NORTH, NA, nav_pps, nav_pps, NA, phase_flag31,
+			 NA, NA, NA, NA, NA),
+	[144] = PINGROUP(144, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[145] = PINGROUP(145, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[146] = PINGROUP(146, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[147] = PINGROUP(147, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[148] = PINGROUP(148, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[149] = PINGROUP(149, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[150] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
+	[151] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
+	[152] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
+	[153] = UFS_RESET(ufs_reset, 0x99f000),
+};
+
+static const struct msm_dir_conn sdm845_dir_conn[] = {
+	{1, 510},
+	{3, 511},
+	{5, 512},
+	{10, 513},
+	{11, 514},
+	{20, 515},
+	{22, 516},
+	{24, 517},
+	{26, 518},
+	{30, 519},
+	{31, 639},
+	{32, 521},
+	{34, 522},
+	{36, 523},
+	{37, 524},
+	{38, 525},
+	{39, 526},
+	{40, 527},
+	{41, 637},
+	{43, 529},
+	{44, 530},
+	{46, 531},
+	{48, 532},
+	{49, 640},
+	{52, 534},
+	{53, 535},
+	{54, 536},
+	{56, 537},
+	{57, 538},
+	{58, 539},
+	{59, 540},
+	{60, 541},
+	{61, 542},
+	{62, 543},
+	{63, 544},
+	{64, 545},
+	{66, 546},
+	{68, 547},
+	{71, 548},
+	{73, 549},
+	{77, 550},
+	{78, 551},
+	{79, 552},
+	{80, 553},
+	{84, 554},
+	{85, 555},
+	{86, 556},
+	{88, 557},
+	{89, 638},
+	{91, 559},
+	{92, 560},
+	{95, 561},
+	{96, 562},
+	{97, 563},
+	{101, 564},
+	{103, 565},
+	{104, 566},
+	{115, 570},
+	{116, 571},
+	{117, 572},
+	{118, 573},
+	{119, 609},
+	{120, 610},
+	{121, 611},
+	{122, 612},
+	{123, 613},
+	{124, 614},
+	{125, 615},
+	{126, 616},
+	{127, 617},
+	{128, 618},
+	{129, 619},
+	{130, 620},
+	{132, 621},
+	{133, 622},
+	{145, 623},
+	{0, 216},
+	{0, 215},
+	{0, 214},
+	{0, 213},
+	{0, 212},
+	{0, 211},
+	{0, 210},
+	{0, 209},
+};
+
+static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
+	.pins = sdm845_pins,
+	.npins = ARRAY_SIZE(sdm845_pins),
+	.functions = sdm845_functions,
+	.nfunctions = ARRAY_SIZE(sdm845_functions),
+	.groups = sdm845_groups,
+	.ngroups = ARRAY_SIZE(sdm845_groups),
+	.ngpios = 150,
+	.dir_conn = sdm845_dir_conn,
+	.n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
+	.dir_conn_irq_base = 216,
+};
+
+static int sdm845_pinctrl_probe(struct platform_device *pdev)
+{
+	return msm_pinctrl_probe(pdev, &sdm845_pinctrl);
+}
+
+static const struct of_device_id sdm845_pinctrl_of_match[] = {
+	{ .compatible = "qcom,sdm845-pinctrl-v2", },
+	{ },
+};
+
+static struct platform_driver sdm845_pinctrl_driver = {
+	.driver = {
+		.name = "sdm845-v2-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = sdm845_pinctrl_of_match,
+	},
+	.probe = sdm845_pinctrl_probe,
+	.remove = msm_pinctrl_remove,
+};
+
+static int __init sdm845_pinctrl_init(void)
+{
+	return platform_driver_register(&sdm845_pinctrl_driver);
+}
+arch_initcall(sdm845_pinctrl_init);
+
+static void __exit sdm845_pinctrl_exit(void)
+{
+	platform_driver_unregister(&sdm845_pinctrl_driver);
+}
+module_exit(sdm845_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI sdm845-v2 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, sdm845_pinctrl_of_match);
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
index 8faabb0..7855531 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -25,8 +25,10 @@
 		.ngroups = ARRAY_SIZE(fname##_groups),	\
 	}
 
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
 #define REG_SIZE 0x1000
-#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)	\
 	{						\
 		.name = "gpio" #id,			\
 		.pins = gpio##id##_pins,		\
@@ -41,14 +43,17 @@
 			msm_mux_##f6,			\
 			msm_mux_##f7,			\
 			msm_mux_##f8,			\
-			msm_mux_##f9			\
+			msm_mux_##f9,			\
+			msm_mux_##f10			\
 		},					\
-		.nfuncs = 10,				\
-		.ctl_reg = REG_SIZE * id,		\
-		.io_reg = 0x4 + REG_SIZE * id,		\
-		.intr_cfg_reg = 0x8 + REG_SIZE * id,	\
-		.intr_status_reg = 0xc + REG_SIZE * id,	\
-		.intr_target_reg = 0x8 + REG_SIZE * id,	\
+		.nfuncs = 11,				\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
+		.dir_conn_reg = (base == NORTH) ? base + 0xa5000 :\
+					base + 0xa8000, \
 		.mux_bit = 2,			\
 		.pull_bit = 0,			\
 		.drv_bit = 6,			\
@@ -63,6 +68,7 @@
 		.intr_polarity_bit = 1,		\
 		.intr_detection_bit = 2,	\
 		.intr_detection_width = 2,	\
+		.dir_conn_en_bit = 8,	        \
 	}
 
 #define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
@@ -115,9 +121,6 @@
 		.intr_detection_width = -1,		\
 	}
 
-static const u32 sdm845_tile_offsets[] = {0x500000, 0x900000, 0x100000};
-static u32 sdm845_pin_base[150];
-
 static const struct pinctrl_pin_desc sdm845_pins[] = {
 	PINCTRL_PIN(0, "GPIO_0"),
 	PINCTRL_PIN(1, "GPIO_1"),
@@ -1371,262 +1374,285 @@
  * Clients would not be able to request these dummy pin groups.
  */
 static const struct msm_pingroup sdm845_groups[] = {
-	[0] = PINGROUP(0, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[1] = PINGROUP(1, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[2] = PINGROUP(2, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[3] = PINGROUP(3, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[4] = PINGROUP(4, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[5] = PINGROUP(5, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[6] = PINGROUP(6, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA),
-	[7] = PINGROUP(7, qup9, ddr_bist, NA, atest_tsens2,
-		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA),
-	[8] = PINGROUP(8, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1,
-		       atest_usb13, ddr_pxi1, NA),
-	[9] = PINGROUP(9, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12,
-		       ddr_pxi1, NA, NA, NA),
-	[10] = PINGROUP(10, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
-			atest_usb11, ddr_pxi2, NA, NA, NA),
-	[11] = PINGROUP(11, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
-			atest_usb10, ddr_pxi2, NA, NA, NA),
-	[12] = PINGROUP(12, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
-			NA, NA, NA, NA),
-	[13] = PINGROUP(13, cam_mclk, pll_bypassnl, qdss_gpio0,
-			ddr_pxi3, NA, NA, NA, NA, NA),
-	[14] = PINGROUP(14, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
-			NA, NA, NA),
-	[15] = PINGROUP(15, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA,
-			NA, NA),
-	[16] = PINGROUP(16, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA,
-			NA, NA),
-	[17] = PINGROUP(17, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
-			NA, NA),
-	[18] = PINGROUP(18, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
-			NA, NA),
-	[19] = PINGROUP(19, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
-			NA, NA),
-	[20] = PINGROUP(20, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
-			NA, NA),
-	[21] = PINGROUP(21, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
-			NA, NA, NA),
-	[22] = PINGROUP(22, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
-			NA, NA, NA),
-	[23] = PINGROUP(23, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
-			NA, NA),
-	[24] = PINGROUP(24, cci_timer3, cci_async, qdss_gpio10, NA, NA,
-			NA, NA, NA, NA),
-	[25] = PINGROUP(25, cci_timer4, cci_async, qdss_gpio11, NA, NA,
-			NA, NA, NA, NA),
-	[26] = PINGROUP(26, cci_async, qdss_gpio12, NA, NA, NA, NA, NA,
-			NA, NA),
-	[27] = PINGROUP(27, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA,
-			NA),
-	[28] = PINGROUP(28, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA,
-			NA),
-	[29] = PINGROUP(29, qup2, NA, phase_flag1, qdss_gpio15, NA, NA,
-			NA, NA, NA),
-	[30] = PINGROUP(30, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
-			NA, NA),
-	[31] = PINGROUP(31, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[32] = PINGROUP(32, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[33] = PINGROUP(33, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[34] = PINGROUP(34, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[35] = PINGROUP(35, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA,
-			NA, NA),
-	[36] = PINGROUP(36, pci_e0, qup_l5, pll_bist, NA, atest_tsens,
-			NA, NA, NA, NA),
-	[37] = PINGROUP(37, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA,
-			NA),
-	[38] = PINGROUP(38, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA),
-	[39] = PINGROUP(39, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
-			NA),
-	[40] = PINGROUP(40, sd_write, tsif1_error, NA, NA, NA, NA, NA,
-			NA, NA),
-	[41] = PINGROUP(41, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA),
-	[42] = PINGROUP(42, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA),
-	[43] = PINGROUP(43, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
-			NA),
-	[44] = PINGROUP(44, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
-			NA),
-	[45] = PINGROUP(45, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[46] = PINGROUP(46, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[47] = PINGROUP(47, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[48] = PINGROUP(48, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[49] = PINGROUP(49, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[50] = PINGROUP(50, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[51] = PINGROUP(51, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[52] = PINGROUP(52, qup12, phase_flag16, qdss_cti, NA, NA, NA,
-			NA, NA, NA),
-	[53] = PINGROUP(53, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
-			NA),
-	[54] = PINGROUP(54, qup10, NA, phase_flag12, NA, NA, NA, NA, NA,
-			NA),
-	[55] = PINGROUP(55, qup10, phase_flag13, NA, NA, NA, NA, NA, NA,
-			NA),
-	[56] = PINGROUP(56, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
-			NA),
-	[57] = PINGROUP(57, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
-			NA, NA, NA),
-	[58] = PINGROUP(58, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA,
-			NA, NA, NA),
-	[59] = PINGROUP(59, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA,
-			NA, NA, NA),
-	[60] = PINGROUP(60, qua_mi2s, cri_trng0, phase_flag21, NA, NA,
-			NA, NA, NA, NA),
-	[61] = PINGROUP(61, qua_mi2s, cri_trng1, phase_flag22, NA, NA,
-			NA, NA, NA, NA),
-	[62] = PINGROUP(62, qua_mi2s, cri_trng, phase_flag23, qdss_cti,
+	[0] = PINGROUP(0, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[1] = PINGROUP(1, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[2] = PINGROUP(2, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[3] = PINGROUP(3, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2,
+		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA, NA),
+	[8] = PINGROUP(8, NORTH, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1,
+		       atest_usb13, ddr_pxi1, NA, NA),
+	[9] = PINGROUP(9, NORTH, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12,
+		       ddr_pxi1, NA, NA, NA, NA),
+	[10] = PINGROUP(10, NORTH, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+			atest_usb11, ddr_pxi2, NA, NA, NA, NA),
+	[11] = PINGROUP(11, NORTH, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+			atest_usb10, ddr_pxi2, NA, NA, NA, NA),
+	[12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
 			NA, NA, NA, NA, NA),
-	[63] = PINGROUP(63, qua_mi2s, NA, phase_flag24, qdss_cti, NA,
+	[13] = PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss_gpio0,
+			ddr_pxi3, NA, NA, NA, NA, NA, NA),
+	[14] = PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
 			NA, NA, NA, NA),
-	[64] = PINGROUP(64, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA,
+	[15] = PINGROUP(15, SOUTH, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA,
 			NA, NA, NA),
-	[65] = PINGROUP(65, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA),
-	[66] = PINGROUP(66, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA,
-			NA),
-	[67] = PINGROUP(67, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA),
-	[68] = PINGROUP(68, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA),
-	[69] = PINGROUP(69, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA,
-			NA),
-	[70] = PINGROUP(70, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
-			NA, NA),
-	[71] = PINGROUP(71, lpass_slimbus, spkr_i2s, tsense_pwm1,
-			tsense_pwm2, NA, NA, NA, NA, NA),
-	[72] = PINGROUP(72, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
-			NA, NA),
-	[73] = PINGROUP(73, btfm_slimbus, atest_usb2, NA, NA, NA, NA, NA,
-			NA, NA),
-	[74] = PINGROUP(74, btfm_slimbus, ter_mi2s, phase_flag7,
-			atest_usb23, NA, NA, NA, NA, NA),
-	[75] = PINGROUP(75, ter_mi2s, phase_flag8, qdss_gpio8,
-			atest_usb22, NA, NA, NA, NA, NA),
-	[76] = PINGROUP(76, ter_mi2s, phase_flag9, qdss_gpio9,
-			atest_usb21, NA, NA, NA, NA, NA),
-	[77] = PINGROUP(77, ter_mi2s, phase_flag4, qdss_gpio10,
-			atest_usb20, NA, NA, NA, NA, NA),
-	[78] = PINGROUP(78, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
-			NA),
-	[79] = PINGROUP(79, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA,
-			NA, NA),
-	[80] = PINGROUP(80, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
-			NA, NA),
-	[81] = PINGROUP(81, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[82] = PINGROUP(82, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[83] = PINGROUP(83, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[84] = PINGROUP(84, qup15, NA, NA, NA, NA, NA, NA, NA, NA),
-	[85] = PINGROUP(85, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[86] = PINGROUP(86, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[87] = PINGROUP(87, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[88] = PINGROUP(88, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[89] = PINGROUP(89, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
+	[16] = PINGROUP(16, SOUTH, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[17] = PINGROUP(17, SOUTH, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
+			NA, NA, NA),
+	[18] = PINGROUP(18, SOUTH, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
+			NA, NA, NA),
+	[19] = PINGROUP(19, SOUTH, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
+			NA, NA, NA),
+	[20] = PINGROUP(20, SOUTH, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
+			NA, NA, NA),
+	[21] = PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
 			NA, NA, NA, NA),
-	[90] = PINGROUP(90, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
-			mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti),
-	[91] = PINGROUP(91, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
-			qdss_cti, NA, NA, NA),
-	[92] = PINGROUP(92, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
+	[22] = PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
 			NA, NA, NA, NA),
-	[93] = PINGROUP(93, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
-			NA, NA, NA, NA),
-	[94] = PINGROUP(94, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
-			NA),
-	[95] = PINGROUP(95, tsif2_data, sdc41, qup7, NA, NA, NA, NA, NA,
-			NA),
-	[96] = PINGROUP(96, tsif2_sync, sdc40, qup7, phase_flag3, NA,
-			NA, NA, NA, NA),
-	[97] = PINGROUP(97, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
-			NA),
-	[98] = PINGROUP(98, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+	[23] = PINGROUP(23, SOUTH, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[24] = PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss_gpio10, NA, NA,
+			NA, NA, NA, NA, NA),
+	[25] = PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss_gpio11, NA, NA,
+			NA, NA, NA, NA, NA),
+	[26] = PINGROUP(26, SOUTH, cci_async, qdss_gpio12, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[27] = PINGROUP(27, NORTH, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA,
 			NA, NA),
-	[99] = PINGROUP(99, phase_flag14, NA, NA, NA, NA, NA, NA, NA,
+	[28] = PINGROUP(28, NORTH, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[29] = PINGROUP(29, NORTH, qup2, NA, phase_flag1, qdss_gpio15, NA, NA,
+			NA, NA, NA, NA),
+	[30] = PINGROUP(30, NORTH, qup2, phase_flag2, qdss_gpio, NA, NA, NA,
+			NA, NA, NA, NA),
+	[31] = PINGROUP(31, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
 			NA),
-	[100] = PINGROUP(100, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[101] = PINGROUP(101, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[102] = PINGROUP(102, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[103] = PINGROUP(103, pci_e1, phase_flag5, NA, NA, NA, NA, NA,
+	[32] = PINGROUP(32, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[33] = PINGROUP(33, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[34] = PINGROUP(34, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[35] = PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA,
+			NA, NA, NA),
+	[36] = PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, NA, atest_tsens,
+			NA, NA, NA, NA, NA),
+	[37] = PINGROUP(37, SOUTH, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, NORTH, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[40] = PINGROUP(40, SOUTH, sd_write, tsif1_error, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[41] = PINGROUP(41, SOUTH, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA,
+			NA, NA),
+	[42] = PINGROUP(42, SOUTH, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA,
+			NA, NA),
+	[43] = PINGROUP(43, SOUTH, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
+			NA, NA),
+	[44] = PINGROUP(44, SOUTH, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
+			NA, NA),
+	[45] = PINGROUP(45, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA,
+			NA, NA, NA, NA),
+	[53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[54] = PINGROUP(54, NORTH, qup10, NA, phase_flag12, NA, NA, NA, NA, NA,
+			NA, NA),
+	[55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
+			NA, NA, NA, NA),
+	[58] = PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA,
+			NA, NA, NA, NA),
+	[59] = PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA,
+			NA, NA, NA, NA),
+	[60] = PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag21, NA, NA,
+			NA, NA, NA, NA, NA),
+	[61] = PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag22, NA, NA,
+			NA, NA, NA, NA, NA),
+	[62] = PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag23, qdss_cti,
+			NA, NA, NA, NA, NA, NA),
+	[63] = PINGROUP(63, NORTH, qua_mi2s, NA, phase_flag24, qdss_cti, NA,
+			NA, NA, NA, NA, NA),
+	[64] = PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA,
+			NA, NA, NA, NA),
+	[65] = PINGROUP(65, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[69] = PINGROUP(69, NORTH, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[70] = PINGROUP(70, NORTH, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[71] = PINGROUP(71, NORTH, lpass_slimbus, spkr_i2s, tsense_pwm1,
+			tsense_pwm2, NA, NA, NA, NA, NA, NA),
+	[72] = PINGROUP(72, NORTH, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[73] = PINGROUP(73, NORTH, btfm_slimbus, atest_usb2, NA, NA, NA, NA,
+			NA, NA, NA, NA),
+	[74] = PINGROUP(74, NORTH, btfm_slimbus, ter_mi2s, phase_flag7,
+			atest_usb23, NA, NA, NA, NA, NA, NA),
+	[75] = PINGROUP(75, NORTH, ter_mi2s, phase_flag8, qdss_gpio8,
+			atest_usb22, NA, NA, NA, NA, NA, NA),
+	[76] = PINGROUP(76, NORTH, ter_mi2s, phase_flag9, qdss_gpio9,
+			atest_usb21, NA, NA, NA, NA, NA, NA),
+	[77] = PINGROUP(77, NORTH, ter_mi2s, phase_flag4, qdss_gpio10,
+			atest_usb20, NA, NA, NA, NA, NA, NA),
+	[78] = PINGROUP(78, NORTH, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[79] = PINGROUP(79, NORTH, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA,
+			NA, NA, NA),
+	[80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
+			NA, NA, NA),
+	[81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
+			NA, NA, NA, NA, NA),
+	[90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
+			mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti,
+			NA),
+	[91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
+			qdss_cti, NA, NA, NA, NA),
+	[92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
+			NA, NA, NA, NA, NA),
+	[93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
+			NA, NA, NA, NA, NA),
+	[94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
+			NA, NA),
+	[95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, NA, NA, NA, NA, NA,
+			NA, NA),
+	[96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA,
+			NA, NA, NA, NA, NA),
+	[97] = PINGROUP(97, NORTH, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
+			NA, NA),
+	[98] = PINGROUP(98, NORTH, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+			NA, NA, NA),
+	[99] = PINGROUP(99, NORTH, phase_flag14, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[100] = PINGROUP(100, NORTH, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
 			 NA, NA),
-	[104] = PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[105] = PINGROUP(105, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+	[101] = PINGROUP(101, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[102] = PINGROUP(102, NORTH, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
 			 NA, NA),
-	[106] = PINGROUP(106, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
-			 NA, NA),
-	[107] = PINGROUP(107, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
-			 NA, NA),
-	[108] = PINGROUP(108, uim2_present, qup13, NA, NA, NA, NA, NA,
-			 NA, NA),
-	[109] = PINGROUP(109, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
-	[110] = PINGROUP(110, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
-	[111] = PINGROUP(111, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[112] = PINGROUP(112, uim1_present, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[113] = PINGROUP(113, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[114] = PINGROUP(114, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
-			 NA),
-	[115] = PINGROUP(115, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
-			 NA),
-	[116] = PINGROUP(116, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[117] = PINGROUP(117, NA, qdss_gpio0, atest_char, NA, NA, NA,
+	[103] = PINGROUP(103, NORTH, pci_e1, phase_flag5, NA, NA, NA, NA, NA,
 			 NA, NA, NA),
-	[118] = PINGROUP(118, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+	[104] = PINGROUP(104, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[114] = PINGROUP(114, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[115] = PINGROUP(115, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[116] = PINGROUP(116, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA,
 			 NA, NA, NA, NA),
-	[119] = PINGROUP(119, NA, qdss_gpio2, atest_char2, NA, NA, NA,
-			 NA, NA, NA),
-	[120] = PINGROUP(120, NA, qdss_gpio3, atest_char1, NA, NA, NA,
-			 NA, NA, NA),
-	[121] = PINGROUP(121, NA, qdss_gpio4, atest_char0, NA, NA, NA,
-			 NA, NA, NA),
-	[122] = PINGROUP(122, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA, NA),
-	[123] = PINGROUP(123, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
-			 NA),
-	[124] = PINGROUP(124, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
-			 NA),
-	[125] = PINGROUP(125, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[126] = PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[127] = PINGROUP(127, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[128] = PINGROUP(128, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[129] = PINGROUP(129, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[130] = PINGROUP(130, qlink_request, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[131] = PINGROUP(131, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[132] = PINGROUP(132, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[133] = PINGROUP(133, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[134] = PINGROUP(134, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[135] = PINGROUP(135, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[136] = PINGROUP(136, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[137] = PINGROUP(137, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
-			 NA),
-	[138] = PINGROUP(138, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
-			 NA),
-	[139] = PINGROUP(139, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[140] = PINGROUP(140, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
-			 NA),
-	[141] = PINGROUP(141, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[142] = PINGROUP(142, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[143] = PINGROUP(143, NA, nav_pps, nav_pps, NA, phase_flag31,
+	[118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+			 NA, NA, NA, NA, NA),
+	[119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA,
 			 NA, NA, NA, NA),
-	[144] = PINGROUP(144, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
-	[145] = PINGROUP(145, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
-	[146] = PINGROUP(146, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[147] = PINGROUP(147, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[148] = PINGROUP(148, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[149] = PINGROUP(149, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[122] = PINGROUP(122, NORTH, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[123] = PINGROUP(123, NORTH, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[124] = PINGROUP(124, NORTH, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[125] = PINGROUP(125, NORTH, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[126] = PINGROUP(126, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[127] = PINGROUP(127, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[128] = PINGROUP(128, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[129] = PINGROUP(129, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[130] = PINGROUP(130, NORTH, qlink_request, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[131] = PINGROUP(131, NORTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[132] = PINGROUP(132, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[135] = PINGROUP(135, NORTH, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[136] = PINGROUP(136, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[137] = PINGROUP(137, NORTH, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[138] = PINGROUP(138, NORTH, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[139] = PINGROUP(139, NORTH, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[140] = PINGROUP(140, NORTH, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[141] = PINGROUP(141, NORTH, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[142] = PINGROUP(142, NORTH, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[143] = PINGROUP(143, NORTH, NA, nav_pps, nav_pps, NA, phase_flag31,
+			 NA, NA, NA, NA, NA),
+	[144] = PINGROUP(144, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[145] = PINGROUP(145, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[146] = PINGROUP(146, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[147] = PINGROUP(147, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[148] = PINGROUP(148, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[149] = PINGROUP(149, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
 	[150] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
 	[151] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
 	[152] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
 	[153] = UFS_RESET(ufs_reset, 0x99f000),
 };
 
-static const struct msm_dir_conn sdm845_dir_conn[] = {
+static struct msm_dir_conn sdm845_dir_conn[] = {
 	{1, 510},
 	{3, 511},
 	{5, 512},
@@ -1695,6 +1721,7 @@
 	{123, 613},
 	{124, 614},
 	{125, 615},
+	{126, 616},
 	{127, 617},
 	{128, 618},
 	{129, 619},
@@ -1702,6 +1729,14 @@
 	{132, 621},
 	{133, 622},
 	{145, 623},
+	{0, 216},
+	{0, 215},
+	{0, 214},
+	{0, 213},
+	{0, 212},
+	{0, 211},
+	{0, 210},
+	{0, 209},
 };
 
 static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
@@ -1714,10 +1749,7 @@
 	.ngpios = 150,
 	.dir_conn = sdm845_dir_conn,
 	.n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
-	.tile_offsets = sdm845_tile_offsets,
-	.n_tile_offsets = ARRAY_SIZE(sdm845_tile_offsets),
-	.pin_base = sdm845_pin_base,
-	.reg_size = REG_SIZE,
+	.dir_conn_irq_base = 216,
 };
 
 static int sdm845_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 70d74f0..6230356 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -536,6 +536,7 @@
 	int retval;
 	struct ipa_wan_msg *wan_msg;
 	struct ipa_msg_meta msg_meta;
+	struct ipa_wan_msg cache_wan_msg;
 
 	wan_msg = kzalloc(sizeof(struct ipa_wan_msg), GFP_KERNEL);
 	if (!wan_msg) {
@@ -549,6 +550,8 @@
 		return -EFAULT;
 	}
 
+	memcpy(&cache_wan_msg, wan_msg, sizeof(cache_wan_msg));
+
 	memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
 	msg_meta.msg_type = msg_type;
 	msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -565,8 +568,8 @@
 		/* cache the cne event */
 		memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
 			ipa_ctx->num_ipa_cne_evt_req].wan_msg,
-			wan_msg,
-			sizeof(struct ipa_wan_msg));
+			&cache_wan_msg,
+			sizeof(cache_wan_msg));
 
 		memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
 			ipa_ctx->num_ipa_cne_evt_req].msg_meta,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index 57e0a53..2608e1d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -655,9 +655,8 @@
 
 	/* check if the filter rules from IPACM is valid */
 	if (req->filter_index_list_len == 0) {
-		IPAWANERR(" delete UL filter rule for pipe %d\n",
+		IPAWANDBG(" delete UL filter rule for pipe %d\n",
 		req->source_pipe_index);
-		return -EINVAL;
 	} else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) {
 		IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
 		req->source_pipe_index,
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 2f272d2..39a1cc2 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -2693,6 +2693,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 +2985,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 +3023,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 +3070,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/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 097eec8..180d03eb 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -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)
@@ -545,6 +537,7 @@
 	int retval;
 	struct ipa_wan_msg *wan_msg;
 	struct ipa_msg_meta msg_meta;
+	struct ipa_wan_msg cache_wan_msg;
 
 	wan_msg = kzalloc(sizeof(struct ipa_wan_msg), GFP_KERNEL);
 	if (!wan_msg) {
@@ -558,6 +551,8 @@
 		return -EFAULT;
 	}
 
+	memcpy(&cache_wan_msg, wan_msg, sizeof(cache_wan_msg));
+
 	memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
 	msg_meta.msg_type = msg_type;
 	msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -574,8 +569,8 @@
 		/* cache the cne event */
 		memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
 			ipa3_ctx->num_ipa_cne_evt_req].wan_msg,
-			wan_msg,
-			sizeof(struct ipa_wan_msg));
+			&cache_wan_msg,
+			sizeof(cache_wan_msg));
 
 		memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
 			ipa3_ctx->num_ipa_cne_evt_req].msg_meta,
@@ -4427,8 +4422,6 @@
 	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");
@@ -4640,10 +4633,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 {
+		for (i = 0; i < IPA_SMMU_CB_MAX; i++)
+			ipa3_ctx->s1_bypass_arr[i] = smmu_info.s1_bypass_arr[i];
+	}
+
 	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;
@@ -5486,7 +5483,8 @@
 	}
 	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;
 		if (iommu_domain_set_attr(cb->iommu,
 					DOMAIN_ATTR_S1_BYPASS,
 					&bypass)) {
@@ -5494,8 +5492,9 @@
 			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;
 		if (iommu_domain_set_attr(cb->iommu,
 					DOMAIN_ATTR_ATOMIC,
 					&atomic_ctx)) {
@@ -5503,7 +5502,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,
@@ -5517,6 +5516,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);
@@ -5604,20 +5606,23 @@
 	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;
 		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;
 		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;
@@ -5627,8 +5632,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;
@@ -5638,6 +5643,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) {
@@ -5704,7 +5712,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)) {
@@ -5713,8 +5723,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)) {
@@ -5723,7 +5734,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,
@@ -5736,6 +5747,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);
@@ -5919,17 +5933,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..c9a0b50 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();
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 7f30a10..cd19a91 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);
 			}
 		}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 3e1d188..a74c924 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1098,6 +1098,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 +1303,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;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
index 11c1737..3bf0327 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
@@ -425,7 +425,13 @@
 	activate_client(client->hdl);
 
 	mutex_lock(&ipa_pm_ctx->client_mutex);
-	client->callback(client->callback_params, IPA_PM_CLIENT_ACTIVATED);
+	if (client->callback) {
+		client->callback(client->callback_params,
+			IPA_PM_CLIENT_ACTIVATED);
+	} else {
+		IPA_PM_ERR("client has no callback");
+		WARN_ON(1);
+	}
 	mutex_unlock(&ipa_pm_ctx->client_mutex);
 
 	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
@@ -677,8 +683,7 @@
 {
 	struct ipa_pm_client *client;
 
-	if (params == NULL || hdl == NULL || params->name == NULL
-		|| params->callback == NULL) {
+	if (params == NULL || hdl == NULL || params->name == NULL) {
 		IPA_PM_ERR("Invalid Params\n");
 		return -EINVAL;
 	}
@@ -1115,9 +1120,14 @@
 		if (pipe_bitmask & (1 << i)) {
 			client = ipa_pm_ctx->clients_by_pipe[i];
 			if (client && client_notified[client->hdl] == false) {
-				client->callback(client->callback_params,
-					IPA_PM_REQUEST_WAKEUP);
-				client_notified[client->hdl] = true;
+				if (client->callback) {
+					client->callback(client->callback_params
+						, IPA_PM_REQUEST_WAKEUP);
+					client_notified[client->hdl] = true;
+				} else {
+					IPA_PM_ERR("client has no callback");
+					WARN_ON(1);
+				}
 			}
 		}
 	}
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 4e8c233..fbaa4ae 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -798,9 +798,8 @@
 
 	/* check if the filter rules from IPACM is valid */
 	if (req->rule_id_len == 0) {
-		IPAWANERR(" delete UL filter rule for pipe %d\n",
+		IPAWANDBG(" delete UL filter rule for pipe %d\n",
 		req->source_pipe_index);
-		return -EINVAL;
 	} else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) {
 		IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
 		req->source_pipe_index,
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/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index f08e1e3..e5791b1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -2984,6 +2984,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 +3279,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 +3317,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 +3364,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/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 92321ad..8495787 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -102,7 +102,6 @@
 struct usb_bam_ctx_type {
 	struct usb_bam_sps_type		usb_bam_sps;
 	struct resource			*io_res;
-	void __iomem			*regs;
 	int				irq;
 	struct platform_device		*usb_bam_pdev;
 	struct workqueue_struct		*usb_bam_wq;
@@ -112,6 +111,7 @@
 	u32				inactivity_timer_ms;
 	bool				is_bam_inactivity;
 	struct usb_bam_pipe_connect	*usb_bam_connections;
+	struct msm_usb_bam_data *usb_bam_data;
 	spinlock_t		usb_bam_lock;
 };
 
@@ -125,13 +125,13 @@
  * CI_CTRL & DWC3_CTRL shouldn't be used simultaneously
  * since both share the same prod & cons rm resourses
  */
-static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_prod[MAX_BAMS] = {
 	[CI_CTRL] = IPA_RM_RESOURCE_USB_PROD,
 	[HSIC_CTRL]  = IPA_RM_RESOURCE_HSIC_PROD,
 	[DWC3_CTRL] = IPA_RM_RESOURCE_USB_PROD,
 };
 
-static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_cons[MAX_BAMS] = {
 	[CI_CTRL] = IPA_RM_RESOURCE_USB_CONS,
 	[HSIC_CTRL]  = IPA_RM_RESOURCE_HSIC_CONS,
 	[DWC3_CTRL] = IPA_RM_RESOURCE_USB_CONS,
@@ -319,8 +319,6 @@
 {
 	int ret = 0;
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[pipe_connect->bam_type];
-	struct msm_usb_bam_platform_data *pdata =
-				ctx->usb_bam_pdev->dev.platform_data;
 	struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf);
 	struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf);
 
@@ -359,7 +357,7 @@
 		}
 
 		data_buf->phys_base = pipe_connect->data_fifo_base_offset +
-						pdata->usb_bam_fifo_baseaddr;
+				ctx->usb_bam_data->usb_bam_fifo_baseaddr;
 		data_buf->size = pipe_connect->data_fifo_size;
 		data_buf->base = ioremap(data_buf->phys_base, data_buf->size);
 		if (!data_buf->base) {
@@ -371,7 +369,7 @@
 		memset_io(data_buf->base, 0, data_buf->size);
 
 		desc_buf->phys_base = pipe_connect->desc_fifo_base_offset +
-						pdata->usb_bam_fifo_baseaddr;
+				ctx->usb_bam_data->usb_bam_fifo_baseaddr;
 		desc_buf->size = pipe_connect->desc_fifo_size;
 		desc_buf->base = ioremap(desc_buf->phys_base, desc_buf->size);
 		if (!desc_buf->base) {
@@ -1068,7 +1066,6 @@
 	struct usb_bam_pipe_connect *pipe_connect =
 				&ctx->usb_bam_connections[idx];
 	struct device *bam_dev = &ctx->usb_bam_pdev->dev;
-	struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
 	enum usb_bam_mode cur_mode;
 
 	if (pipe_connect->enabled) {
@@ -1094,7 +1091,7 @@
 
 	spin_lock(&ctx->usb_bam_lock);
 	/* Check if BAM requires RESET before connect and reset of first pipe */
-	if ((pdata->reset_on_connect == true) &&
+	if ((ctx->usb_bam_data->reset_on_connect == true) &&
 			    (ctx->pipes_enabled_per_bam == 0)) {
 		spin_unlock(&ctx->usb_bam_lock);
 
@@ -1595,8 +1592,6 @@
 static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam)
 {
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
-	struct msm_usb_bam_platform_data *pdata =
-					ctx->usb_bam_pdev->dev.platform_data;
 	struct ipa_rm_create_params usb_prod_create_params;
 	struct ipa_rm_create_params usb_cons_create_params;
 	int ret;
@@ -1605,7 +1600,8 @@
 	memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params));
 	usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam];
 	usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb;
-	usb_prod_create_params.reg_params.user_data = &pdata->bam_type;
+	usb_prod_create_params.reg_params.user_data
+					 = &ctx->usb_bam_data->bam_type;
 	usb_prod_create_params.floor_voltage = IPA_VOLTAGE_SVS;
 	ret = ipa_rm_create_resource(&usb_prod_create_params);
 	if (ret) {
@@ -1628,6 +1624,22 @@
 	}
 }
 
+static void usb_bam_ipa_delete_resources(enum usb_ctrl cur_bam)
+{
+	int ret;
+
+	ret = ipa_rm_delete_resource(ipa_rm_resource_prod[cur_bam]);
+	if (ret)
+		log_event_err("%s: Failed to delete USB_PROD resource\n",
+							__func__);
+
+	ret = ipa_rm_delete_resource(ipa_rm_resource_cons[cur_bam]);
+	if (ret)
+		log_event_err("%s: Failed to delete USB_CONS resource\n",
+							__func__);
+
+}
+
 static void wait_for_prod_granted(enum usb_ctrl cur_bam)
 {
 	int ret;
@@ -2134,16 +2146,14 @@
 	int ret;
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
 	struct ipa_rm_perf_profile ipa_rm_perf_prof;
-	struct msm_usb_bam_platform_data *pdata =
-					ctx->usb_bam_pdev->dev.platform_data;
 
 	if (usb_connection_speed == USB_SPEED_SUPER)
 		ipa_rm_perf_prof.max_supported_bandwidth_mbps =
-			pdata->max_mbps_superspeed;
+			ctx->usb_bam_data->max_mbps_superspeed;
 	else
 		/* Bam2Bam is supported only for SS and HS (HW limitation) */
 		ipa_rm_perf_prof.max_supported_bandwidth_mbps =
-			pdata->max_mbps_highspeed;
+			ctx->usb_bam_data->max_mbps_highspeed;
 
 	/*
 	 * Having a max mbps property in dtsi file is a must
@@ -2180,7 +2190,6 @@
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
 	struct usb_bam_pipe_connect *pipe_connect;
 	struct device *bam_dev = &ctx->usb_bam_pdev->dev;
-	struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
 	int ret;
 	bool bam2bam, is_dpl;
 
@@ -2256,7 +2265,8 @@
 
 	/* Check if BAM requires RESET before connect and reset first pipe */
 	spin_lock(&ctx->usb_bam_lock);
-	if (pdata->reset_on_connect && !ctx->pipes_enabled_per_bam) {
+	if (ctx->usb_bam_data->reset_on_connect &&
+		!ctx->pipes_enabled_per_bam) {
 		spin_unlock(&ctx->usb_bam_lock);
 		if (cur_bam == CI_CTRL)
 			msm_hw_bam_disable(1);
@@ -2606,8 +2616,6 @@
 	struct usb_bam_pipe_connect *pipe_connect;
 	struct device *bam_dev = &ctx->usb_bam_pdev->dev;
 	int ret;
-	struct msm_usb_bam_platform_data *pdata =
-				ctx->usb_bam_pdev->dev.platform_data;
 
 	pipe_connect = &ctx->usb_bam_connections[idx];
 
@@ -2635,7 +2643,8 @@
 
 	log_event_dbg("%s: success disconnecting pipe %d\n", __func__, idx);
 
-	if (pdata->reset_on_disconnect && !ctx->pipes_enabled_per_bam) {
+	if (ctx->usb_bam_data->reset_on_disconnect
+				&& !ctx->pipes_enabled_per_bam) {
 		if (bam_type == CI_CTRL)
 			msm_hw_bam_disable(1);
 
@@ -2803,10 +2812,10 @@
 	}
 }
 
-static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
+static struct msm_usb_bam_data *usb_bam_dt_to_data(
 	struct platform_device *pdev, u32 usb_addr)
 {
-	struct msm_usb_bam_platform_data *pdata;
+	struct msm_usb_bam_data *usb_bam_data;
 	struct device_node *node = pdev->dev.of_node;
 	int rc = 0;
 	u8 i = 0;
@@ -2815,8 +2824,9 @@
 	u32 threshold, max_connections = 0;
 	static struct usb_bam_pipe_connect *usb_bam_connections;
 
-	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
+	usb_bam_data = devm_kzalloc(&pdev->dev, sizeof(*usb_bam_data),
+								GFP_KERNEL);
+	if (!usb_bam_data)
 		return NULL;
 
 	rc = of_property_read_u32(node, "qcom,bam-type", &bam);
@@ -2830,7 +2840,7 @@
 			__func__, bam);
 		return NULL;
 	}
-	pdata->bam_type = bam;
+	usb_bam_data->bam_type = bam;
 
 	rc = of_property_read_u32(node, "qcom,bam-mode", &bam_mode);
 	if (rc) {
@@ -2840,49 +2850,49 @@
 		bam_mode = USB_BAM_DEVICE;
 	}
 
-	pdata->reset_on_connect = of_property_read_bool(node,
+	usb_bam_data->reset_on_connect = of_property_read_bool(node,
 					"qcom,reset-bam-on-connect");
 
-	pdata->reset_on_disconnect = of_property_read_bool(node,
+	usb_bam_data->reset_on_disconnect = of_property_read_bool(node,
 					"qcom,reset-bam-on-disconnect");
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-num-pipes",
-		&pdata->usb_bam_num_pipes);
+		&usb_bam_data->usb_bam_num_pipes);
 	if (rc) {
 		log_event_err("Invalid usb bam num pipes property\n");
 		return NULL;
 	}
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-highspeed",
-		&pdata->max_mbps_highspeed);
+		&usb_bam_data->max_mbps_highspeed);
 	if (rc)
-		pdata->max_mbps_highspeed = 0;
+		usb_bam_data->max_mbps_highspeed = 0;
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-superspeed",
-		&pdata->max_mbps_superspeed);
+		&usb_bam_data->max_mbps_superspeed);
 	if (rc)
-		pdata->max_mbps_superspeed = 0;
+		usb_bam_data->max_mbps_superspeed = 0;
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr);
 	if (rc)
 		pr_debug("%s: Invalid usb base address property\n", __func__);
 	else
-		pdata->usb_bam_fifo_baseaddr = addr;
+		usb_bam_data->usb_bam_fifo_baseaddr = addr;
 
-	pdata->ignore_core_reset_ack = of_property_read_bool(node,
+	usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node,
 		"qcom,ignore-core-reset-ack");
 
-	pdata->disable_clk_gating = of_property_read_bool(node,
+	usb_bam_data->disable_clk_gating = of_property_read_bool(node,
 		"qcom,disable-clk-gating");
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-override-threshold",
 			&threshold);
 	if (rc)
-		pdata->override_threshold = USB_THRESHOLD;
+		usb_bam_data->override_threshold = USB_THRESHOLD;
 	else
-		pdata->override_threshold = threshold;
+		usb_bam_data->override_threshold = threshold;
 
-	pdata->enable_hsusb_bam_on_boot = of_property_read_bool(node,
+	usb_bam_data->enable_hsusb_bam_on_boot = of_property_read_bool(node,
 		"qcom,enable-hsusb-bam-on-boot");
 
 	for_each_child_of_node(pdev->dev.of_node, node)
@@ -2918,7 +2928,7 @@
 			goto err;
 
 		if (usb_bam_connections[i].mem_type == OCI_MEM) {
-			if (!pdata->usb_bam_fifo_baseaddr) {
+			if (!usb_bam_data->usb_bam_fifo_baseaddr) {
 				log_event_err("%s: base address is missing\n",
 					__func__);
 				goto err;
@@ -3002,7 +3012,7 @@
 	msm_usb_bam[bam].usb_bam_connections = usb_bam_connections;
 	msm_usb_bam[bam].max_connections = max_connections;
 
-	return pdata;
+	return usb_bam_data;
 err:
 	log_event_err("%s: failed\n", __func__);
 	return NULL;
@@ -3011,9 +3021,8 @@
 static int usb_bam_init(struct platform_device *pdev)
 {
 	int ret;
-	struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
-	enum usb_ctrl bam_type = pdata->bam_type;
-	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+	struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+	enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
 	struct sps_bam_props props;
 
 	memset(&props, 0, sizeof(props));
@@ -3022,23 +3031,22 @@
 		bam_enable_strings[bam_type]);
 
 	props.phys_addr = ctx->io_res->start;
-	props.virt_addr = ctx->regs;
 	props.virt_size = resource_size(ctx->io_res);
 	props.irq = ctx->irq;
-	props.summing_threshold = pdata->override_threshold;
-	props.event_threshold = pdata->override_threshold;
-	props.num_pipes = pdata->usb_bam_num_pipes;
+	props.summing_threshold = ctx->usb_bam_data->override_threshold;
+	props.event_threshold = ctx->usb_bam_data->override_threshold;
+	props.num_pipes = ctx->usb_bam_data->usb_bam_num_pipes;
 	props.callback = usb_bam_sps_events;
 	props.user = bam_enable_strings[bam_type];
 
 	/*
-	 * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs.
-	 * Hence let BAM to ignore acknowledge from USB while resetting PIPE.
-	 */
-	if (pdata->ignore_core_reset_ack && bam_type != DWC3_CTRL)
+	* HSUSB and HSIC Cores don't support RESET ACK signal to BAMs
+	* Hence, let BAM to ignore acknowledge from USB while resetting PIPE
+	*/
+	if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL)
 		props.options = SPS_BAM_NO_EXT_P_RST;
 
-	if (pdata->disable_clk_gating)
+	if (ctx->usb_bam_data->disable_clk_gating)
 		props.options |= SPS_BAM_NO_LOCAL_CLK_GATING;
 
 	/*
@@ -3046,7 +3054,8 @@
 	 * starting peripheral controller to avoid switching USB core mode
 	 * from legacy to BAM with ongoing data transfers.
 	 */
-	if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) {
+	if (ctx->usb_bam_data->enable_hsusb_bam_on_boot
+					&& bam_type == CI_CTRL) {
 		pr_debug("Register and enable HSUSB BAM\n");
 		props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT;
 	}
@@ -3063,9 +3072,8 @@
 static int enable_usb_bam(struct platform_device *pdev)
 {
 	int ret;
-	struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
-	enum usb_ctrl bam_type = pdata->bam_type;
-	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+	struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+	enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
 
 	ret = usb_bam_init(pdev);
 	if (ret) {
@@ -3132,14 +3140,19 @@
 			&usb_bam_panic_blk);
 }
 
+static void usb_bam_unregister_panic_hdlr(void)
+{
+	atomic_notifier_chain_unregister(&panic_notifier_list,
+			&usb_bam_panic_blk);
+}
+
 static int usb_bam_probe(struct platform_device *pdev)
 {
 	int ret, i, irq;
 	struct resource *io_res;
-	void __iomem *regs;
 	enum usb_ctrl bam_type;
 	struct usb_bam_ctx_type *ctx;
-	struct msm_usb_bam_platform_data *pdata;
+	struct msm_usb_bam_data *usb_bam_data;
 
 	dev_dbg(&pdev->dev, "usb_bam_probe\n");
 
@@ -3155,25 +3168,19 @@
 		return irq;
 	}
 
-	regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res));
-	if (!regs) {
-		log_event_err("%s: ioremap failed\n", __func__);
-		return -ENOMEM;
-	}
-
 	/* specify BAM physical address to be filled in BAM connections */
-	pdata = usb_bam_dt_to_pdata(pdev, io_res->start);
-	if (!pdata)
+	usb_bam_data = usb_bam_dt_to_data(pdev, io_res->start);
+	if (!usb_bam_data)
 		return -EINVAL;
 
-	pdev->dev.platform_data = pdata;
-	bam_type = pdata->bam_type;
+	bam_type = usb_bam_data->bam_type;
 	ctx = &msm_usb_bam[bam_type];
+	dev_set_drvdata(&pdev->dev, ctx);
 
 	ctx->usb_bam_pdev = pdev;
 	ctx->irq = irq;
-	ctx->regs = regs;
 	ctx->io_res = io_res;
+	ctx->usb_bam_data = usb_bam_data;
 
 	for (i = 0; i < ctx->max_connections; i++) {
 		ctx->usb_bam_connections[i].enabled = 0;
@@ -3314,15 +3321,15 @@
 
 bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable)
 {
-	struct msm_usb_bam_platform_data *pdata;
+	struct msm_usb_bam_data *usb_bam_data;
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam];
 
 	if (!ctx->usb_bam_pdev)
 		return 0;
 
-	pdata = ctx->usb_bam_pdev->dev.platform_data;
+	usb_bam_data = ctx->usb_bam_data;
 	if ((bam != CI_CTRL) || !(bam_enable ||
-					pdata->enable_hsusb_bam_on_boot))
+					usb_bam_data->enable_hsusb_bam_on_boot))
 		return 0;
 
 	msm_hw_bam_disable(1);
@@ -3404,10 +3411,11 @@
 
 static int usb_bam_remove(struct platform_device *pdev)
 {
-	struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
-	enum usb_ctrl bam_type = pdata->bam_type;
-	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+	struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
 
+	usb_bam_ipa_delete_resources(ctx->usb_bam_data->bam_type);
+	usb_bam_unregister_panic_hdlr();
+	sps_deregister_bam_device(ctx->h_bam);
 	destroy_workqueue(ctx->usb_bam_wq);
 
 	return 0;
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 9f04957..8d023a8 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -37,6 +37,7 @@
 #define EMERGENCY_DLOAD_MAGIC1    0x322A4F99
 #define EMERGENCY_DLOAD_MAGIC2    0xC67E4350
 #define EMERGENCY_DLOAD_MAGIC3    0x77777777
+#define EMMC_DLOAD_TYPE		0x2
 
 #define SCM_IO_DISABLE_PMIC_ARBITER	1
 #define SCM_IO_DEASSERT_PS_HOLD		2
@@ -47,13 +48,14 @@
 
 
 static int restart_mode;
-void *restart_reason;
+static void __iomem *restart_reason, *dload_type_addr;
 static bool scm_pmic_arbiter_disable_supported;
 static bool scm_deassert_ps_hold_supported;
 /* Download mode master kill-switch */
 static void __iomem *msm_ps_hold;
 static phys_addr_t tcsr_boot_misc_detect;
 static int download_mode = 1;
+static struct kobject dload_kobj;
 
 #ifdef CONFIG_QCOM_DLOAD_MODE
 #define EDL_MODE_PROP "qcom,msm-imem-emergency_download_mode"
@@ -72,8 +74,23 @@
 static bool scm_dload_supported;
 
 static int dload_set(const char *val, struct kernel_param *kp);
+/* interface for exporting attributes */
+struct reset_attribute {
+	struct attribute        attr;
+	ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
+			char *buf);
+	size_t (*store)(struct kobject *kobj, struct attribute *attr,
+			const char *buf, size_t count);
+};
+#define to_reset_attr(_attr) \
+	container_of(_attr, struct reset_attribute, attr)
+#define RESET_ATTR(_name, _mode, _show, _store)	\
+	static struct reset_attribute reset_attr_##_name = \
+			__ATTR(_name, _mode, _show, _store)
+
 module_param_call(download_mode, dload_set, param_get_int,
 			&download_mode, 0644);
+
 static int panic_prep_restart(struct notifier_block *this,
 			      unsigned long event, void *ptr)
 {
@@ -85,7 +102,7 @@
 	.notifier_call	= panic_prep_restart,
 };
 
-int scm_set_dload_mode(int arg1, int arg2)
+static int scm_set_dload_mode(int arg1, int arg2)
 {
 	struct scm_desc desc = {
 		.args[0] = arg1,
@@ -397,6 +414,84 @@
 	pr_err("Powering off has failed\n");
 }
 
+static ssize_t attr_show(struct kobject *kobj, struct attribute *attr,
+				char *buf)
+{
+	struct reset_attribute *reset_attr = to_reset_attr(attr);
+	ssize_t ret = -EIO;
+
+	if (reset_attr->show)
+		ret = reset_attr->show(kobj, attr, buf);
+
+	return ret;
+}
+
+static ssize_t attr_store(struct kobject *kobj, struct attribute *attr,
+				const char *buf, size_t count)
+{
+	struct reset_attribute *reset_attr = to_reset_attr(attr);
+	ssize_t ret = -EIO;
+
+	if (reset_attr->store)
+		ret = reset_attr->store(kobj, attr, buf, count);
+
+	return ret;
+}
+
+static const struct sysfs_ops reset_sysfs_ops = {
+	.show	= attr_show,
+	.store	= attr_store,
+};
+
+static struct kobj_type reset_ktype = {
+	.sysfs_ops	= &reset_sysfs_ops,
+};
+
+static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr,
+				char *buf)
+{
+	uint32_t read_val, show_val;
+
+	read_val = __raw_readl(dload_type_addr);
+	if (read_val == EMMC_DLOAD_TYPE)
+		show_val = 1;
+	else
+		show_val = 0;
+
+	return snprintf(buf, sizeof(show_val), "%u\n", show_val);
+}
+
+static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
+				const char *buf, size_t count)
+{
+	uint32_t enabled;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &enabled);
+	if (ret < 0)
+		return ret;
+
+	if (!((enabled == 0) || (enabled == 1)))
+		return -EINVAL;
+
+	if (enabled == 1)
+		__raw_writel(EMMC_DLOAD_TYPE, dload_type_addr);
+	else
+		__raw_writel(0, dload_type_addr);
+
+	return count;
+}
+RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
+
+static struct attribute *reset_attrs[] = {
+	&reset_attr_emmc_dload.attr,
+	NULL
+};
+
+static struct attribute_group reset_attr_group = {
+	.attrs = reset_attrs,
+};
+
 static int msm_restart_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -448,6 +543,33 @@
 		iounmap(kaslr_imem_addr);
 	}
 #endif
+	np = of_find_compatible_node(NULL, NULL,
+				"qcom,msm-imem-dload-type");
+	if (!np) {
+		pr_err("unable to find DT imem dload-type node\n");
+		goto skip_sysfs_create;
+	} else {
+		dload_type_addr = of_iomap(np, 0);
+		if (!dload_type_addr) {
+			pr_err("unable to map imem dload-type offset\n");
+			goto skip_sysfs_create;
+		}
+	}
+
+	ret = kobject_init_and_add(&dload_kobj, &reset_ktype,
+			kernel_kobj, "%s", "dload");
+	if (ret) {
+		pr_err("%s:Error in creation kobject_add\n", __func__);
+		kobject_put(&dload_kobj);
+		goto skip_sysfs_create;
+	}
+
+	ret = sysfs_create_group(&dload_kobj, &reset_attr_group);
+	if (ret) {
+		pr_err("%s:Error in creation sysfs_create_group\n", __func__);
+		kobject_del(&dload_kobj);
+	}
+skip_sysfs_create:
 #endif
 	np = of_find_compatible_node(NULL, NULL,
 				"qcom,msm-imem-restart_reason");
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 4303960..3ca5def 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -507,6 +507,7 @@
 extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
 extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
 extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
+extern int fg_dump_regs(struct fg_chip *chip);
 extern int fg_ima_init(struct fg_chip *chip);
 extern int fg_dma_init(struct fg_chip *chip);
 extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts);
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index 0abc9df..c1b5adc 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -792,6 +792,7 @@
 		} else {
 			pr_err("wait for mem_grant timed out ret=%d\n",
 				ret);
+			fg_dump_regs(chip);
 		}
 	}
 
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index aed2062..fc60b26 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -492,6 +492,33 @@
 	return rc;
 }
 
+int fg_dump_regs(struct fg_chip *chip)
+{
+	int i, rc;
+	u8 buf[256];
+
+	if (!chip)
+		return -EINVAL;
+
+	rc = fg_read(chip, chip->batt_soc_base, buf, sizeof(buf));
+	if (rc < 0)
+		return rc;
+
+	pr_info("batt_soc_base registers:\n");
+	for (i = 0; i < sizeof(buf); i++)
+		pr_info("%04x:%02x\n", chip->batt_soc_base + i, buf[i]);
+
+	rc = fg_read(chip, chip->mem_if_base, buf, sizeof(buf));
+	if (rc < 0)
+		return rc;
+
+	pr_info("mem_if_base registers:\n");
+	for (i = 0; i < sizeof(buf); i++)
+		pr_info("%04x:%02x\n", chip->mem_if_base + i, buf[i]);
+
+	return 0;
+}
+
 int64_t twos_compliment_extend(int64_t val, int sign_bit_pos)
 {
 	int i, nbytes = DIV_ROUND_UP(sign_bit_pos, 8);
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index b02c860..fc34a8c9 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -3730,6 +3730,7 @@
 		return NOTIFY_OK;
 
 	if ((strcmp(psy->desc->name, "battery") == 0)
+		|| (strcmp(psy->desc->name, "parallel") == 0)
 		|| (strcmp(psy->desc->name, "usb") == 0)) {
 		/*
 		 * We cannot vote for awake votable here as that takes
@@ -4116,8 +4117,7 @@
 	}
 
 	fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
-	if (status & MEM_GNT_BIT)
-		complete_all(&chip->mem_grant);
+	complete_all(&chip->mem_grant);
 
 	return IRQ_HANDLED;
 }
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-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..44823b3 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -3059,6 +3059,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);
@@ -4007,6 +4012,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);
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/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ee23fc7..afda4c9 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.
  */
@@ -2676,6 +2688,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/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index b6ba4c4..8405537 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -9208,6 +9208,7 @@
 			goto enable_gating;
 	}
 
+	flush_work(&hba->eeh_work);
 	ret = ufshcd_link_state_transition(hba, req_link_state, 1);
 	if (ret)
 		goto set_dev_active;
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 ef55e58..c94f915 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -71,6 +71,16 @@
 	  deadlock detection mode AMON will trigger an interrupt if some LLCC request
 	  ages out.
 
+config QCOM_LLCC_PERFMON
+	tristate "Qualcomm Technologies, Inc. LLCC Perfmon driver"
+	depends on QCOM_LLCC
+	help
+	  This option enables driver for LLCC Performance monitor block. Using
+	  this various events in different LLCC sub block ports can be monitored.
+	  This is used for performance and debug activity and exports SYSFS
+	  interface. SYSFS interface used for configure and dump the LLCC
+	  performance events.
+
 config QCOM_PM
 	bool "Qualcomm Power Management"
 	depends on ARCH_QCOM && !ARM64
@@ -375,16 +385,6 @@
 	  32-bit values by specifying a unique string and
 	  remote processor ID.
 
-config MSM_SMP2P_TEST
-	bool "SMSM Point-to-Point Test"
-	depends on MSM_SMP2P
-	help
-	  Enables loopback and unit testing support for
-	  SMP2P. Loopback support is used by other
-	  processors to do unit testing. Unit tests
-	  are used to verify the local and remote
-	  implementations.
-
 config MSM_IPC_ROUTER_SMD_XPRT
 	depends on MSM_SMD
 	depends on IPC_ROUTER
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 37b33e6..4966c04 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_QCOM_LLCC) += llcc-core.o llcc-slice.o
 obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
 obj-$(CONFIG_QCOM_SDM670_LLCC) += llcc-sdm670.o
+obj-$(CONFIG_QCOM_LLCC_PERFMON) += llcc_perfmon.o
 obj-$(CONFIG_QCOM_LLCC_AMON) += llcc-amon.o
 obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o
 obj-$(CONFIG_QCOM_PM)	+=	spm.o
@@ -42,8 +43,7 @@
 obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o
 obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
 obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o
-obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_debug.o smp2p_sleepstate.o
-obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o
+obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_loopback.o smp2p_debug.o smp2p_sleepstate.o
 obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o
 obj-$(CONFIG_MSM_IPC_ROUTER_HSIC_XPRT) += ipc_router_hsic_xprt.o
 obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 6ce481b..9be0821 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2636,7 +2636,9 @@
 	clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
 
 	fw_down_data.crashed = event_data->crashed;
-	icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
+	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,
 				ICNSS_EVENT_SYNC, event_data);
 done:
diff --git a/drivers/soc/qcom/llcc_events.h b/drivers/soc/qcom/llcc_events.h
new file mode 100644
index 0000000..28f4594
--- /dev/null
+++ b/drivers/soc/qcom/llcc_events.h
@@ -0,0 +1,301 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SOC_QCOM_LLCC_EVENTS_H_
+#define _SOC_QCOM_LLCC_EVENTS_H_
+
+enum event_port_select {
+	EVENT_PORT_FEAC,
+	EVENT_PORT_FERC,
+	EVENT_PORT_FEWC,
+	EVENT_PORT_BEAC,
+	EVENT_PORT_BERC,
+	EVENT_PORT_TRP,
+	EVENT_PORT_DRP,
+	EVENT_PORT_PMGR,
+	EVENT_PORT_TENURE,
+	EVENT_PORT_TLAT,
+};
+
+enum feac_events {
+	FEAC_ANY_ACCESS,
+	FEAC_READ_INCR,
+	FEAC_WRITE_INCR,
+	FEAC_WRITE_ORDERED,
+	FEAC_READE_EXCL,
+	FEAC_WRITE_EXCL,
+	FEAC_CMO,
+	FEAC_CMO_CLEAN,
+	FEAC_CMO_INVAL,
+	FEAC_CMO_CLEANINVAL,
+	FEAC_CMO_DCPLD,
+	FEAC_READ_NOALLOC,
+	FEAC_WRITE_NOALLOC,
+	FEAC_PREFETCH,
+	FEAC_RD_BYTES,
+	FEAC_RD_BEATS,
+	FEAC_WR_BYTES,
+	FEAC_WR_BEATS,
+	FEAC_FC_READ,
+	FEAC_EWD_ACCESS,
+	FEAC_TCM_ACCESS,
+	FEAC_GM_HIT,
+	FEAC_GM_MISS,
+	FEAC_GM_UNAVAILABLE,
+	FEAC_XPU_ERROR,
+	FEAC_READ_HAZARD,
+	FEAC_WRITE_HAZARD,
+	FEAC_GRANULE_READ,
+	FEAC_GRANULE_WRITE,
+	FEAC_RIFB_ALLOC,
+	FEAC_WIFB_ALLOC,
+	FEAC_RIFB_DEALLOC,
+	FEAC_WIFB_DEALLOC,
+	FEAC_RESERVED,
+	FEAC_RESERVED1,
+	FEAC_FEAC2TRP_LP_TX,
+	FEAC_TRP_LP_BUSY,
+	FEAC_FEAC2TRP_HP_TX,
+	FEAC_TRP_HP_BUSY,
+	FEAC_FEAC2FEWC_TX,
+	FEAC_BEAC_LP_BUSY,
+	FEAC_BEAC_HP_BUSY,
+	FEAC_RIFB_FULL,
+	FEAC_WIFB_FULL,
+	FEAC_RD_CRDT_TX,
+	FEAC_WR_CRDT_TX,
+	FEAC_PROMOTION,
+	FEAC_FEAC2TRP_LP_PRESSURE,
+	FEAC_FEAC2TRP_HP_PRESSURE,
+	FEAC_FEAC2FEWC_PRESSURE,
+	FEAC_FEAC2BEAC_LP_PRESSURE,
+	FEAC_FEAC2BEAC_HP_PRESSURE,
+	FEAC_WR_THROUGH,
+};
+
+enum ferc_events {
+	FERC_BERC_CMD,
+	FERC_BERC_BEAT,
+	FERC_DRP_CMD,
+	FERC_DRP_BEAT,
+	FERC_RD_CTRL_RSP_TX,
+	FERC_WR_CTRL_RSP_TX,
+	FERC_RD_DATA_TX,
+	FERC_MISS_TRUMPS_HIT,
+	FERC_HIT_TRUMPS_WRSP,
+	FERC_RD_INTRA_RSP_IDLE,
+};
+
+enum fewc_events {
+	FEWC_WR_CMD,
+	FEWC_WR_DATA_BEAT,
+	FEWC_WR_LAST,
+	FEWC_WBUF_DEALLOC,
+	FEWC_WR_HIT,
+	FEWC_WR_MISS,
+	FEWC_NC_RMW,
+	FEWC_WR_DOWNGRADE,
+	FEWC_BEAC_WR_CMD,
+	FEWC_BEAC_WR_BEAT,
+	FEWC_BEAC_RD_CMD,
+	FEWC_BERC_FILL_BEAT,
+	FEWC_DRP_WR_CMD,
+	FEWC_DRP_WR_BEAT,
+	FEWC_DRP_RD_BEAT,
+	FEWC_TRP_TAG_LOOKUP,
+	FEWC_TRP_TAG_UPDATE,
+	FEWC_TRP_UNSTALL,
+	FEWC_WBUFFS_FULL,
+	FEWC_DRP_BUSY,
+	FEWC_BEAC_WR_BUSY,
+	FEWC_BEAC_RD_BUSY,
+	FEWC_TRP_TAG_LOOKUP_BUSY,
+	FEWC_TRP_TAG_UPDATE_BUSY,
+	FEWC_C_RMW,
+	FEWC_NC_ALLOC_RMW,
+	FEWC_NC_NO_ALLOC_RMW,
+	FEWC_NC_RMW_DEALLOC,
+	FEWC_C_RMW_DEALLOC,
+	FEWC_STALLED_BY_EVICT,
+};
+
+enum beac_events {
+	BEAC_RD_TX,
+	BEAC_WR_TX,
+	BEAC_RD_GRANULE,
+	BEAC_WR_GRANULE,
+	BEAC_WR_BEAT_TX,
+	BEAC_RD_CRDT_ZERO,
+	BEAC_WR_CRDT_ZERO,
+	BEAC_WDATA_CRDT_ZERO,
+	BEAC_IFCMD_CRDT_ZERO,
+	BEAC_IFWDATA_CRDT_ZERO,
+	BEAC_PCT_ENTRY_ALLOC,
+	BEAC_PCT_ENTRY_FREE,
+	BEAC_PCT_FULL,
+	BEAC_RD_PROMOTION_TX,
+	BEAC_WR_PROMOTION_TX,
+	BEAC_RD_PRESSURE_TX,
+	BEAC_WR_PRESSURE_TX,
+};
+
+enum berc_events {
+	BERC_RD_CMD,
+	BERC_ERROR_CMD,
+	BERC_PCT_ENTRY_DEALLOC,
+	BERC_RD_RSP_RX,
+	BERC_RD_RSP_BEAT_RX,
+	BERC_RD_LA_RX,
+	BERC_UNSTALL_RX,
+	BERC_TX_RD_CMD,
+	BERC_TX_ERR_CMD,
+	BERC_TX_RD_BEAT,
+	BERC_TX_ERR_BEAT,
+	BERC_RESERVED,
+	BERC_RESERVED1,
+	BERC_CMO_RX,
+	BERC_CMO_TX,
+	BERC_DRP_WR_TX,
+	BERC_DRP_WR_BEAT_TX,
+	BERC_FEWC_WR_TX,
+	BERC_FEWC_WR_BEAT_TX,
+	BERC_LBUFFS_FULL,
+	BERC_DRP_BUSY,
+	BERC_FEWC_BUSY,
+	BERC_LBUFF_STALLED,
+};
+
+enum trp_events {
+	TRP_ANY_ACCESS,
+	TRP_INCR_RD,
+	TRP_INCR_WR,
+	TRP_ANY_HIT,
+	TRP_RD_HIT,
+	TRP_WR_HIT,
+	TRP_RD_MISS,
+	TRP_WR_MISS,
+	TRP_RD_HIT_MISS,
+	TRP_WR_HIT_MISS,
+	TRP_EVICT,
+	TRP_GRANULE_EVICT,
+	TRP_RD_EVICT,
+	TRP_WR_EVICT,
+	TRP_LINE_FILL,
+	TRP_GRANULE_FILL,
+	TRP_WSC_WRITE,
+	TRP_WSC_EVICT,
+	TRP_SUBCACHE_ACT,
+	TRP_SUBCACHE_DEACT,
+	TRP_RD_DEACTIVE_SUBCACHE,
+	TRP_WR_DEACTIVE_SUBCACHE,
+	TRP_INVALID_LINE_ALLOC,
+	TRP_DEACTIVE_LINE_ALLOC,
+	TRP_SELF_EVICTION_ALLOC,
+	TRP_UC_SUBCACHE_ALLOC,
+	TRP_FC_SELF_EVICTION_ALLOC,
+	TRP_LP_SUBCACHE_VICTIM,
+	TRP_OC_SUBCACHE_VICTIM,
+	TRP_MRU_ROLLOVER,
+	TRP_NC_DOWNGRADE,
+	TRP_TAGRAM_CORR_ERR,
+	TRP_TAGRAM_UNCORR_ERR,
+	TRP_RD_MISS_FC,
+	TRP_CPU_WRITE_EWD_LINE,
+	TRP_CLIENT_WRITE_EWD_LINE,
+	TRP_CLIENT_READ_EWD_LINE,
+	TRP_CMO_I_EWD_LINE,
+	TRP_CMO_I_DIRTY_LINE,
+	TRP_DRP_RD_NOTIFICATION,
+	TRP_DRP_WR_NOTIFICATION,
+	TRP_LINEFILL_TAG_UPDATE,
+	TRP_FEWC_TAG_UPDATE,
+	TRP_ET_FULL,
+	TRP_NAWT_FULL,
+	TRP_HITQ_FULL,
+	TRP_ET_ALLOC,
+	TRP_ET_DEALLOC,
+	TRP_NAWT_ALLOC,
+	TRP_NAWT_DEALLOC,
+	TRP_RD_REPLAY,
+	TRP_WR_ECC_RD,
+	TRP_ET_LP_FULL,
+	TRP_ET_HP_FULL,
+	TRP_SOEH,
+};
+
+enum drp_events {
+	DRP_TRP_RD_NOTIFICATION,
+	DRP_TRP_WR_NOTIFICATION,
+	DRP_BIST_WR_NOTIFICATION,
+	DRP_DRIE_WR_NOTIFICATION,
+	DRP_ECC_CORR_ERR,
+	DRP_ECC_UNCORR_ERR,
+	DRP_FERC_RD_TX,
+	DRP_FEWC_RD_TX,
+	DRP_EVICT_LINE_TX,
+	DRP_EVICT_GRANULE_TX,
+	DRP_BIST_TX,
+	DRP_FERC_RD_BEAT,
+	DRP_FEWC_RD_BEAT,
+	DRP_BIST_RD_BEAT,
+	DRP_EVICT_RD_BEAT,
+	DRP_BERC_WR_BEAT,
+	DRP_FEWC_WR_BEAT,
+	DRP_BIST_WR_BEAT,
+	DRP_DRIE_WR_BEAT,
+	DRP_BERC_UNSTALL,
+	DRP_FEWC_UNSTALL,
+	DRP_LB_RD,
+	DRP_LB_WR,
+	DRP_BANK_CONFLICT,
+	DRP_FILL_TRUMPS_RD,
+	DRP_RD_TRUMPS_WR,
+	DRP_LB_SLP_RET,
+	DRP_LB_SLP_NRET,
+	DRP_LB_WAKEUP,
+	DRP_TRP_EARLY_WAKEUP,
+	DRP_PCB_IDLE,
+	DRP_EVICT_RDFIFO_FULL,
+	DRP_FEWC_RDFIFO_FULL,
+	DRP_FERC_RDFIFO_FULL,
+	DRP_FERC_RD,
+	DRP_FEWC_RD,
+	DRP_LINE_EVICT,
+	DRP_GRANULE_EVICT,
+	DRP_BIST_RD,
+	DRP_FEWC_WR,
+	DRP_LINE_FILL,
+	DRP_GRANULE_FILL,
+	DRP_BIST_WR,
+	DRP_DRIE_WR,
+};
+
+enum pmgr_events {
+	PMGR_Q_RUN_STATE,
+	PMGR_Q_DENIED_STATE,
+	PMGR_Q_STOPEED_TO_Q_RUN,
+	PMGR_Q_RUN_TO_Q_FENCED,
+	PMGR_Q_RUN_TO_Q_DENIED,
+	PMGR_Q_DENIED_TO_Q_RUN,
+	PMGR_Q_FENCED_TO_Q_STOPPED,
+	PMGR_Q_FENCED_TO_Q_DENIED,
+};
+
+enum filter_type {
+	SCID,
+	MID,
+	PROFILING_TAG,
+	WAY_ID,
+	UNKNOWN,
+};
+
+#endif /* _SOC_QCOM_LLCC_EVENTS_H_ */
diff --git a/drivers/soc/qcom/llcc_perfmon.c b/drivers/soc/qcom/llcc_perfmon.c
new file mode 100644
index 0000000..39276a9
--- /dev/null
+++ b/drivers/soc/qcom/llcc_perfmon.c
@@ -0,0 +1,1203 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/module.h>
+#include "llcc_events.h"
+#include "llcc_perfmon.h"
+
+#define LLCC_PERFMON_NAME		"llcc_perfmon"
+#define LLCC_PERFMON_COUNTER_MAX	16
+#define MAX_NUMBER_OF_PORTS		8
+#define NUM_CHANNELS			16
+#define MAX_STRING_SIZE			20
+#define DELIM_CHAR			" "
+
+/**
+ * struct llcc_perfmon_counter_map	- llcc perfmon counter map info
+ * @port_sel:		Port selected for configured counter
+ * @event_sel:		Event selected for configured counter
+ * @counter_dump:	Cumulative counter dump
+ */
+struct llcc_perfmon_counter_map {
+	unsigned int port_sel;
+	unsigned int event_sel;
+	unsigned long long counter_dump;
+};
+
+struct llcc_perfmon_private;
+/**
+ * struct event_port_ops		- event port operation
+ * @event_config:		Counter config support for port &  event
+ * @event_enable:		Counter enable support for port
+ * @event_filter_config:	Port filter config support
+ */
+struct event_port_ops {
+	void (*event_config)(struct llcc_perfmon_private *,
+			unsigned int, unsigned int, bool);
+	void (*event_enable)(struct llcc_perfmon_private *, bool);
+	void (*event_filter_config)(struct llcc_perfmon_private *,
+			enum filter_type, unsigned long, bool);
+};
+
+/**
+ * struct llcc_perfmon_private	- llcc perfmon private
+ * @llcc_map:		llcc register address space map
+ * @broadcast_off:	Offset of llcc broadcast address space
+ * @bank_off:		Offset of llcc banks
+ * @num_banks:		Number of banks supported
+ * @port_ops:		struct event_port_ops
+ * @configured:		Mapping of configured event counters
+ * @configured_counters:
+ *			Count of configured counters.
+ * @enables_port:	Port enabled for perfmon configuration
+ * @filtered_ports:	Port filter enabled
+ * @port_configd:	Number of perfmon port configuration supported
+ * @mutex:		mutex to protect this structure
+ * @hrtimer:		hrtimer instance for timer functionality
+ * @expires:		timer expire time in nano seconds
+ */
+struct llcc_perfmon_private {
+	struct regmap *llcc_map;
+	unsigned int broadcast_off;
+	unsigned int bank_off[NUM_CHANNELS];
+	unsigned int num_banks;
+	struct event_port_ops *port_ops[MAX_NUMBER_OF_PORTS];
+	struct llcc_perfmon_counter_map configured[LLCC_PERFMON_COUNTER_MAX];
+	unsigned int configured_counters;
+	unsigned int enables_port;
+	unsigned int filtered_ports;
+	unsigned int port_configd;
+	struct mutex mutex;
+	struct hrtimer hrtimer;
+	ktime_t expires;
+};
+
+static inline void llcc_bcast_write(struct llcc_perfmon_private *llcc_priv,
+			unsigned int offset, uint32_t val)
+{
+	regmap_write(llcc_priv->llcc_map, llcc_priv->broadcast_off + offset,
+			val);
+}
+
+static inline void llcc_bcast_read(struct llcc_perfmon_private *llcc_priv,
+		unsigned int offset, uint32_t *val)
+{
+	regmap_read(llcc_priv->llcc_map, llcc_priv->broadcast_off + offset,
+			val);
+}
+
+static void llcc_bcast_modify(struct llcc_perfmon_private *llcc_priv,
+		unsigned int offset, uint32_t val, uint32_t mask)
+{
+	uint32_t readval;
+
+	llcc_bcast_read(llcc_priv, offset, &readval);
+	readval &= ~mask;
+	readval |= val & mask;
+	llcc_bcast_write(llcc_priv, offset, readval);
+}
+
+static void perfmon_counter_dump(struct llcc_perfmon_private *llcc_priv)
+{
+	uint32_t val;
+	unsigned int i, j;
+	unsigned long long total;
+
+	llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP);
+	for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+		total = 0;
+		for (j = 0; j < llcc_priv->num_banks; j++) {
+			regmap_read(llcc_priv->llcc_map, llcc_priv->bank_off[j]
+					+ LLCC_COUNTER_n_VALUE(i), &val);
+			total += val;
+		}
+
+		llcc_priv->configured[i].counter_dump += total;
+	}
+
+	total = 0;
+	for (j = 0; j < llcc_priv->num_banks; j++) {
+		regmap_read(llcc_priv->llcc_map, llcc_priv->bank_off[j] +
+				LLCC_COUNTER_n_VALUE(i), &val);
+		total += val;
+	}
+
+	llcc_priv->configured[i].counter_dump += total;
+}
+
+static ssize_t perfmon_counter_dump_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	uint32_t val;
+	unsigned int i, j;
+	unsigned long long total;
+	ssize_t cnt = 0, print;
+
+	if (llcc_priv->configured_counters == 0) {
+		pr_err("counters not configured\n");
+		return cnt;
+	}
+
+	if (llcc_priv->expires.tv64) {
+		perfmon_counter_dump(llcc_priv);
+		for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+			print = snprintf(buf, MAX_STRING_SIZE, "Port %02d,",
+					llcc_priv->configured[i].port_sel);
+			buf += print;
+			cnt += print;
+			print = snprintf(buf, MAX_STRING_SIZE, "Event %02d,",
+					llcc_priv->configured[i].event_sel);
+			buf += print;
+			cnt += print;
+
+			print = snprintf(buf, MAX_STRING_SIZE, "0x%016llx\n",
+				       llcc_priv->configured[i].counter_dump);
+			buf += print;
+			cnt += print;
+			llcc_priv->configured[i].counter_dump = 0;
+		}
+
+		print = snprintf(buf, MAX_STRING_SIZE, "CYCLE COUNT, ,");
+		buf += print;
+		cnt += print;
+		print = snprintf(buf, MAX_STRING_SIZE, "0x%016llx\n",
+			llcc_priv->configured[i].counter_dump);
+		buf += print;
+		cnt += print;
+		llcc_priv->configured[i].counter_dump = 0;
+		hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+	} else {
+		llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP);
+
+		for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+			print = snprintf(buf, MAX_STRING_SIZE, "Port %02d,",
+					llcc_priv->configured[i].port_sel);
+			buf += print;
+			cnt += print;
+			print = snprintf(buf, MAX_STRING_SIZE, "Event %02d,",
+					llcc_priv->configured[i].event_sel);
+			buf += print;
+			cnt += print;
+			total = 0;
+			for (j = 0; j < llcc_priv->num_banks; j++) {
+				regmap_read(llcc_priv->llcc_map,
+						llcc_priv->bank_off[j]
+						+ LLCC_COUNTER_n_VALUE(i),
+						&val);
+				print = snprintf(buf, MAX_STRING_SIZE,
+						"0x%08x,", val);
+				buf += print;
+				cnt += print;
+				total += val;
+			}
+
+			print = snprintf(buf, MAX_STRING_SIZE, "0x%09llx\n",
+					total);
+			buf += print;
+			cnt += print;
+		}
+
+		print = snprintf(buf, MAX_STRING_SIZE, "CYCLE COUNT, ,");
+		buf += print;
+		cnt += print;
+		total = 0;
+		for (j = 0; j < llcc_priv->num_banks; j++) {
+			regmap_read(llcc_priv->llcc_map,
+					llcc_priv->bank_off[j] +
+					LLCC_COUNTER_n_VALUE(i), &val);
+			print = snprintf(buf, MAX_STRING_SIZE, "0x%08x,", val);
+			buf += print;
+			cnt += print;
+			total += val;
+		}
+
+		print = snprintf(buf, MAX_STRING_SIZE, "0x%09llx\n", total);
+		buf += print;
+		cnt += print;
+	}
+
+	return cnt;
+}
+
+static ssize_t perfmon_configure_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	struct event_port_ops *port_ops;
+	unsigned int j;
+	unsigned long port_sel, event_sel;
+	uint32_t val;
+	char *token, *delim = DELIM_CHAR;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (llcc_priv->configured_counters) {
+		pr_err("Counters configured already, remove & try again\n");
+		mutex_unlock(&llcc_priv->mutex);
+		return -EINVAL;
+	}
+
+	llcc_priv->configured_counters = 0;
+	j = 0;
+	token = strsep((char **)&buf, delim);
+
+	while (token != NULL) {
+		if (kstrtoul(token, 10, &port_sel))
+			break;
+
+		if (port_sel >= llcc_priv->port_configd)
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &event_sel))
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (event_sel >= EVENT_NUM_MAX) {
+			pr_err("unsupported event num %ld\n", event_sel);
+			continue;
+		}
+
+		llcc_priv->configured[j].port_sel = port_sel;
+		llcc_priv->configured[j].event_sel = event_sel;
+		port_ops = llcc_priv->port_ops[port_sel];
+		pr_info("configured event %ld counter %d on port %ld\n",
+				event_sel, j, port_sel);
+		port_ops->event_config(llcc_priv, event_sel, j++, true);
+		if (!(llcc_priv->enables_port & (1 << port_sel)))
+			if (port_ops->event_enable)
+				port_ops->event_enable(llcc_priv, true);
+
+		llcc_priv->enables_port |= (1 << port_sel);
+
+		/* Last perfmon counter for cycle counter */
+		if (llcc_priv->configured_counters++ ==
+				(LLCC_PERFMON_COUNTER_MAX - 2))
+			break;
+	}
+
+	/* configure clock event */
+	val = COUNT_CLOCK_EVENT | CLEAR_ON_ENABLE | CLEAR_ON_DUMP;
+	llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), val);
+
+	llcc_priv->configured_counters++;
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_remove_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	struct event_port_ops *port_ops;
+	unsigned int j, counter_remove = 0;
+	unsigned long port_sel, event_sel;
+	char *token, *delim = DELIM_CHAR;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (!llcc_priv->configured_counters) {
+		pr_err("Counters not configured\n");
+		mutex_unlock(&llcc_priv->mutex);
+		return -EINVAL;
+	}
+
+	j = 0;
+	token = strsep((char **)&buf, delim);
+
+	while (token != NULL) {
+		if (kstrtoul(token, 10, &port_sel))
+			break;
+
+		if (port_sel >= llcc_priv->port_configd)
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &event_sel))
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (event_sel >= EVENT_NUM_MAX) {
+			pr_err("unsupported event num %ld\n", event_sel);
+			continue;
+		}
+
+		/* put dummy values */
+		llcc_priv->configured[j].port_sel = MAX_NUMBER_OF_PORTS;
+		llcc_priv->configured[j].event_sel = 100;
+		port_ops = llcc_priv->port_ops[port_sel];
+		pr_info("Removed event %ld counter %d from port %ld\n",
+				event_sel, j, port_sel);
+
+		port_ops->event_config(llcc_priv, event_sel, j++, false);
+		if (llcc_priv->enables_port & (1 << port_sel))
+			if (port_ops->event_enable)
+				port_ops->event_enable(llcc_priv, false);
+
+		llcc_priv->enables_port &= ~(1 << port_sel);
+
+		/* Last perfmon counter for cycle counter */
+		if (counter_remove++ == (LLCC_PERFMON_COUNTER_MAX - 2))
+			break;
+	}
+
+	/* remove clock event */
+	llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), 0);
+
+	llcc_priv->configured_counters = 0;
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static enum filter_type find_filter_type(char *filter)
+{
+	enum filter_type ret = UNKNOWN;
+
+	if (!strcmp(filter, "SCID"))
+		ret = SCID;
+	else if (!strcmp(filter, "MID"))
+		ret = MID;
+	else if (!strcmp(filter, "PROFILING_TAG"))
+		ret = PROFILING_TAG;
+	else if (!strcmp(filter, "WAY_ID"))
+		ret = WAY_ID;
+
+	return ret;
+}
+
+static ssize_t perfmon_filter_config_store(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	unsigned long port, val;
+	struct event_port_ops *port_ops;
+	char *token, *delim = DELIM_CHAR;
+	enum filter_type filter = UNKNOWN;
+
+	mutex_lock(&llcc_priv->mutex);
+
+	token = strsep((char **)&buf, delim);
+	if (token != NULL)
+		filter = find_filter_type(token);
+
+	if (filter == UNKNOWN) {
+		pr_err("filter configuration failed, Unsupported filter\n");
+		goto filter_config_free;
+	}
+
+	token = strsep((char **)&buf, delim);
+	if (token == NULL) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_config_free;
+	}
+
+	if (kstrtoul(token, 10, &val)) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_config_free;
+	}
+
+	if ((filter == SCID) && (val >= SCID_MAX)) {
+		pr_err("filter configuration failed, SCID above MAX value\n");
+		goto filter_config_free;
+	}
+
+
+	while (token != NULL) {
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &port))
+			break;
+
+		llcc_priv->filtered_ports |= 1 << port;
+		port_ops = llcc_priv->port_ops[port];
+		if (port_ops->event_filter_config)
+			port_ops->event_filter_config(llcc_priv, filter, val,
+					true);
+	}
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+
+filter_config_free:
+	mutex_unlock(&llcc_priv->mutex);
+	return -EINVAL;
+}
+
+static ssize_t perfmon_filter_remove_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	struct event_port_ops *port_ops;
+	unsigned long port, val;
+	char *token, *delim = DELIM_CHAR;
+	enum filter_type filter = UNKNOWN;
+
+	mutex_lock(&llcc_priv->mutex);
+	token = strsep((char **)&buf, delim);
+	if (token != NULL)
+		filter = find_filter_type(token);
+
+	if (filter == UNKNOWN) {
+		pr_err("filter configuration failed, Unsupported filter\n");
+		goto filter_remove_free;
+	}
+
+	token = strsep((char **)&buf, delim);
+	if (token == NULL) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_remove_free;
+	}
+
+	if (kstrtoul(token, 10, &val)) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_remove_free;
+	}
+
+	if ((filter == SCID) && (val >= SCID_MAX)) {
+		pr_err("filter configuration failed, SCID above MAX value\n");
+		goto filter_remove_free;
+	}
+
+	while (token != NULL) {
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &port))
+			break;
+
+		llcc_priv->filtered_ports &= ~(1 << port);
+		port_ops = llcc_priv->port_ops[port];
+		if (port_ops->event_filter_config)
+			port_ops->event_filter_config(llcc_priv, filter, val,
+					false);
+	}
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+
+filter_remove_free:
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_start_store(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	uint32_t val = 0, mask;
+	unsigned long start;
+
+	if (kstrtoul(buf, 10, &start))
+		return -EINVAL;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (start) {
+		if (!llcc_priv->configured_counters)
+			pr_err("start failed. perfmon not configured\n");
+
+		val = MANUAL_MODE | MONITOR_EN;
+		if (llcc_priv->expires.tv64) {
+		if (hrtimer_is_queued(&llcc_priv->hrtimer))
+			hrtimer_forward_now(&llcc_priv->hrtimer,
+					llcc_priv->expires);
+		else
+			hrtimer_start(&llcc_priv->hrtimer,
+					llcc_priv->expires,
+					HRTIMER_MODE_REL_PINNED);
+		}
+
+	} else {
+		if (llcc_priv->expires.tv64)
+			hrtimer_cancel(&llcc_priv->hrtimer);
+
+		if (!llcc_priv->configured_counters)
+			pr_err("stop failed. perfmon not configured\n");
+	}
+
+	mask = PERFMON_MODE_MONITOR_MODE_MASK | PERFMON_MODE_MONITOR_EN_MASK;
+	llcc_bcast_modify(llcc_priv, PERFMON_MODE, val, mask);
+
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_ns_periodic_dump_store(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+
+	if (kstrtos64(buf, 10, &llcc_priv->expires.tv64))
+		return -EINVAL;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (!llcc_priv->expires.tv64) {
+		hrtimer_cancel(&llcc_priv->hrtimer);
+		mutex_unlock(&llcc_priv->mutex);
+		return count;
+	}
+
+	if (hrtimer_is_queued(&llcc_priv->hrtimer))
+		hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+	else
+		hrtimer_start(&llcc_priv->hrtimer, llcc_priv->expires,
+			      HRTIMER_MODE_REL_PINNED);
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_scid_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	uint32_t val;
+	unsigned int i, offset;
+	ssize_t cnt = 0, print;
+
+	for (i = 0; i < SCID_MAX; i++) {
+		offset = TRP_SCID_n_STATUS(i);
+		llcc_bcast_read(llcc_priv, offset, &val);
+		if (val & TRP_SCID_STATUS_ACTIVE_MASK)
+			print = snprintf(buf, MAX_STRING_SIZE, "SCID %02d %10s",
+					i, "ACTIVE");
+		else
+			print = snprintf(buf, MAX_STRING_SIZE, "SCID %02d %10s",
+					i, "DEACTIVE");
+
+		buf += print;
+		cnt += print;
+
+		val = (val & TRP_SCID_STATUS_CURRENT_CAP_MASK)
+			>> TRP_SCID_STATUS_CURRENT_CAP_SHIFT;
+		print = snprintf(buf, MAX_STRING_SIZE, ",0x%08x\n", val);
+		buf += print;
+		cnt += print;
+	}
+
+	return cnt;
+}
+
+static DEVICE_ATTR_RO(perfmon_counter_dump);
+static DEVICE_ATTR_WO(perfmon_configure);
+static DEVICE_ATTR_WO(perfmon_remove);
+static DEVICE_ATTR_WO(perfmon_filter_config);
+static DEVICE_ATTR_WO(perfmon_filter_remove);
+static DEVICE_ATTR_WO(perfmon_start);
+static DEVICE_ATTR_RO(perfmon_scid_status);
+static DEVICE_ATTR_WO(perfmon_ns_periodic_dump);
+
+static struct attribute *llcc_perfmon_attrs[] = {
+	&dev_attr_perfmon_counter_dump.attr,
+	&dev_attr_perfmon_configure.attr,
+	&dev_attr_perfmon_remove.attr,
+	&dev_attr_perfmon_filter_config.attr,
+	&dev_attr_perfmon_filter_remove.attr,
+	&dev_attr_perfmon_start.attr,
+	&dev_attr_perfmon_scid_status.attr,
+	&dev_attr_perfmon_ns_periodic_dump.attr,
+	NULL,
+};
+
+static struct attribute_group llcc_perfmon_group = {
+	.attrs	= llcc_perfmon_attrs,
+};
+
+static void perfmon_counter_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int port, unsigned int event_counter_num)
+{
+	uint32_t val;
+
+	val = (port & PERFMON_PORT_SELECT_MASK) |
+		((event_counter_num << EVENT_SELECT_SHIFT) &
+		PERFMON_EVENT_SELECT_MASK) | CLEAR_ON_ENABLE | CLEAR_ON_DUMP;
+	llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(event_counter_num),
+			val);
+}
+
+static void feac_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, FEAC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_FEAC, counter_num);
+}
+
+static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable) {
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+			val |= (FILTER_0 << FEAC_SCALING_FILTER_SEL_SHIFT) |
+			       FEAC_SCALING_FILTER_EN;
+	}
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+		mask |= FEAC_SCALING_FILTER_SEL_MASK |
+			FEAC_SCALING_FILTER_EN_MASK;
+
+	llcc_bcast_modify(llcc_priv, FEAC_PROF_CFG, val, mask);
+}
+
+static void feac_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter == SCID) {
+		if (enable)
+			val = (match << SCID_MATCH_SHIFT)
+				| SCID_MASK_MASK;
+
+		mask = SCID_MATCH_MASK | SCID_MASK_MASK;
+		llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG6, val,
+				mask);
+	} else if (filter == MID) {
+		if (enable)
+			val = (match << MID_MATCH_SHIFT)
+				| MID_MASK_MASK;
+
+		mask = MID_MATCH_MASK | MID_MASK_MASK;
+		llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG5, val,
+				mask);
+	} else {
+		pr_err("unknown filter/not supported\n");
+	}
+}
+
+static struct event_port_ops feac_port_ops = {
+	.event_config	= feac_event_config,
+	.event_enable	= feac_event_enable,
+	.event_filter_config	= feac_event_filter_config,
+};
+
+static void ferc_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = event_type << EVENT_SEL_SHIFT;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, FERC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_FERC, counter_num);
+}
+
+static void ferc_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, FERC_PROF_CFG, val, mask);
+}
+
+static void ferc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << PROFTAG_MATCH_SHIFT) |
+		       FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+	mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+	llcc_bcast_modify(llcc_priv, FERC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops ferc_port_ops = {
+	.event_config	= ferc_event_config,
+	.event_enable	= ferc_event_enable,
+	.event_filter_config	= ferc_event_filter_config,
+};
+
+static void fewc_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, FEWC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_FEWC, counter_num);
+}
+
+static void fewc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << PROFTAG_MATCH_SHIFT) |
+		       FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+	mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+	llcc_bcast_modify(llcc_priv, FEWC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops fewc_port_ops = {
+	.event_config	= fewc_event_config,
+	.event_filter_config	= fewc_event_filter_config,
+};
+
+static void beac_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_BEAC, counter_num);
+}
+
+static void beac_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_CFG, val, mask);
+}
+
+static void beac_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << BEAC_PROFTAG_MATCH_SHIFT)
+			| FILTER_0_MASK << BEAC_PROFTAG_MASK_SHIFT;
+
+	mask = BEAC_PROFTAG_MASK_MASK | BEAC_PROFTAG_MATCH_MASK;
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_FILTER_0_CFG5, val, mask);
+
+	if (enable)
+		val = match << BEAC_MC_PROFTAG_SHIFT;
+
+	mask = BEAC_MC_PROFTAG_MASK;
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_CFG, val, mask);
+}
+
+static struct event_port_ops beac_port_ops = {
+	.event_config	= beac_event_config,
+	.event_enable	= beac_event_enable,
+	.event_filter_config	= beac_event_filter_config,
+};
+
+static void berc_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, BERC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_BERC, counter_num);
+}
+
+static void berc_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, BERC_PROF_CFG, val, mask);
+}
+
+static void berc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << PROFTAG_MATCH_SHIFT) |
+		       FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+	mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+	llcc_bcast_modify(llcc_priv, BERC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops berc_port_ops = {
+	.event_config	= berc_event_config,
+	.event_enable	= berc_event_enable,
+	.event_filter_config	= berc_event_filter_config,
+};
+
+static void trp_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, TRP_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_TRP, counter_num);
+}
+
+static void trp_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter == SCID) {
+		if (enable)
+			val = (match << TRP_SCID_MATCH_SHIFT)
+				| TRP_SCID_MASK_MASK;
+
+		mask = TRP_SCID_MATCH_MASK | TRP_SCID_MASK_MASK;
+	} else if (filter == WAY_ID) {
+		if (enable)
+			val = (match << TRP_WAY_ID_MATCH_SHIFT)
+				| TRP_WAY_ID_MASK_MASK;
+
+		mask = TRP_WAY_ID_MATCH_MASK |
+			TRP_WAY_ID_MASK_MASK;
+	} else if (filter == PROFILING_TAG) {
+		if (enable)
+			val = (match << TRP_PROFTAG_MATCH_SHIFT)
+				| FILTER_0_MASK << TRP_PROFTAG_MASK_SHIFT;
+
+		mask = TRP_PROFTAG_MATCH_MASK | TRP_PROFTAG_MASK_MASK;
+	} else {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	llcc_bcast_modify(llcc_priv, TRP_PROF_FILTER_0_CFG1, val, mask);
+}
+
+static struct event_port_ops  trp_port_ops = {
+	.event_config	= trp_event_config,
+	.event_filter_config	= trp_event_filter_config,
+};
+
+static void drp_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, DRP_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_DRP, counter_num);
+}
+
+static void drp_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, DRP_PROF_CFG, val, mask);
+}
+
+static struct event_port_ops drp_port_ops = {
+	.event_config	= drp_event_config,
+	.event_enable	= drp_event_enable,
+};
+
+static void pmgr_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, PMGR_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_PMGR, counter_num);
+}
+
+static struct event_port_ops pmgr_port_ops = {
+	.event_config	= pmgr_event_config,
+};
+
+static void llcc_register_event_port(struct llcc_perfmon_private *llcc_priv,
+		struct event_port_ops *ops, unsigned int event_port_num)
+{
+	if (llcc_priv->port_configd >= MAX_NUMBER_OF_PORTS) {
+		pr_err("Register port Failure!");
+		return;
+	}
+
+	llcc_priv->port_configd = llcc_priv->port_configd + 1;
+	llcc_priv->port_ops[event_port_num] = ops;
+}
+
+static enum hrtimer_restart llcc_perfmon_timer_handler(struct hrtimer *hrtimer)
+{
+	struct llcc_perfmon_private *llcc_priv = container_of(hrtimer,
+			struct llcc_perfmon_private, hrtimer);
+
+	perfmon_counter_dump(llcc_priv);
+	hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+	return HRTIMER_RESTART;
+}
+
+static int llcc_perfmon_probe(struct platform_device *pdev)
+{
+	int result = 0;
+	struct llcc_perfmon_private *llcc_priv;
+	struct device *dev = &pdev->dev;
+	uint32_t val;
+
+	llcc_priv = devm_kzalloc(&pdev->dev, sizeof(*llcc_priv), GFP_KERNEL);
+	if (llcc_priv == NULL)
+		return -ENOMEM;
+
+	llcc_priv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(llcc_priv->llcc_map))
+		return PTR_ERR(llcc_priv->llcc_map);
+
+	result = of_property_read_u32(pdev->dev.parent->of_node,
+			"qcom,llcc-broadcast-off", &llcc_priv->broadcast_off);
+	if (result) {
+		pr_err("Invalid qcom,broadcast-off entry\n");
+		return result;
+	}
+
+	llcc_bcast_read(llcc_priv, LLCC_COMMON_STATUS0, &val);
+
+	llcc_priv->num_banks = (val & LB_CNT_MASK) >> LB_CNT_SHIFT;
+	result = of_property_read_variable_u32_array(pdev->dev.parent->of_node,
+			"qcom,llcc-banks-off", (u32 *)&llcc_priv->bank_off,
+			1, llcc_priv->num_banks);
+	if (result < 0) {
+		pr_err("Invalid qcom,llcc-banks-off entry\n");
+		return result;
+	}
+
+	result = sysfs_create_group(&pdev->dev.kobj, &llcc_perfmon_group);
+	if (result) {
+		pr_err("Unable to create sysfs version group\n");
+		return result;
+	}
+
+	mutex_init(&llcc_priv->mutex);
+	platform_set_drvdata(pdev, llcc_priv);
+	llcc_register_event_port(llcc_priv, &feac_port_ops, EVENT_PORT_FEAC);
+	llcc_register_event_port(llcc_priv, &ferc_port_ops, EVENT_PORT_FERC);
+	llcc_register_event_port(llcc_priv, &fewc_port_ops, EVENT_PORT_FEWC);
+	llcc_register_event_port(llcc_priv, &beac_port_ops, EVENT_PORT_BEAC);
+	llcc_register_event_port(llcc_priv, &berc_port_ops, EVENT_PORT_BERC);
+	llcc_register_event_port(llcc_priv, &trp_port_ops, EVENT_PORT_TRP);
+	llcc_register_event_port(llcc_priv, &drp_port_ops, EVENT_PORT_DRP);
+	llcc_register_event_port(llcc_priv, &pmgr_port_ops, EVENT_PORT_PMGR);
+	hrtimer_init(&llcc_priv->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	llcc_priv->hrtimer.function = llcc_perfmon_timer_handler;
+	llcc_priv->expires.tv64 = 0;
+	return 0;
+}
+
+static int llcc_perfmon_remove(struct platform_device *pdev)
+{
+	struct llcc_perfmon_private *llcc_priv = platform_get_drvdata(pdev);
+
+	while (hrtimer_active(&llcc_priv->hrtimer))
+		hrtimer_cancel(&llcc_priv->hrtimer);
+
+	mutex_destroy(&llcc_priv->mutex);
+	sysfs_remove_group(&pdev->dev.kobj, &llcc_perfmon_group);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static const struct of_device_id of_match_llcc[] = {
+	{
+		.compatible = "qcom,llcc-perfmon",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_match_llcc);
+
+static struct platform_driver llcc_perfmon_driver = {
+	.probe = llcc_perfmon_probe,
+	.remove	= llcc_perfmon_remove,
+	.driver	= {
+		.name = LLCC_PERFMON_NAME,
+		.of_match_table = of_match_llcc,
+	}
+};
+module_platform_driver(llcc_perfmon_driver);
+
+MODULE_DESCRIPTION("QCOM LLCC PMU MONITOR");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/llcc_perfmon.h b/drivers/soc/qcom/llcc_perfmon.h
new file mode 100644
index 0000000..11dd2f8
--- /dev/null
+++ b/drivers/soc/qcom/llcc_perfmon.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SOC_QCOM_LLCC_PERFMON_H_
+#define _SOC_QCOM_LLCC_PERFMON_H_
+
+#define LLCC_COMMON_STATUS0		(0x3000C)
+/* FEAC */
+#define FEAC_PROF_FILTER_0_CFG5		(0x037014)
+#define FEAC_PROF_FILTER_0_CFG6		(0x037018)
+#define FEAC_PROF_EVENT_n_CFG(n)	(0x037060 + 4 * n)
+#define FEAC_PROF_CFG			(0x0370A0)
+
+/* FERC */
+#define FERC_PROF_FILTER_0_CFG0		(0x03B000)
+#define FERC_PROF_EVENT_n_CFG(n)	(0x03B020 + 4 * n)
+#define FERC_PROF_CFG			(0x03B060)
+
+/* FEWC */
+#define FEWC_PROF_FILTER_0_CFG0		(0x033000)
+#define FEWC_PROF_EVENT_n_CFG(n)	(0x033020 + 4 * n)
+
+/* BEAC */
+#define BEAC_PROF_FILTER_0_CFG5		(0x049014)
+#define BEAC_PROF_EVENT_n_CFG(n)	(0x049040 + 4 * n)
+#define BEAC_PROF_CFG			(0x049080)
+
+/* BERC */
+#define BERC_PROF_FILTER_0_CFG0		(0x039000)
+#define BERC_PROF_EVENT_n_CFG(n)	(0x039020 + 4 * n)
+#define BERC_PROF_CFG			(0x039060)
+
+/* TRP */
+#define TRP_PROF_FILTER_0_CFG1		(0x024004)
+#define TRP_PROF_EVENT_n_CFG(n)		(0x024020 + 4 * n)
+#define TRP_SCID_n_STATUS(n)		(0x000004 + 0x1000 * n)
+
+/* DRP */
+#define DRP_PROF_EVENT_n_CFG(n)		(0x044010 + 4 * n)
+#define DRP_PROF_CFG			(0x044050)
+
+/* PMGR */
+#define PMGR_PROF_EVENT_n_CFG(n)	(0x03F000 + 4 * n)
+
+#define PERFMON_COUNTER_n_CONFIG(n)	(0x031020 + 4 * n)
+#define PERFMON_MODE			(0x03100C)
+#define PERFMON_DUMP			(0x031010)
+#define BROADCAST_COUNTER_n_VALUE(n)	(0x031060 + 4 * n)
+
+#define LLCC_COUNTER_n_VALUE(n)		(0x031060 + 4 * n)
+
+#define EVENT_NUM_MAX			(64)
+#define SCID_MAX			(32)
+
+/* Perfmon */
+#define CLEAR_ON_ENABLE			BIT(31)
+#define CLEAR_ON_DUMP			BIT(30)
+#define FREEZE_ON_SATURATE		BIT(29)
+#define CHAINING_EN			BIT(28)
+#define COUNT_CLOCK_EVENT		BIT(24)
+
+#define EVENT_SELECT_SHIFT		(16)
+#define PERFMON_EVENT_SELECT_MASK	GENMASK(EVENT_SELECT_SHIFT + 4,\
+						EVENT_SELECT_SHIFT)
+#define PORT_SELECT_SHIFT		(0)
+#define PERFMON_PORT_SELECT_MASK	GENMASK(PORT_SELECT_SHIFT + 3,\
+						PORT_SELECT_SHIFT)
+
+#define MANUAL_MODE			(0)
+#define TIMED_MODE			(1)
+#define TRIGGER_MODE			(2)
+#define MONITOR_EN_SHIFT		(15)
+#define MONITOR_EN			BIT(MONITOR_EN_SHIFT)
+#define PERFMON_MODE_MONITOR_EN_MASK	GENMASK(MONITOR_EN_SHIFT + 0,\
+						MONITOR_EN_SHIFT)
+#define MONITOR_MODE_SHIFT		(0)
+#define PERFMON_MODE_MONITOR_MODE_MASK	GENMASK(MONITOR_MODE_SHIFT + 0,\
+						MONITOR_MODE_SHIFT)
+
+#define MONITOR_DUMP			BIT(0)
+
+/* COMMON */
+#define BYTE_SCALING			(1024)
+#define BEAT_SCALING			(32)
+#define LB_CNT_SHIFT			(28)
+#define LB_CNT_MASK			GENMASK(LB_CNT_SHIFT + 3, \
+						LB_CNT_SHIFT)
+
+#define BYTE_SCALING_SHIFT		(16)
+#define PROF_CFG_BYTE_SCALING_MASK	GENMASK(BYTE_SCALING_SHIFT + 11,\
+						BYTE_SCALING_SHIFT)
+#define BEAT_SCALING_SHIFT		(8)
+#define PROF_CFG_BEAT_SCALING_MASK	GENMASK(BEAT_SCALING_SHIFT + 7,\
+						BEAT_SCALING_SHIFT)
+#define PROF_EN_SHIFT			(0)
+#define PROF_EN				BIT(PROF_EN_SHIFT)
+#define PROF_CFG_EN_MASK		GENMASK(PROF_EN_SHIFT + 0,\
+						PROF_EN_SHIFT)
+
+#define FILTER_EN_SHIFT			(31)
+#define FILTER_EN			BIT(FILTER_EN_SHIFT)
+#define FILTER_EN_MASK			GENMASK(FILTER_EN_SHIFT + 0,\
+						FILTER_EN_SHIFT)
+#define FILTER_0			(0)
+#define FILTER_0_MASK			GENMASK(FILTER_0 + 0, \
+						FILTER_0)
+#define FILTER_1			(1)
+#define FILTER_1_MASK			GENMASK(FILTER_1 + 0, \
+						FILTER_1)
+
+#define FILTER_SEL_SHIFT		(16)
+#define FILTER_SEL_MASK			GENMASK(FILTER_SEL_SHIFT + 0,\
+						FILTER_SEL_SHIFT)
+#define EVENT_SEL_SHIFT			(0)
+#define EVENT_SEL_MASK			GENMASK(EVENT_SEL_SHIFT + 5,\
+						EVENT_SEL_SHIFT)
+
+#define MID_MASK_SHIFT			(16)
+#define MID_MASK_MASK			GENMASK(MID_MASK_SHIFT + 15, \
+						MID_MASK_SHIFT)
+#define MID_MATCH_SHIFT			(0)
+#define MID_MATCH_MASK			GENMASK(MID_MATCH_SHIFT + 15, \
+						MID_MATCH_SHIFT)
+#define SCID_MASK_SHIFT			(16)
+#define SCID_MASK_MASK			GENMASK(SCID_MASK_SHIFT + 15, \
+						SCID_MASK_SHIFT)
+#define SCID_MATCH_SHIFT		(0)
+#define SCID_MATCH_MASK			GENMASK(SCID_MATCH_SHIFT + 15, \
+						SCID_MATCH_SHIFT)
+#define PROFTAG_MASK_SHIFT		(2)
+#define PROFTAG_MASK_MASK		GENMASK(PROFTAG_MASK_SHIFT + 1,\
+						PROFTAG_MASK_SHIFT)
+#define PROFTAG_MATCH_SHIFT		(0)
+#define PROFTAG_MATCH_MASK		GENMASK(PROFTAG_MATCH_SHIFT + 1,\
+						PROFTAG_MATCH_SHIFT)
+/* FEAC */
+#define FEAC_SCALING_FILTER_SEL_SHIFT	(2)
+#define FEAC_SCALING_FILTER_SEL_MASK	GENMASK(FEAC_SCALING_FILTER_SEL_SHIFT \
+					+ 0, \
+					FEAC_SCALING_FILTER_SEL_SHIFT)
+#define FEAC_SCALING_FILTER_EN_SHIFT	(1)
+#define FEAC_SCALING_FILTER_EN		BIT(FEAC_SCALING_FILTER_EN_SHIFT)
+#define FEAC_SCALING_FILTER_EN_MASK	GENMASK(FEAC_SCALING_FILTER_EN_SHIFT \
+					+ 0, \
+					FEAC_SCALING_FILTER_EN_SHIFT)
+/* BEAC */
+#define BEAC_PROFTAG_MASK_SHIFT		(14)
+#define BEAC_PROFTAG_MASK_MASK		GENMASK(BEAC_PROFTAG_MASK_SHIFT + 1,\
+						BEAC_PROFTAG_MASK_SHIFT)
+#define BEAC_PROFTAG_MATCH_SHIFT	(12)
+#define BEAC_PROFTAG_MATCH_MASK		GENMASK(BEAC_PROFTAG_MATCH_SHIFT + 1,\
+						BEAC_PROFTAG_MATCH_SHIFT)
+#define BEAC_MC_PROFTAG_SHIFT		(1)
+#define BEAC_MC_PROFTAG_MASK		GENMASK(BEAC_MC_PROFTAG_SHIFT + 1,\
+					BEAC_MC_PROFTAG_SHIFT)
+/* TRP */
+#define TRP_SCID_MATCH_SHIFT		(0)
+#define TRP_SCID_MATCH_MASK		GENMASK(TRP_SCID_MATCH_SHIFT + 4,\
+						TRP_SCID_MATCH_SHIFT)
+#define TRP_SCID_MASK_SHIFT		(8)
+#define TRP_SCID_MASK_MASK		GENMASK(TRP_SCID_MASK_SHIFT + 4,\
+						TRP_SCID_MASK_SHIFT)
+#define TRP_WAY_ID_MATCH_SHIFT		(16)
+#define TRP_WAY_ID_MATCH_MASK		GENMASK(TRP_WAY_ID_MATCH_SHIFT + 3,\
+						TRP_WAY_ID_MATCH_SHIFT)
+#define TRP_WAY_ID_MASK_SHIFT		(20)
+#define TRP_WAY_ID_MASK_MASK		GENMASK(TRP_WAY_ID_MASK_SHIFT + 3,\
+						TRP_WAY_ID_MASK_SHIFT)
+#define TRP_PROFTAG_MATCH_SHIFT		(24)
+#define TRP_PROFTAG_MATCH_MASK		GENMASK(TRP_PROFTAG_MATCH_SHIFT + 1,\
+						TRP_PROFTAG_MATCH_SHIFT)
+#define TRP_PROFTAG_MASK_SHIFT		(28)
+#define TRP_PROFTAG_MASK_MASK		GENMASK(TRP_PROFTAG_MASK_SHIFT + 1,\
+						TRP_PROFTAG_MASK_SHIFT)
+
+#define TRP_SCID_STATUS_ACTIVE_SHIFT		(0)
+#define TRP_SCID_STATUS_ACTIVE_MASK		GENMASK( \
+						TRP_SCID_STATUS_ACTIVE_SHIFT \
+						+ 0, \
+						TRP_SCID_STATUS_ACTIVE_SHIFT)
+#define TRP_SCID_STATUS_DEACTIVE_SHIFT		(1)
+#define TRP_SCID_STATUS_CURRENT_CAP_SHIFT	(16)
+#define TRP_SCID_STATUS_CURRENT_CAP_MASK	GENMASK( \
+					TRP_SCID_STATUS_CURRENT_CAP_SHIFT \
+					+ 13, \
+					TRP_SCID_STATUS_CURRENT_CAP_SHIFT)
+
+#endif /* _SOC_QCOM_LLCC_PERFMON_H_ */
diff --git a/drivers/soc/qcom/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/smp2p_spinlock_test.c b/drivers/soc/qcom/smp2p_spinlock_test.c
deleted file mode 100644
index d1e5090..0000000
--- a/drivers/soc/qcom/smp2p_spinlock_test.c
+++ /dev/null
@@ -1,820 +0,0 @@
-/* drivers/soc/qcom/smp2p_spinlock_test.c
- *
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/jiffies.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <linux/remote_spinlock.h>
-#include <soc/qcom/smem.h>
-#include "smem_private.h"
-#include "smp2p_private.h"
-#include "smp2p_test_common.h"
-
-#define RS_END_THIEF_PID_BIT 20
-#define RS_END_THIEF_MASK 0x00f00000
-
-/* Spinlock commands used for testing Apps<->RPM spinlocks. */
-enum RPM_SPINLOCK_CMDS {
-	RPM_CMD_INVALID,
-	RPM_CMD_START,
-	RPM_CMD_LOCKED,
-	RPM_CMD_UNLOCKED,
-	RPM_CMD_END,
-};
-
-/* Shared structure for testing Apps<->RPM spinlocks. */
-struct rpm_spinlock_test {
-	uint32_t apps_cmd;
-	uint32_t apps_lock_count;
-	uint32_t rpm_cmd;
-	uint32_t rpm_lock_count;
-};
-
-static uint32_t ut_remote_spinlock_run_time = 1;
-
-/**
- * smp2p_ut_remote_spinlock_core - Verify remote spinlock.
- *
- * @s:           Pointer to output file
- * @remote_pid:  Remote processor to test
- * @use_trylock: Use trylock to prevent an Apps deadlock if the
- *               remote spinlock fails.
- */
-static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
-		bool use_trylock)
-{
-	int failed = 0;
-	unsigned int lock_count = 0;
-	struct msm_smp2p_out *handle = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response;
-	struct mock_cb_data cb_out;
-	struct mock_cb_data cb_in;
-	unsigned long flags;
-	unsigned int n;
-	bool have_lock;
-	bool timeout;
-	int failed_tmp;
-	int spinlock_owner;
-	remote_spinlock_t *smem_spinlock;
-	unsigned long end;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
-	cb_out.initialized = false;
-	cb_in.initialized = false;
-	mock_cb_data_init(&cb_out);
-	mock_cb_data_init(&cb_in);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		/* Open output entry */
-		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-			&cb_out.nb, &handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_out.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
-		/* Open inbound entry */
-		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
-		/* Send start */
-		mock_cb_data_reset(&cb_in);
-		mock_cb_data_reset(&cb_out);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
-		SMP2P_SET_RMT_DATA(test_request, 0x0);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		test_response = SMP2P_GET_RMT_CMD(test_response);
-		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
-				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
-			/* invalid response from remote - abort test */
-			test_request = 0x0;
-			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, 0x0);
-			ret = msm_smp2p_out_write(handle, test_request);
-			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
-					test_response);
-		}
-
-		/* Run spinlock test */
-		if (use_trylock)
-			seq_puts(s, "\tUsing remote_spin_trylock\n");
-		else
-			seq_puts(s, "\tUsing remote_spin_lock\n");
-
-		flags = 0;
-		have_lock = false;
-		timeout = false;
-		spinlock_owner = 0;
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-		end = jiffies + (ut_remote_spinlock_run_time * HZ);
-		if (ut_remote_spinlock_run_time < 300) {
-			seq_printf(s, "\tRunning test for %u seconds; ",
-				ut_remote_spinlock_run_time);
-			seq_puts(s,
-				"on physical hardware please run >= 300 seconds by doing 'echo 300 >  ut_remote_spinlock_time'\n");
-		}
-		while (time_is_after_jiffies(end)) {
-			/* try to acquire spinlock */
-			if (use_trylock) {
-				unsigned long j_start = jiffies;
-
-				while (!remote_spin_trylock_irqsave(
-						smem_spinlock, flags)) {
-					if (jiffies_to_msecs(jiffies - j_start)
-							> 1000) {
-						seq_puts(s,
-							"\tFail: Timeout trying to get the lock\n");
-						timeout = true;
-						break;
-					}
-				}
-				if (timeout)
-					break;
-			} else {
-				remote_spin_lock_irqsave(smem_spinlock, flags);
-			}
-			have_lock = true;
-			++lock_count;
-
-			/* tell the remote side that we have the lock */
-			SMP2P_SET_RMT_DATA(test_request, lock_count);
-			SMP2P_SET_RMT_CMD(test_request,
-					SMP2P_LB_CMD_RSPIN_LOCKED);
-			ret = msm_smp2p_out_write(handle, test_request);
-			UT_ASSERT_INT(ret, ==, 0);
-
-			/* verify the other side doesn't say it has the lock */
-			for (n = 0; n < 1000; ++n) {
-				spinlock_owner =
-					remote_spin_owner(smem_spinlock);
-				if (spinlock_owner != SMEM_APPS) {
-					/* lock stolen by remote side */
-					seq_puts(s, "\tFail: Remote side: ");
-					seq_printf(s, "%d stole lock pid: %d\n",
-						remote_pid, spinlock_owner);
-					failed = true;
-					break;
-				}
-				spinlock_owner = 0;
-
-				ret = msm_smp2p_in_read(remote_pid,
-					SMP2P_RLPB_ENTRY_NAME, &test_response);
-				UT_ASSERT_INT(ret, ==, 0);
-				test_response =
-					SMP2P_GET_RMT_CMD(test_response);
-				UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
-					test_response);
-			}
-			if (failed)
-				break;
-
-			/* tell remote side we are unlocked and release lock */
-			SMP2P_SET_RMT_CMD(test_request,
-					SMP2P_LB_CMD_RSPIN_UNLOCKED);
-			(void)msm_smp2p_out_write(handle, test_request);
-			have_lock = false;
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-		}
-		if (have_lock)
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-
-		/* End test */
-		mock_cb_data_reset(&cb_in);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-		SMP2P_SET_RMT_DATA(test_request, lock_count |
-				(spinlock_owner << RS_END_THIEF_PID_BIT));
-		(void)msm_smp2p_out_write(handle, test_request);
-
-		failed_tmp = failed;
-		failed = false;
-		do {
-			UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-				>, 0);
-			reinit_completion(&cb_in.cb_completion);
-			ret = msm_smp2p_in_read(remote_pid,
-					SMP2P_RLPB_ENTRY_NAME, &test_response);
-			UT_ASSERT_INT(ret, ==, 0);
-		} while (!failed &&
-			SMP2P_GET_RMT_CMD(test_response) !=
-			SMP2P_LB_CMD_RSPIN_END);
-		if (failed)
-			break;
-		failed = failed_tmp;
-
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		seq_puts(s, "\tLocked spinlock ");
-		seq_printf(s, "local %u times; remote %u times",
-			lock_count,
-			test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
-			);
-		if (test_response & RS_END_THIEF_MASK) {
-			seq_puts(s, "Remote side reporting lock stolen by ");
-			seq_printf(s, "pid %d.\n",
-				SMP2P_GET_BITS(test_response,
-					RS_END_THIEF_MASK,
-					RS_END_THIEF_PID_BIT));
-			failed = 1;
-		}
-		seq_puts(s, "\n");
-
-		/* Cleanup */
-		ret = msm_smp2p_out_close(&handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(handle, ==, NULL);
-		ret = msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		if (!failed && !timeout)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		if (handle) {
-			/* send end command */
-			test_request = 0;
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, lock_count);
-			(void)msm_smp2p_out_write(handle, test_request);
-			(void)msm_smp2p_out_close(&handle);
-		}
-		(void)msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
- *
- * @s:           Pointer to output file
- * @pid:         Processor to test
- * @use_trylock: Use trylock to prevent an Apps deadlock if the
- *               remote spinlock fails.
- */
-static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
-		bool use_trylock)
-{
-	struct smp2p_interrupt_config *int_cfg;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
-		return;
-
-	msm_smp2p_deinit_rmt_lpb_proc(pid);
-	smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
-	msm_smp2p_init_rmt_lpb_proc(pid);
-}
-
-/**
- * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
- *
- * @s:   pointer to output file
- */
-static void smp2p_ut_remote_spinlock(struct seq_file *s)
-{
-	int pid;
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
-		smp2p_ut_remote_spinlock_pid(s, pid, false);
-}
-
-/**
- * smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
- *
- * @s:   Pointer to output file
- */
-static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
-{
-	int pid;
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
-		smp2p_ut_remote_spinlock_pid(s, pid, true);
-}
-
-/**
- * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
- *
- * @s:   pointer to output file
- *
- * This test verifies inbound and outbound functionality for all
- * configured remote processor.
- */
-static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_dsps(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_SENSOR_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_cdsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_CDSP_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_tz(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_TZ_PROC, false);
-}
-
-/**
- * smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
- *
- * @s:   pointer to output file
- * @remote_pid:  Remote processor to test
- */
-static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
-{
-	int failed = 0;
-	unsigned long flags;
-	unsigned int n;
-	unsigned int test_num;
-	struct rpm_spinlock_test *data_ptr;
-	remote_spinlock_t *smem_spinlock;
-	bool have_lock;
-
-	seq_printf(s, "Running %s for Apps<->RPM Test\n",
-		   __func__);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		data_ptr = smem_alloc(SMEM_ID_VENDOR0,
-				sizeof(struct rpm_spinlock_test), 0,
-				SMEM_ANY_HOST_FLAG);
-		UT_ASSERT_PTR(0, !=, data_ptr);
-
-		/* Send start */
-		writel_relaxed(0, &data_ptr->apps_lock_count);
-		writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);
-
-		seq_puts(s, "\tWaiting for RPM to start test\n");
-		for (n = 0; n < 1000; ++n) {
-			if (readl_relaxed(&data_ptr->rpm_cmd) !=
-					RPM_CMD_INVALID)
-				break;
-			usleep_range(1000, 1200);
-		}
-		if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
-			/* timeout waiting for RPM */
-			writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
-			UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
-		}
-
-		/* Run spinlock test */
-		flags = 0;
-		have_lock = false;
-		for (test_num = 0; !failed && test_num < 10000; ++test_num) {
-			/* acquire spinlock */
-			remote_spin_lock_irqsave(smem_spinlock, flags);
-			have_lock = true;
-			data_ptr->apps_lock_count++;
-			writel_relaxed(data_ptr->apps_lock_count,
-				&data_ptr->apps_lock_count);
-			writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
-			/*
-			 * Ensure that the remote side sees our lock has
-			 * been acquired before we start polling their status.
-			 */
-			wmb();
-
-			/* verify the other side doesn't say it has the lock */
-			for (n = 0; n < 1000; ++n) {
-				UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
-					readl_relaxed(&data_ptr->rpm_cmd));
-			}
-			if (failed)
-				break;
-
-			/* release spinlock */
-			have_lock = false;
-			writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
-			/*
-			 * Ensure that our status-update write was committed
-			 * before we unlock the spinlock.
-			 */
-			wmb();
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-		}
-		if (have_lock)
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-
-		/* End test */
-		writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
-		seq_printf(s, "\tLocked spinlock local %u remote %u\n",
-				readl_relaxed(&data_ptr->apps_lock_count),
-				readl_relaxed(&data_ptr->rpm_lock_count));
-
-		if (!failed)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-struct rmt_spinlock_work_item {
-	struct work_struct work;
-	struct completion try_lock;
-	struct completion locked;
-	bool has_locked;
-};
-
-static void ut_remote_spinlock_ssr_worker(struct work_struct *work)
-{
-	remote_spinlock_t *smem_spinlock;
-	unsigned long flags;
-	struct rmt_spinlock_work_item *work_item =
-		container_of(work, struct rmt_spinlock_work_item, work);
-
-	work_item->has_locked = false;
-	complete(&work_item->try_lock);
-	smem_spinlock = smem_get_remote_spinlock();
-	if (!smem_spinlock) {
-		pr_err("%s Failed\n", __func__);
-		return;
-	}
-
-	remote_spin_lock_irqsave(smem_spinlock, flags);
-	remote_spin_unlock_irqrestore(smem_spinlock, flags);
-	work_item->has_locked = true;
-	complete(&work_item->locked);
-}
-
-/**
- * smp2p_ut_remote_spinlock_ssr - Verify remote spinlock.
- *
- * @s:   pointer to output file
- */
-static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s)
-{
-	int failed = 0;
-	unsigned long flags;
-	remote_spinlock_t *smem_spinlock;
-	int spinlock_owner = 0;
-
-	struct workqueue_struct *ws = NULL;
-	struct rmt_spinlock_work_item work_item = { .has_locked = false };
-
-	seq_printf(s, " Running %s Test\n",
-		   __func__);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		ws = create_singlethread_workqueue("ut_remote_spinlock_ssr");
-		UT_ASSERT_PTR(ws, !=, NULL);
-		INIT_WORK(&work_item.work, ut_remote_spinlock_ssr_worker);
-		init_completion(&work_item.try_lock);
-		init_completion(&work_item.locked);
-
-		remote_spin_lock_irqsave(smem_spinlock, flags);
-		/* Unlock local spin lock and hold HW spinlock */
-		spin_unlock_irqrestore(&((smem_spinlock)->local), flags);
-
-		queue_work(ws, &work_item.work);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&work_item.try_lock, HZ * 2), >, 0);
-		UT_ASSERT_INT((int)work_item.has_locked, ==, 0);
-		spinlock_owner = remote_spin_owner(smem_spinlock);
-		UT_ASSERT_INT(spinlock_owner, ==, SMEM_APPS);
-		remote_spin_release_all(SMEM_APPS);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&work_item.locked, HZ * 2), >, 0);
-
-		if (!failed)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_spinlock_track_core - Verify remote spinlock.
- *
- * @s:           Pointer to output file
- * @remote_pid:  Remote processor to test
- *
- * This test has the remote subsystem grab the lock, and then has the local
- * subsystem attempt to grab the lock using the trylock() API. It then verifies
- * that the ID in the hw_spinlocks array matches the owner of the lock.
- */
-static void smp2p_ut_remote_spinlock_track_core(struct seq_file *s,
-		int remote_pid)
-{
-	int failed = 0;
-	struct msm_smp2p_out *handle = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response;
-	struct mock_cb_data cb_out;
-	struct mock_cb_data cb_in;
-	unsigned long flags;
-	int stored_value;
-	remote_spinlock_t *smem_spinlock;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
-	cb_out.initialized = false;
-	cb_in.initialized = false;
-	mock_cb_data_init(&cb_out);
-	mock_cb_data_init(&cb_in);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		/* Open output entry */
-		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-			&cb_out.nb, &handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_out.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
-		/* Open inbound entry */
-		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
-		/* Send start */
-		mock_cb_data_reset(&cb_in);
-		mock_cb_data_reset(&cb_out);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
-		SMP2P_SET_RMT_DATA(test_request, 0x0);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		test_response = SMP2P_GET_RMT_CMD(test_response);
-		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
-				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
-			/* invalid response from remote - abort test */
-			test_request = 0x0;
-			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, 0x0);
-			ret = msm_smp2p_out_write(handle, test_request);
-			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
-					test_response);
-		}
-
-		/* Run spinlock test */
-		flags = 0;
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-
-		/* try to acquire spinlock */
-		remote_spin_trylock_irqsave(smem_spinlock, flags);
-		/*
-		 * Need to check against the locking token (PID + 1)
-		 * because the remote_spin_owner() API only returns the
-		 * PID.
-		 */
-		stored_value = remote_spin_get_hw_spinlocks_element(
-				smem_spinlock);
-		UT_ASSERT_INT(stored_value, ==,
-			remote_spin_owner(smem_spinlock) + 1);
-		UT_ASSERT_INT(stored_value, ==, remote_pid + 1);
-
-		/* End test */
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-		SMP2P_SET_RMT_DATA(test_request, 0x0);
-		(void)msm_smp2p_out_write(handle, test_request);
-
-		/* Cleanup */
-		ret = msm_smp2p_out_close(&handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(handle, ==, NULL);
-		ret = msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		if (!failed)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		if (handle) {
-			/* send end command */
-			test_request = 0x0;
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, 0x0);
-			(void)msm_smp2p_out_write(handle, test_request);
-			(void)msm_smp2p_out_close(&handle);
-		}
-		(void)msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_spinlock_track - Verify PID tracking for modem.
- *
- * @s:	Pointer to output file
- * @pid:		The processor to test
- */
-static void smp2p_ut_remote_spinlock_track(struct seq_file *s, int pid)
-{
-	struct smp2p_interrupt_config *int_cfg;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
-		return;
-
-	msm_smp2p_deinit_rmt_lpb_proc(pid);
-	smp2p_ut_remote_spinlock_track_core(s, pid);
-	msm_smp2p_init_rmt_lpb_proc(pid);
-}
-
-/**
- * smp2p_ut_remote_spinlock_track - Verify PID tracking for all processors.
- *
- * @s:	Pointer to output file
- *
- * This test verifies PID tracking for all configured remote processors.
- */
-static void smp2p_ut_remote_spinlock_track_modem(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_MODEM_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_adsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_AUDIO_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_dsps(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_SENSOR_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_wcnss(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_WIRELESS_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_cdsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_CDSP_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_tz(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_TZ_PROC);
-}
-
-static int __init smp2p_debugfs_init(void)
-{
-	/*
-	 * Add Unit Test entries.
-	 *
-	 * The idea with unit tests is that you can run all of them
-	 * from ADB shell by doing:
-	 *  adb shell
-	 *  cat ut*
-	 *
-	 * And if particular tests fail, you can then repeatedly run the
-	 * failing tests as you debug and resolve the failing test.
-	 */
-	smp2p_debug_create("ut_remote_spinlock",
-		smp2p_ut_remote_spinlock);
-	smp2p_debug_create("ut_remote_spin_trylock",
-		smp2p_ut_remote_spin_trylock);
-	smp2p_debug_create("ut_remote_spinlock_modem",
-		smp2p_ut_remote_spinlock_modem);
-	smp2p_debug_create("ut_remote_spinlock_adsp",
-		smp2p_ut_remote_spinlock_adsp);
-	smp2p_debug_create("ut_remote_spinlock_dsps",
-		smp2p_ut_remote_spinlock_dsps);
-	smp2p_debug_create("ut_remote_spinlock_wcnss",
-		smp2p_ut_remote_spinlock_wcnss);
-	smp2p_debug_create("ut_remote_spinlock_cdsp",
-		smp2p_ut_remote_spinlock_cdsp);
-	smp2p_debug_create("ut_remote_spinlock_tz",
-		smp2p_ut_remote_spinlock_tz);
-	smp2p_debug_create("ut_remote_spinlock_rpm",
-		smp2p_ut_remote_spinlock_rpm);
-	smp2p_debug_create_u32("ut_remote_spinlock_time",
-		&ut_remote_spinlock_run_time);
-	smp2p_debug_create("ut_remote_spinlock_ssr",
-		&smp2p_ut_remote_spinlock_ssr);
-	smp2p_debug_create("ut_remote_spinlock_track_modem",
-		&smp2p_ut_remote_spinlock_track_modem);
-	smp2p_debug_create("ut_remote_spinlock_track_adsp",
-		&smp2p_ut_remote_spinlock_track_adsp);
-	smp2p_debug_create("ut_remote_spinlock_track_dsps",
-		&smp2p_ut_remote_spinlock_track_dsps);
-	smp2p_debug_create("ut_remote_spinlock_track_wcnss",
-		&smp2p_ut_remote_spinlock_track_wcnss);
-	smp2p_debug_create("ut_remote_spinlock_track_cdsp",
-		&smp2p_ut_remote_spinlock_track_cdsp);
-	smp2p_debug_create("ut_remote_spinlock_track_tz",
-		&smp2p_ut_remote_spinlock_track_tz);
-	return 0;
-}
-module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test.c b/drivers/soc/qcom/smp2p_test.c
deleted file mode 100644
index aa8d0c8..0000000
--- a/drivers/soc/qcom/smp2p_test.c
+++ /dev/null
@@ -1,1324 +0,0 @@
-/* drivers/soc/qcom/smp2p_test.c
- *
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/jiffies.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <soc/qcom/subsystem_restart.h>
-#include "smp2p_private.h"
-#include "smp2p_test_common.h"
-
-/**
- * smp2p_ut_local_basic - Basic sanity test using local loopback.
- *
- * @s: pointer to output file
- *
- * This test simulates a simple write and read
- * when remote processor does not exist.
- */
-static void smp2p_ut_local_basic(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_out *smp2p_obj;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_data;
-
-	seq_printf(s, "Running %s\n", __func__);
-	mock_cb_data_init(&cb_data);
-	do {
-		/* initialize mock edge and start opening */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-
-		msm_smp2p_set_remote_mock_exists(false);
-
-		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
-			&cb_data.nb, &smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
-		rmp->rx_interrupt_count = 0;
-
-		/* simulate response from remote side */
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-					SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-					SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		/* verify port was opened */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 2), >, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_data.event_open, ==, 1);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* do write (test outbound entries) */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0xC0DE;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* do read (test inbound entries) */
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(test_request, ==, test_response);
-
-		ret = msm_smp2p_out_close(&smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		(void)msm_smp2p_out_close(&smp2p_obj);
-	}
-}
-
-/**
- * smp2p_ut_local_late_open - Verify post-negotiation opening.
- *
- * @s: pointer to output file
- *
- * Verify entry creation for opening entries after negotiation is complete.
- */
-static void smp2p_ut_local_late_open(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_out *smp2p_obj;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_data;
-
-	seq_printf(s, "Running %s\n", __func__);
-	mock_cb_data_init(&cb_data);
-	do {
-		/* initialize mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-			rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-			rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-			rmp->remote_item.header.valid_total_ent,
-			SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-
-		msm_smp2p_set_remote_mock_exists(true);
-
-		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
-			&cb_data.nb, &smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* verify port was opened */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_data.event_open, ==, 1);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* do write (test outbound entries) */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0xC0DE;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* do read (test inbound entries) */
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(test_request, ==, test_response);
-
-		ret = msm_smp2p_out_close(&smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		(void)msm_smp2p_out_close(&smp2p_obj);
-	}
-}
-
-/**
- * smp2p_ut_local_early_open - Verify pre-negotiation opening.
- *
- * @s: pointer to output file
- *
- * Verify entry creation for opening entries before negotiation is complete.
- */
-static void smp2p_ut_local_early_open(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_out *smp2p_obj;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	struct smp2p_smem *outbound_item;
-	int negotiation_state;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_data;
-
-	seq_printf(s, "Running %s\n", __func__);
-	mock_cb_data_init(&cb_data);
-	do {
-		/* initialize mock edge, but don't enable, yet */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-
-		msm_smp2p_set_remote_mock_exists(false);
-		UT_ASSERT_PTR(NULL, ==,
-				smp2p_get_in_item(SMP2P_REMOTE_MOCK_PROC));
-
-		/* initiate open, but verify it doesn't complete */
-		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
-			&cb_data.nb, &smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 8),
-			==, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
-		UT_ASSERT_INT(cb_data.event_open, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		outbound_item = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
-				&negotiation_state);
-		UT_ASSERT_PTR(outbound_item, !=, NULL);
-		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENING);
-		UT_ASSERT_INT(0, ==,
-			SMP2P_GET_ENT_VALID(outbound_item->valid_total_ent));
-
-		/* verify that read/write don't work yet */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0x0;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, -ENODEV);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
-
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, -ENODEV);
-
-		/* allocate remote entry and verify open */
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_data.event_open, ==, 1);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* do write (test outbound entries) */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0xC0DE;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* do read (test inbound entries) */
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(test_request, ==, test_response);
-
-		ret = msm_smp2p_out_close(&smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		(void)msm_smp2p_out_close(&smp2p_obj);
-	}
-}
-
-/**
- * smp2p_ut_mock_loopback - Exercise the remote loopback using remote mock.
- *
- * @s: pointer to output file
- *
- * This test exercises the remote loopback code using
- * remote mock object. The remote mock object simulates the remote
- * processor sending remote loopback commands to the local processor.
- */
-static void smp2p_ut_mock_loopback(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	uint32_t test_request = 0;
-	uint32_t test_response = 0;
-	struct msm_smp2p_out  *local;
-
-	seq_printf(s, "Running %s\n", __func__);
-	do {
-		/* Initialize the mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 1);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-
-		/* Create test entry and attach loopback server */
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		strlcpy(rmp->remote_item.entries[0].name, "smp2p",
-							SMP2P_MAX_ENTRY_NAME);
-		rmp->remote_item.entries[0].entry = 0;
-		rmp->tx_interrupt();
-
-		local = msm_smp2p_init_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* Send Echo Command */
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
-		SMP2P_SET_RMT_DATA(test_request, 10);
-		rmp->remote_item.entries[0].entry = test_request;
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-
-		/* Verify Echo Response */
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		ret = msm_smp2p_out_read(local,
-							&test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		UT_ASSERT_INT(test_response, ==, 10);
-
-		/* Send PINGPONG command */
-		test_request = 0;
-		test_response = 0;
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
-		SMP2P_SET_RMT_DATA(test_request, 10);
-		rmp->remote_item.entries[0].entry = test_request;
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-
-		/* Verify PINGPONG Response */
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		ret = msm_smp2p_out_read(local, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		UT_ASSERT_INT(test_response, ==, 9);
-
-		/* Send CLEARALL command */
-		test_request = 0;
-		test_response = 0;
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
-		SMP2P_SET_RMT_DATA(test_request, 10);
-		rmp->remote_item.entries[0].entry = test_request;
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-
-		/* Verify CLEARALL response */
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		ret = msm_smp2p_out_read(local, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		UT_ASSERT_INT(test_response, ==, 0);
-
-		ret = msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
-		UT_ASSERT_INT(ret, ==, 0);
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
-	}
-}
-
-/**
- * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
- *
- * @s: pointer to output file
- * @remote_pid:  Remote processor to test
- *
- * This test verifies inbound/outbound functionality for the remote processor.
- */
-static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid)
-{
-	int failed = 0;
-	struct msm_smp2p_out *handle;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_out;
-	static struct mock_cb_data cb_in;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-	mock_cb_data_init(&cb_out);
-	mock_cb_data_init(&cb_in);
-	do {
-		/* Open output entry */
-		ret = msm_smp2p_out_open(remote_pid, "smp2p",
-			&cb_out.nb, &handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_out.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
-		/* Open inbound entry */
-		ret = msm_smp2p_in_register(remote_pid, "smp2p",
-				&cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
-		/* Write an echo request */
-		mock_cb_data_reset(&cb_out);
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* Verify inbound reply */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
-			    cb_in.entry_data.current_value), ==, 0xAA55);
-
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
-		UT_ASSERT_INT(SMP2P_LB_CMD_ECHO, ==,
-				SMP2P_GET_RMT_CMD(test_response));
-		UT_ASSERT_INT(0xAA55, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Write a clear all request */
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* Verify inbound reply */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
-			    cb_in.entry_data.current_value), ==, 0x0000);
-
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
-		UT_ASSERT_INT(0x0000, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Write a decrement request */
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* Verify inbound reply */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
-			    cb_in.entry_data.current_value), ==, 0xAA54);
-
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
-		UT_ASSERT_INT(SMP2P_LB_CMD_PINGPONG, ==,
-				SMP2P_GET_RMT_CMD(test_response));
-		UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Test the ignore flag */
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_RLPB_IGNORE);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			==, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 0);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 0);
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Cleanup */
-		ret = msm_smp2p_out_close(&handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(handle, ==, 0);
-		ret = msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		if (handle)
-			(void)msm_smp2p_out_close(&handle);
-		(void)msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
-
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
- *
- * @s: pointer to output file
- *
- * This test verifies inbound and outbound functionality for all
- * configured remote processor.
- */
-static void smp2p_ut_remote_inout(struct seq_file *s)
-{
-	struct smp2p_interrupt_config *int_cfg;
-	int pid;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
-		if (!int_cfg[pid].is_configured)
-			continue;
-
-		msm_smp2p_deinit_rmt_lpb_proc(pid);
-		smp2p_ut_remote_inout_core(s, pid);
-		msm_smp2p_init_rmt_lpb_proc(pid);
-	}
-}
-
-/**
- * smp2p_ut_remote_out_max_entries_core - Verify open functionality.
- *
- * @s: pointer to output file
- * @remote_pid:  Remote processor for which the test is executed.
- *
- * This test verifies open functionality by creating maximum outbound entries.
- */
-static void smp2p_ut_remote_out_max_entries_core(struct seq_file *s,
-	int remote_pid)
-{
-	int j = 0;
-	int failed = 0;
-	struct msm_smp2p_out *handle[SMP2P_MAX_ENTRY];
-	int ret;
-	static struct mock_cb_data cb_out[SMP2P_MAX_ENTRY];
-	char entry_name[SMP2P_MAX_ENTRY_NAME];
-	int num_created;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
-	for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-		handle[j] = NULL;
-		mock_cb_data_init(&cb_out[j]);
-	}
-
-	do {
-		num_created = 0;
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			/* Open as many output entries as possible */
-			scnprintf((char *)entry_name, SMP2P_MAX_ENTRY_NAME,
-				"smp2p%d", j);
-			ret = msm_smp2p_out_open(remote_pid, entry_name,
-				&cb_out[j].nb, &handle[j]);
-			if (ret == -ENOMEM)
-				/* hit max number */
-				break;
-			UT_ASSERT_INT(ret, ==, 0);
-			++num_created;
-		}
-		if (failed)
-			break;
-
-		/* verify we created more than 1 entry */
-		UT_ASSERT_INT(num_created, <=, SMP2P_MAX_ENTRY);
-		UT_ASSERT_INT(num_created, >, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-
-	/* cleanup */
-	for (j = 0; j < SMP2P_MAX_ENTRY; j++)
-		ret = msm_smp2p_out_close(&handle[j]);
-}
-
-/**
- * smp2p_ut_remote_out_max_entries - Verify open for all configured processors.
- *
- * @s: pointer to output file
- *
- * This test verifies creating max number of entries for
- * all configured remote processor.
- */
-static void smp2p_ut_remote_out_max_entries(struct seq_file *s)
-{
-	struct smp2p_interrupt_config *int_cfg;
-	int pid;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
-		if (!int_cfg[pid].is_configured)
-			continue;
-
-		smp2p_ut_remote_out_max_entries_core(s, pid);
-	}
-}
-
-/**
- * smp2p_ut_local_in_max_entries - Verify registering and unregistering.
- *
- * @s: pointer to output file
- *
- * This test verifies registering and unregistering for inbound entries using
- * the remote mock processor.
- */
-static void smp2p_ut_local_in_max_entries(struct seq_file *s)
-{
-	int j = 0;
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	static struct mock_cb_data cb_in[SMP2P_MAX_ENTRY];
-	static struct mock_cb_data cb_out;
-
-	seq_printf(s, "Running %s\n", __func__);
-
-	for (j = 0; j < SMP2P_MAX_ENTRY; j++)
-		mock_cb_data_init(&cb_in[j]);
-
-	mock_cb_data_init(&cb_out);
-
-	do {
-		/* Initialize mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-
-		/* Create Max Entries in the remote mock object */
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			scnprintf(rmp->remote_item.entries[j].name,
-				SMP2P_MAX_ENTRY_NAME, "smp2p%d", j);
-			rmp->remote_item.entries[j].entry = 0;
-			rmp->tx_interrupt();
-		}
-
-		/* Register for in entries */
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[j].name,
-				&(cb_in[j].nb));
-			UT_ASSERT_INT(ret, ==, 0);
-			UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-					&(cb_in[j].cb_completion), HZ / 2),
-				>, 0);
-			UT_ASSERT_INT(cb_in[j].cb_count, ==, 1);
-			UT_ASSERT_INT(cb_in[j].event_entry_update, ==, 0);
-		}
-		UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
-
-		/* Unregister */
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[j].name,
-				&(cb_in[j].nb));
-		    UT_ASSERT_INT(ret, ==, 0);
-		}
-		UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++)
-			ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[j].name,
-				&(cb_in[j].nb));
-	}
-}
-
-/**
- * smp2p_ut_local_in_multiple - Verify Multiple Inbound Registration.
- *
- * @s: pointer to output file
- *
- * This test verifies multiple clients registering for same inbound entries
- * using the remote mock processor.
- */
-static void smp2p_ut_local_in_multiple(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	static struct mock_cb_data cb_in_1;
-	static struct mock_cb_data cb_in_2;
-	static struct mock_cb_data cb_out;
-
-	seq_printf(s, "Running %s\n", __func__);
-
-	mock_cb_data_init(&cb_in_1);
-	mock_cb_data_init(&cb_in_2);
-	mock_cb_data_init(&cb_out);
-
-	do {
-		/* Initialize mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, 1);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-
-		/* Create an Entry in the remote mock object */
-		scnprintf(rmp->remote_item.entries[0].name,
-				SMP2P_MAX_ENTRY_NAME, "smp2p%d", 1);
-		rmp->remote_item.entries[0].entry = 0;
-		rmp->tx_interrupt();
-
-		/* Register multiple clients for the inbound entry */
-		ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&cb_in_1.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-				&(cb_in_1.cb_completion), HZ / 2),
-				>, 0);
-		UT_ASSERT_INT(cb_in_1.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in_1.event_entry_update, ==, 0);
-
-		ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&cb_in_2.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-				&(cb_in_2.cb_completion), HZ / 2),
-				>, 0);
-		UT_ASSERT_INT(cb_in_2.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in_2.event_entry_update, ==, 0);
-
-
-		/* Unregister the clients */
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_1.nb));
-		UT_ASSERT_INT(ret, ==, 0);
-
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_2.nb));
-		UT_ASSERT_INT(ret, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_1.nb));
-
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_2.nb));
-	}
-}
-
-/**
- * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- */
-static void smp2p_ut_local_ssr_ack(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-
-	seq_printf(s, "Running %s\n", __func__);
-	do {
-		struct smp2p_smem *rhdr;
-		struct smp2p_smem *lhdr;
-		int negotiation_state;
-
-		/* initialize v1 without SMP2P_FEATURE_SSR_ACK enabled */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-		rhdr = &rmp->remote_item.header;
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
-		rhdr->magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
-				SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(rhdr->feature_version, 1);
-		SMP2P_SET_FEATURES(rhdr->feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
-		rhdr->flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		/* verify edge is open */
-		lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
-					&negotiation_state);
-		UT_ASSERT_PTR(NULL, !=, lhdr);
-		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* verify no response to ack feature */
-		rmp->rx_interrupt_count = 0;
-		SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
-
-		/* initialize v1 with SMP2P_FEATURE_SSR_ACK enabled */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-		rhdr = &rmp->remote_item.header;
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
-		rhdr->magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
-				SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(rhdr->feature_version, 1);
-		SMP2P_SET_FEATURES(rhdr->feature_version,
-				SMP2P_FEATURE_SSR_ACK);
-		SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
-		rmp->rx_interrupt_count = 0;
-		rhdr->flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		/* verify edge is open */
-		lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
-					&negotiation_state);
-		UT_ASSERT_PTR(NULL, !=, lhdr);
-		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* verify response to ack feature */
-		rmp->rx_interrupt_count = 0;
-		SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
-		UT_ASSERT_INT(1, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		rmp->rx_interrupt_count = 0;
-		SMP2P_SET_RESTART_DONE(rhdr->flags, 0);
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * get_ssr_name_for_proc - Retrieve an SSR name from the provided list
- *
- * @names:	List of possible processor names
- * @name_len:	The length of @names
- * @index:	Index into @names
- *
- * Return: Pointer to the next processor name, NULL in error conditions
- */
-static char *get_ssr_name_for_proc(char * const names[], size_t name_len,
-				   int index)
-{
-	if (index >= name_len) {
-		pr_err("%s: SSR failed; check subsys name table\n",
-				__func__);
-		return NULL;
-	}
-
-	return names[index];
-}
-
-/**
- * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- * @rpid: Remote processor ID
- * @int_cfg: Interrupt config
- */
-static void smp2p_ut_remotesubsys_ssr_ack(struct seq_file *s, uint32_t rpid,
-		struct smp2p_interrupt_config *int_cfg)
-{
-	int failed = 0;
-
-	seq_printf(s, "Running %s\n", __func__);
-	do {
-		struct smp2p_smem *rhdr;
-		struct smp2p_smem *lhdr;
-		int negotiation_state;
-		int name_index;
-		int ret;
-		uint32_t ssr_done_start;
-		bool ssr_ack_enabled = false;
-		bool ssr_success = false;
-		char *name = NULL;
-
-		static char * const mpss_names[] = {"modem", "mpss"};
-		static char * const lpass_names[] = {"adsp", "lpass"};
-		static char * const sensor_names[] = {"slpi", "dsps"};
-		static char * const wcnss_names[] = {"wcnss"};
-
-		lhdr = smp2p_get_out_item(rpid, &negotiation_state);
-		UT_ASSERT_PTR(NULL, !=, lhdr);
-		UT_ASSERT_INT(SMP2P_EDGE_STATE_OPENED, ==, negotiation_state);
-
-		rhdr = smp2p_get_in_item(rpid);
-		UT_ASSERT_PTR(NULL, !=, rhdr);
-
-		/* get initial state of SSR flags */
-		if (SMP2P_GET_FEATURES(rhdr->feature_version)
-				& SMP2P_FEATURE_SSR_ACK)
-			ssr_ack_enabled = true;
-		else
-			ssr_ack_enabled = false;
-
-		ssr_done_start = SMP2P_GET_RESTART_DONE(rhdr->flags);
-		UT_ASSERT_INT(ssr_done_start, ==,
-				SMP2P_GET_RESTART_ACK(lhdr->flags));
-
-		/* trigger restart */
-		name_index = 0;
-		while (!ssr_success) {
-
-			switch (rpid) {
-			case SMP2P_MODEM_PROC:
-				name = get_ssr_name_for_proc(mpss_names,
-						ARRAY_SIZE(mpss_names),
-						name_index);
-				break;
-			case SMP2P_AUDIO_PROC:
-				name = get_ssr_name_for_proc(lpass_names,
-						ARRAY_SIZE(lpass_names),
-						name_index);
-				break;
-			case SMP2P_SENSOR_PROC:
-				name = get_ssr_name_for_proc(sensor_names,
-						ARRAY_SIZE(sensor_names),
-						name_index);
-				break;
-			case SMP2P_WIRELESS_PROC:
-				name = get_ssr_name_for_proc(wcnss_names,
-						ARRAY_SIZE(wcnss_names),
-						name_index);
-				break;
-			default:
-				pr_err("%s: Invalid proc ID %d given for ssr\n",
-						__func__, rpid);
-			}
-
-			if (!name) {
-				seq_puts(s, "\tSSR failed; check subsys name table\n");
-				failed = true;
-				break;
-			}
-
-			seq_printf(s, "Restarting '%s'\n", name);
-			ret = subsystem_restart(name);
-			if (ret == -ENODEV) {
-				seq_puts(s, "\tSSR call failed\n");
-				++name_index;
-				continue;
-			}
-			ssr_success = true;
-		}
-		if (failed)
-			break;
-
-		msleep(10*1000);
-
-		/* verify ack signaling */
-		if (ssr_ack_enabled) {
-			ssr_done_start ^= 1;
-			UT_ASSERT_INT(ssr_done_start, ==,
-					SMP2P_GET_RESTART_ACK(lhdr->flags));
-			UT_ASSERT_INT(ssr_done_start, ==,
-					SMP2P_GET_RESTART_DONE(rhdr->flags));
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_DONE(lhdr->flags));
-			seq_puts(s, "\tSSR ACK Enabled and Toggled\n");
-		} else {
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_DONE(lhdr->flags));
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_ACK(lhdr->flags));
-
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_DONE(rhdr->flags));
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_ACK(rhdr->flags));
-			seq_puts(s, "\tSSR ACK Disabled\n");
-		}
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- *
- * Triggers SSR for each subsystem.
- */
-static void smp2p_ut_remote_ssr_ack(struct seq_file *s)
-{
-	struct smp2p_interrupt_config *int_cfg;
-	int pid;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s,
-			"Remote processor config unavailable\n");
-		return;
-	}
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
-		if (!int_cfg[pid].is_configured)
-			continue;
-
-		msm_smp2p_deinit_rmt_lpb_proc(pid);
-		smp2p_ut_remotesubsys_ssr_ack(s, pid, &int_cfg[pid]);
-		msm_smp2p_init_rmt_lpb_proc(pid);
-	}
-}
-
-static struct dentry *dent;
-
-static int debugfs_show(struct seq_file *s, void *data)
-{
-	void (*show)(struct seq_file *) = s->private;
-
-	show(s);
-
-	return 0;
-}
-
-static int debug_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, debugfs_show, inode->i_private);
-}
-
-static const struct file_operations debug_ops = {
-	.open = debug_open,
-	.release = single_release,
-	.read = seq_read,
-	.llseek = seq_lseek,
-};
-
-void smp2p_debug_create(const char *name,
-			 void (*show)(struct seq_file *))
-{
-	struct dentry *file;
-
-	file = debugfs_create_file(name, 0444, dent, show, &debug_ops);
-	if (!file)
-		pr_err("%s: unable to create file '%s'\n", __func__, name);
-}
-
-void smp2p_debug_create_u32(const char *name, uint32_t *value)
-{
-	struct dentry *file;
-
-	file = debugfs_create_u32(name, 0644, dent, value);
-	if (!file)
-		pr_err("%s: unable to create file '%s'\n", __func__, name);
-}
-
-static int __init smp2p_debugfs_init(void)
-{
-	dent = debugfs_create_dir("smp2p_test", 0);
-	if (IS_ERR(dent))
-		return PTR_ERR(dent);
-
-	/*
-	 * Add Unit Test entries.
-	 *
-	 * The idea with unit tests is that you can run all of them
-	 * from ADB shell by doing:
-	 *  adb shell
-	 *  cat ut*
-	 *
-	 * And if particular tests fail, you can then repeatedly run the
-	 * failing tests as you debug and resolve the failing test.
-	 */
-	smp2p_debug_create("ut_local_basic",
-			smp2p_ut_local_basic);
-	smp2p_debug_create("ut_local_late_open",
-			smp2p_ut_local_late_open);
-	smp2p_debug_create("ut_local_early_open",
-			smp2p_ut_local_early_open);
-	smp2p_debug_create("ut_mock_loopback",
-			smp2p_ut_mock_loopback);
-	smp2p_debug_create("ut_remote_inout",
-			smp2p_ut_remote_inout);
-	smp2p_debug_create("ut_local_in_max_entries",
-		smp2p_ut_local_in_max_entries);
-	smp2p_debug_create("ut_remote_out_max_entries",
-			smp2p_ut_remote_out_max_entries);
-	smp2p_debug_create("ut_local_in_multiple",
-			smp2p_ut_local_in_multiple);
-	smp2p_debug_create("ut_local_ssr_ack",
-			smp2p_ut_local_ssr_ack);
-	smp2p_debug_create("ut_remote_ssr_ack",
-			smp2p_ut_remote_ssr_ack);
-
-	return 0;
-}
-module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test_common.h b/drivers/soc/qcom/smp2p_test_common.h
deleted file mode 100644
index 0d22fec..0000000
--- a/drivers/soc/qcom/smp2p_test_common.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/* drivers/soc/qcom/smp2p_test_common.h
- *
- * Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#ifndef _SMP2P_TEST_COMMON_H_
-#define _SMP2P_TEST_COMMON_H_
-
-#include <linux/debugfs.h>
-
-/**
- * Unit test assertion for logging test cases.
- *
- * @a lval
- * @b rval
- * @cmp comparison operator
- *
- * Assertion fails if (@a cmp @b) is not true which then
- * logs the function and line number where the error occurred
- * along with the values of @a and @b.
- *
- * Assumes that the following local variables exist:
- * @s - sequential output file pointer
- * @failed - set to true if test fails
- */
-#define UT_ASSERT_INT(a, cmp, b) \
-	{ \
-	int a_tmp = (a); \
-	int b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-#define UT_ASSERT_PTR(a, cmp, b) \
-	{ \
-	void *a_tmp = (a); \
-	void *b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%pK) " #cmp \
-				" " #b "(%pK)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-#define UT_ASSERT_UINT(a, cmp, b) \
-	{ \
-	unsigned int a_tmp = (a); \
-	unsigned int b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-#define UT_ASSERT_HEX(a, cmp, b) \
-	{ \
-	unsigned int a_tmp = (a); \
-	unsigned int b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%x) " #cmp " " #b "(%x)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-/**
- * In-range unit test assertion for test cases.
- *
- * @a lval
- * @minv Minimum value
- * @maxv Maximum value
- *
- * Assertion fails if @a is not on the exclusive range minv, maxv
- * ((@a < @minv) or (@a > @maxv)).  In the failure case, the macro
- * logs the function and line number where the error occurred along
- * with the values of @a and @minv, @maxv.
- *
- * Assumes that the following local variables exist:
- * @s - sequential output file pointer
- * @failed - set to true if test fails
- */
-#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
-	{ \
-	int a_tmp = (a); \
-	int minv_tmp = (minv); \
-	int maxv_tmp = (maxv); \
-	if (((a_tmp) < (minv_tmp)) || ((a_tmp) > (maxv_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
-				 #a "(%d) > " #maxv "(%d)\n", \
-				__func__, __LINE__, \
-				a_tmp, minv_tmp, a_tmp, maxv_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-/* Structure to track state changes for the notifier callback. */
-struct mock_cb_data {
-	bool initialized;
-	spinlock_t lock;
-	struct notifier_block nb;
-
-	/* events */
-	struct completion cb_completion;
-	int cb_count;
-	int event_open;
-	int event_entry_update;
-	struct msm_smp2p_update_notif entry_data;
-};
-
-void smp2p_debug_create(const char *name, void (*show)(struct seq_file *));
-void smp2p_debug_create_u32(const char *name, uint32_t *value);
-static inline int smp2p_test_notify(struct notifier_block *self,
-	unsigned long event, void *data);
-
-/**
- * Reset mock callback data to default values.
- *
- * @cb:  Mock callback data
- */
-static inline void mock_cb_data_reset(struct mock_cb_data *cb)
-{
-	reinit_completion(&cb->cb_completion);
-	cb->cb_count = 0;
-	cb->event_open = 0;
-	cb->event_entry_update = 0;
-	memset(&cb->entry_data, 0,
-		sizeof(struct msm_smp2p_update_notif));
-}
-
-
-/**
- * Initialize mock callback data.
- *
- * @cb:  Mock callback data
- */
-static inline void mock_cb_data_init(struct mock_cb_data *cb)
-{
-	if (!cb->initialized) {
-		init_completion(&cb->cb_completion);
-		spin_lock_init(&cb->lock);
-		cb->initialized = true;
-		cb->nb.notifier_call = smp2p_test_notify;
-		memset(&cb->entry_data, 0,
-			sizeof(struct msm_smp2p_update_notif));
-	}
-	mock_cb_data_reset(cb);
-}
-
-/**
- * Notifier function passed into SMP2P for testing.
- *
- * @self:       Pointer to calling notifier block
- * @event:	    Event
- * @data:       Event-specific data
- * @returns:    0
- */
-static inline int smp2p_test_notify(struct notifier_block *self,
-		unsigned long event, void *data)
-{
-	struct mock_cb_data *cb_data_ptr;
-	unsigned long flags;
-
-	cb_data_ptr = container_of(self, struct mock_cb_data, nb);
-
-	spin_lock_irqsave(&cb_data_ptr->lock, flags);
-
-	switch (event) {
-	case SMP2P_OPEN:
-		++cb_data_ptr->event_open;
-		if (data) {
-			cb_data_ptr->entry_data =
-			*(struct msm_smp2p_update_notif *)(data);
-		}
-		break;
-	case SMP2P_ENTRY_UPDATE:
-		++cb_data_ptr->event_entry_update;
-		if (data) {
-			cb_data_ptr->entry_data =
-			*(struct msm_smp2p_update_notif *)(data);
-		}
-		break;
-	default:
-		pr_err("%s Unknown event\n", __func__);
-		break;
-	}
-
-	++cb_data_ptr->cb_count;
-	complete(&cb_data_ptr->cb_completion);
-	spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
-	return 0;
-}
-#endif /* _SMP2P_TEST_COMMON_H_ */
diff --git a/drivers/soc/qcom/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/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 7d9a2e8..b29e60d 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -59,6 +59,8 @@
 #define SPMI_OWNERSHIP_TABLE_REG(N)	(0x0700 + (4 * (N)))
 #define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
 
+#define SPMI_PROTOCOL_IRQ_STATUS	0x6000
+
 /* Channel Status fields */
 enum pmic_arb_chnl_status {
 	PMIC_ARB_STATUS_DONE	= BIT(0),
@@ -148,6 +150,8 @@
  * @ver_ops:		version dependent operations.
  * @ppid_to_apid	in-memory copy of PPID -> channel (APID) mapping table.
  *			v2 only.
+ * @ahb_bus_wa:		Use AHB bus workaround to avoid write transaction
+ *			corruption on some PMIC arbiter v5 platforms.
  */
 struct spmi_pmic_arb {
 	void __iomem		*rd_base;
@@ -172,6 +176,7 @@
 	u16			*ppid_to_apid;
 	u16			last_apid;
 	struct apid_data	apid_data[PMIC_ARB_MAX_PERIPHS];
+	bool			ahb_bus_wa;
 };
 
 /**
@@ -217,6 +222,16 @@
 static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa,
 				       u32 offset, u32 val)
 {
+	if (pa->ahb_bus_wa) {
+		/* AHB bus register dummy read for workaround. */
+		readl_relaxed(pa->cnfg + SPMI_PROTOCOL_IRQ_STATUS);
+		/*
+		 * Ensure that the read completes before initiating the
+		 * subsequent register write.
+		 */
+		mb();
+	}
+
 	writel_relaxed(val, pa->wr_base + offset);
 }
 
@@ -1343,6 +1358,9 @@
 
 	pa->ee = ee;
 
+	pa->ahb_bus_wa = of_property_read_bool(pdev->dev.of_node,
+					"qcom,enable-ahb-bus-workaround");
+
 	pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1,
 					sizeof(*pa->mapping_table), GFP_KERNEL);
 	if (!pa->mapping_table) {
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 3cacc0d..7663e3c 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -522,6 +522,7 @@
 		if (!ret && *temp < crit_temp)
 			*temp = tz->emul_temperature;
 	}
+	trace_thermal_query_temp(tz, *temp);
 	mutex_unlock(&tz->lock);
 exit:
 	return ret;
@@ -2586,9 +2587,11 @@
 	case PM_POST_SUSPEND:
 		atomic_set(&in_suspend, 0);
 		list_for_each_entry(tz, &thermal_tz_list, node) {
+			mutex_lock(&tz->lock);
 			thermal_zone_device_reset(tz);
-			thermal_zone_device_update(tz,
-						   THERMAL_EVENT_UNSPECIFIED);
+			mod_delayed_work(system_freezable_power_efficient_wq,
+						&tz->poll_queue, 0);
+			mutex_unlock(&tz->lock);
 		}
 		break;
 	default:
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 79788eb..af119f4 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -1428,9 +1428,7 @@
 	msm_geni_serial_stop_rx(uport);
 	spin_unlock_irqrestore(&uport->lock, flags);
 
-	if (uart_console(uport)) {
-		se_geni_resources_off(&msm_port->serial_rsc);
-	} else {
+	if (!uart_console(uport)) {
 		msm_geni_serial_power_off(uport);
 		if (msm_port->wakeup_irq > 0) {
 			irq_set_irq_wake(msm_port->wakeup_irq, 0);
@@ -2080,6 +2078,21 @@
 		dev_err(uport->dev, "Failed to create dbg dir\n");
 }
 
+static void msm_geni_serial_cons_pm(struct uart_port *uport,
+		unsigned int new_state, unsigned int old_state)
+{
+	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+
+	if (unlikely(!uart_console(uport)))
+		return;
+
+	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+		se_geni_resources_on(&msm_port->serial_rsc);
+	else if (new_state == UART_PM_STATE_OFF &&
+			old_state == UART_PM_STATE_ON)
+		se_geni_resources_off(&msm_port->serial_rsc);
+}
+
 static const struct uart_ops msm_geni_console_pops = {
 	.tx_empty = msm_geni_serial_tx_empty,
 	.stop_tx = msm_geni_serial_stop_tx,
@@ -2096,6 +2109,7 @@
 	.poll_get_char	= msm_geni_serial_get_char,
 	.poll_put_char	= msm_geni_serial_poll_put_char,
 #endif
+	.pm = msm_geni_serial_cons_pm,
 };
 
 static const struct uart_ops msm_geni_serial_pops = {
@@ -2420,7 +2434,6 @@
 
 	if (uart_console(uport) &&
 	    console_suspend_enabled && uport->suspended) {
-		se_geni_resources_on(&port->serial_rsc);
 		uart_resume_port((struct uart_driver *)uport->private_data,
 									uport);
 		disable_irq(uport->irq);
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3e459b0..4b78e02 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -428,6 +428,9 @@
 {
 	struct dwc3_event_buffer	*evt;
 
+	if (!dwc->ev_buf)
+		return;
+
 	evt = dwc->ev_buf;
 
 	evt->lpos = 0;
@@ -692,8 +695,6 @@
 	phy_exit(dwc->usb2_generic_phy);
 	phy_exit(dwc->usb3_generic_phy);
 
-	usb_phy_set_suspend(dwc->usb2_phy, 1);
-	usb_phy_set_suspend(dwc->usb3_phy, 1);
 	phy_power_off(dwc->usb2_generic_phy);
 	phy_power_off(dwc->usb3_generic_phy);
 }
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b042152..5af75fd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -951,6 +951,7 @@
  * @last_fifo_depth: total TXFIFO depth of all enabled USB IN/INT endpoints
  * @imod_interval: set the interrupt moderation interval in 250ns
  *			increments or 0 to disable.
+ * @create_reg_debugfs: create debugfs entry to allow dwc3 register dump
  */
 struct dwc3 {
 	struct usb_ctrlrequest	*ctrl_req;
@@ -1145,6 +1146,7 @@
 	void			*dwc_ipc_log_ctxt;
 	int			last_fifo_depth;
 	struct dwc3_gadget_events	dbg_gadget_events;
+	bool			create_reg_debugfs;
 };
 
 /* -------------------------------------------------------------------------- */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 260092c..aebad09 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -291,6 +291,11 @@
 	unsigned long		flags;
 	u32			reg;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
 	spin_unlock_irqrestore(&dwc->lock, flags);
@@ -326,6 +331,11 @@
 	u32			mode = 0;
 	char buf[32] = {};
 
+	if (atomic_read(&dwc->in_lpm)) {
+		dev_err(dwc->dev, "USB device is powered off\n");
+		return count;
+	}
+
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 
@@ -360,6 +370,12 @@
 	unsigned long		flags;
 	u32			reg;
 
+
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	reg &= DWC3_DCTL_TSTCTRL_MASK;
@@ -406,6 +422,11 @@
 	u32			testmode = 0;
 	char			buf[32] = {};
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 
@@ -444,6 +465,11 @@
 	enum dwc3_link_state	state;
 	u32			reg;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 	state = DWC3_DSTS_USBLNKST(reg);
@@ -513,6 +539,11 @@
 	enum dwc3_link_state	state = 0;
 	char			buf[32] = {};
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 
@@ -558,6 +589,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ);
 	seq_printf(s, "%u\n", val);
@@ -573,6 +609,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ);
 	seq_printf(s, "%u\n", val);
@@ -588,6 +629,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_TXREQQ);
 	seq_printf(s, "%u\n", val);
@@ -603,6 +649,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
 	seq_printf(s, "%u\n", val);
@@ -618,6 +669,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
 	seq_printf(s, "%u\n", val);
@@ -633,6 +689,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
 	seq_printf(s, "%u\n", val);
@@ -648,6 +709,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
 	seq_printf(s, "%u\n", val);
@@ -1044,9 +1110,12 @@
 	dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
 	dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
 
-	file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
-	if (!file)
-		dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+	if (dwc->create_reg_debugfs) {
+		file = debugfs_create_regset32("regdump", 0444,
+						root, dwc->regset);
+		if (!file)
+			dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+	}
 
 	if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
 		file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index d316821..351f9a2 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1867,6 +1867,9 @@
 		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_SETUP\n");
 		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
 			evt = mdwc->gsi_ev_buff[i];
+			if (!evt)
+				break;
+
 			dev_dbg(mdwc->dev, "Evt buf %p dma %08llx length %d\n",
 				evt->buf, (unsigned long long) evt->dma,
 				evt->length);
@@ -1892,6 +1895,9 @@
 		break;
 	case DWC3_GSI_EVT_BUF_CLEANUP:
 		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_CLEANUP\n");
+		if (!mdwc->gsi_ev_buff)
+			break;
+
 		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
 			evt = mdwc->gsi_ev_buff[i];
 			evt->lpos = 0;
@@ -1911,6 +1917,9 @@
 		break;
 	case DWC3_GSI_EVT_BUF_FREE:
 		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_FREE\n");
+		if (!mdwc->gsi_ev_buff)
+			break;
+
 		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
 			evt = mdwc->gsi_ev_buff[i];
 			if (evt)
@@ -3145,7 +3154,7 @@
 	ret = dwc3_msm_get_clk_gdsc(mdwc);
 	if (ret) {
 		dev_err(&pdev->dev, "error getting clock or gdsc.\n");
-		return ret;
+		goto err;
 	}
 
 	mdwc->id_state = DWC3_ID_FLOAT;
@@ -3441,24 +3450,20 @@
 	return 0;
 
 put_dwc3:
-	platform_device_put(mdwc->dwc3);
 	if (mdwc->bus_perf_client)
 		msm_bus_scale_unregister_client(mdwc->bus_perf_client);
+
 uninit_iommu:
 	if (mdwc->iommu_map) {
 		arm_iommu_detach_device(mdwc->dev);
 		arm_iommu_release_mapping(mdwc->iommu_map);
 	}
+	of_platform_depopulate(&pdev->dev);
 err:
+	destroy_workqueue(mdwc->dwc3_wq);
 	return ret;
 }
 
-static int dwc3_msm_remove_children(struct device *dev, void *data)
-{
-	device_unregister(dev);
-	return 0;
-}
-
 static int dwc3_msm_remove(struct platform_device *pdev)
 {
 	struct dwc3_msm	*mdwc = platform_get_drvdata(pdev);
@@ -3495,8 +3500,7 @@
 
 	if (mdwc->hs_phy)
 		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
-	platform_device_put(mdwc->dwc3);
-	device_for_each_child(&pdev->dev, NULL, dwc3_msm_remove_children);
+	of_platform_depopulate(&pdev->dev);
 
 	dbg_event(0xFF, "Remov put", 0);
 	pm_runtime_disable(mdwc->dev);
@@ -3952,20 +3956,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/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..76e0a32 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;
@@ -2827,7 +2914,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 +3082,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 +3137,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 +3189,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 +3214,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 +3223,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/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 89de903..14c0203 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -307,7 +307,7 @@
 
 	hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
 
-	if (device_property_read_bool(sysdev, "usb3-lpm-capable"))
+	if (device_property_read_bool(&pdev->dev, "usb3-lpm-capable"))
 		xhci->quirks |= XHCI_LPM_SUPPORT;
 
 	if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a8003cc7..32c3d42 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -267,7 +267,7 @@
 static inline void coresight_abort(void) {}
 #endif
 
-#ifdef CONFIG_OF
+#if defined(CONFIG_OF) && defined(CONFIG_CORESIGHT)
 extern struct coresight_platform_data *of_get_coresight_platform_data(
 				struct device *dev, struct device_node *node);
 extern struct coresight_cti_data *of_get_coresight_cti_data(
@@ -275,7 +275,7 @@
 #else
 static inline struct coresight_platform_data *of_get_coresight_platform_data(
 	struct device *dev, struct device_node *node) { return NULL; }
-static inlint struct coresight_cti_data *of_get_coresight_cti_data(
+static inline struct coresight_cti_data *of_get_coresight_cti_data(
 		struct device *dev, struct device_node *node) { return NULL; }
 #endif
 
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 40b66f9..e0aa720 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -194,6 +194,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 +217,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/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/usb_bam.h b/include/linux/usb_bam.h
index 62fd4e4..1b0ca4a 100644
--- a/include/linux/usb_bam.h
+++ b/include/linux/usb_bam.h
@@ -198,7 +198,7 @@
 };
 
 /**
- * struct msm_usb_bam_platform_data: pipe connection information
+ * struct msm_usb_bam_data: pipe connection information
  * between USB/HSIC BAM and another BAM. USB/HSIC BAM can be
  * either src BAM or dst BAM
  * @usb_bam_num_pipes: max number of pipes to use.
@@ -218,7 +218,7 @@
  *		can work at in bam2bam mode when connected to SS host.
  * @enable_hsusb_bam_on_boot: Enable HSUSB BAM (non-NDP) on bootup itself
  */
-struct msm_usb_bam_platform_data {
+struct msm_usb_bam_data {
 	u8 max_connections;
 	int usb_bam_num_pipes;
 	phys_addr_t usb_bam_fifo_baseaddr;
diff --git a/include/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/trace/events/power.h b/include/trace/events/power.h
index cecfb8f..b94e493 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)
 );
 
diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h
index c0475a2..94ee287 100644
--- a/include/trace/events/thermal.h
+++ b/include/trace/events/thermal.h
@@ -20,6 +20,29 @@
 			 { THERMAL_TRIP_PASSIVE,  "PASSIVE"},	\
 			 { THERMAL_TRIP_ACTIVE,   "ACTIVE"})
 
+TRACE_EVENT(thermal_query_temp,
+
+	TP_PROTO(struct thermal_zone_device *tz, int temp),
+
+	TP_ARGS(tz, temp),
+
+	TP_STRUCT__entry(
+		__string(thermal_zone, tz->type)
+		__field(int, id)
+		__field(int, temp)
+	),
+
+	TP_fast_assign(
+		__assign_str(thermal_zone, tz->type);
+		__entry->id = tz->id;
+		__entry->temp = temp;
+	),
+
+	TP_printk("thermal_zone=%s id=%d temp=%d",
+		__get_str(thermal_zone), __entry->id,
+		__entry->temp)
+);
+
 TRACE_EVENT(thermal_temperature,
 
 	TP_PROTO(struct thermal_zone_device *tz),
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index 5f70a57..0765527 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -174,6 +174,17 @@
 	__u32 strength;
 };
 
+#define HIST_V_SIZE 256
+/**
+ * struct drm_msm_hist - histogram feature structure
+ * @flags: for customizing operations
+ * @data: histogram data
+ */
+struct drm_msm_hist {
+	__u64 flags;
+	__u32 data[HIST_V_SIZE];
+};
+
 #define AD4_LUT_GRP0_SIZE 33
 #define AD4_LUT_GRP1_SIZE 32
 /*
diff --git a/include/uapi/media/cam_isp.h b/include/uapi/media/cam_isp.h
index 9253bc7..05c9283 100644
--- a/include/uapi/media/cam_isp.h
+++ b/include/uapi/media/cam_isp.h
@@ -73,6 +73,7 @@
 #define CAM_ISP_PACKET_META_DMI_COMMON          6
 #define CAM_ISP_PACKET_META_CLOCK               7
 #define CAM_ISP_PACKET_META_CSID                8
+#define CAM_ISP_PACKET_META_DUAL_CONFIG         9
 #define CAM_ISP_PACKET_META_GENERIC_BLOB        10
 #define CAM_ISP_PACKET_META_MAX                 11
 
@@ -261,4 +262,60 @@
 	struct cam_isp_port_hfr_config io_hfr_config[1];
 };
 
+/**
+ * struct cam_isp_dual_split_params - dual isp spilt parameters
+ *
+ * @split_point:                Split point information x, where (0 < x < width)
+ *                              left ISP's input ends at x + righ padding and
+ *                              Right ISP's input starts at x - left padding
+ * @right_padding:              Padding added past the split point for left
+ *                              ISP's input
+ * @left_padding:               Padding added before split point for right
+ *                              ISP's input
+ * @reserved:                   Reserved filed for alignment
+ *
+ */
+struct cam_isp_dual_split_params {
+	uint32_t                       split_point;
+	uint32_t                       right_padding;
+	uint32_t                       left_padding;
+	uint32_t                       reserved;
+};
+
+/**
+ * struct cam_isp_dual_stripe_config - stripe config per bus client
+ *
+ * @offset:                     Start horizontal offset relative to
+ *                              output buffer
+ *                              In UBWC mode, this value indicates the H_INIT
+ *                              value in pixel
+ * @width:                      Width of the stripe in bytes
+ * @tileconfig                  Ubwc meta tile config. Contain the partial
+ *                              tile info
+ * @port_id:                    port id of ISP output
+ *
+ */
+struct cam_isp_dual_stripe_config {
+	uint32_t                       offset;
+	uint32_t                       width;
+	uint32_t                       tileconfig;
+	uint32_t                       port_id;
+};
+
+/**
+ * struct cam_isp_dual_config - dual isp configuration
+ *
+ * @num_ports                   Number of isp output ports
+ * @reserved                    Reserved field for alignment
+ * @split_params:               Inpput split parameters
+ * @stripes:                    Stripe information
+ *
+ */
+struct cam_isp_dual_config {
+	uint32_t                           num_ports;
+	uint32_t                           reserved;
+	struct cam_isp_dual_split_params   split_params;
+	struct cam_isp_dual_stripe_config  stripes[1];
+};
+
 #endif /* __UAPI_CAM_ISP_H__ */
diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h
index 4fe325d..63fd555 100644
--- a/include/uapi/media/msm_vidc.h
+++ b/include/uapi/media/msm_vidc.h
@@ -6,6 +6,7 @@
 #define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12	0x2
 #define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC	0x8002
 #define MSM_VIDC_4x_1 0x1
+#define MSM_VIDC_EXTRADATA_FRAME_QP_ADV 0x1
 
 struct msm_vidc_extradata_header {
 	unsigned int size;
@@ -137,6 +138,10 @@
 
 struct msm_vidc_frame_qp_payload {
 	unsigned int frame_qp;
+	unsigned int qp_sum;
+	unsigned int skip_qp_sum;
+	unsigned int skip_num_blocks;
+	unsigned int total_num_blocks;
 };
 
 struct msm_vidc_frame_bits_info_payload {
diff --git a/init/Kconfig b/init/Kconfig
index af000c7..9782dfc 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1293,6 +1293,16 @@
 
 	  If unsure, say N here.
 
+config SCHED_CORE_ROTATE
+	bool "Scheduler core rotation"
+	depends on SMP
+	help
+	  This options enables the core rotation functionality in
+	  the scheduler. Scheduler with core rotation aims to utilize
+	  CPUs evenly.
+
+	  If unsure, say N here.
+
 config CHECKPOINT_RESTORE
 	bool "Checkpoint/restore support" if EXPERT
 	select PROC_CHILDREN
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 69dc428..d2df1ce 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)
 {
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index d3a7411..cbf240d 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)
diff --git a/kernel/locking/osq_lock.c b/kernel/locking/osq_lock.c
index 05a3785..99b8d99 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;
 
@@ -118,8 +120,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/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c
index 10e6d8b..e99d860 100644
--- a/kernel/locking/spinlock_debug.c
+++ b/kernel/locking/spinlock_debug.c
@@ -110,14 +110,38 @@
 	lock->owner_cpu = -1;
 }
 
-/*
- * We are now relying on the NMI watchdog to detect lockup instead of doing
- * the detection here with an unfair lock which can cause problem of its own.
- */
+static void __spin_lock_debug(raw_spinlock_t *lock)
+{
+	u64 i;
+	u64 loops = loops_per_jiffy * HZ;
+
+	for (i = 0; i < loops; i++) {
+		if (arch_spin_trylock(&lock->raw_lock))
+			return;
+		__delay(1);
+	}
+	/* lockup suspected: */
+	spin_bug(lock, "lockup suspected");
+#ifdef CONFIG_SMP
+	trigger_all_cpu_backtrace();
+#endif
+
+	/*
+	 * The trylock above was causing a livelock.  Give the lower level arch
+	 * specific lock code a chance to acquire the lock. We have already
+	 * printed a warning/backtrace at this point. The non-debug arch
+	 * specific code might actually succeed in acquiring the lock.  If it is
+	 * not successful, the end-result is the same - there is no forward
+	 * progress.
+	 */
+	arch_spin_lock(&lock->raw_lock);
+}
+
 void do_raw_spin_lock(raw_spinlock_t *lock)
 {
 	debug_spin_lock_before(lock);
-	arch_spin_lock(&lock->raw_lock);
+	if (unlikely(!arch_spin_trylock(&lock->raw_lock)))
+		__spin_lock_debug(lock);
 	debug_spin_lock_after(lock);
 }
 
@@ -155,6 +179,32 @@
 
 #define RWLOCK_BUG_ON(cond, lock, msg) if (unlikely(cond)) rwlock_bug(lock, msg)
 
+#if 0		/* __write_lock_debug() can lock up - maybe this can too? */
+static void __read_lock_debug(rwlock_t *lock)
+{
+	u64 i;
+	u64 loops = loops_per_jiffy * HZ;
+	int print_once = 1;
+
+	for (;;) {
+		for (i = 0; i < loops; i++) {
+			if (arch_read_trylock(&lock->raw_lock))
+				return;
+			__delay(1);
+		}
+		/* lockup suspected: */
+		if (print_once) {
+			print_once = 0;
+			printk(KERN_EMERG "BUG: read-lock lockup on CPU#%d, "
+					"%s/%d, %p\n",
+				raw_smp_processor_id(), current->comm,
+				current->pid, lock);
+			dump_stack();
+		}
+	}
+}
+#endif
+
 void do_raw_read_lock(rwlock_t *lock)
 {
 	RWLOCK_BUG_ON(lock->magic != RWLOCK_MAGIC, lock, "bad magic");
@@ -204,6 +254,32 @@
 	lock->owner_cpu = -1;
 }
 
+#if 0		/* This can cause lockups */
+static void __write_lock_debug(rwlock_t *lock)
+{
+	u64 i;
+	u64 loops = loops_per_jiffy * HZ;
+	int print_once = 1;
+
+	for (;;) {
+		for (i = 0; i < loops; i++) {
+			if (arch_write_trylock(&lock->raw_lock))
+				return;
+			__delay(1);
+		}
+		/* lockup suspected: */
+		if (print_once) {
+			print_once = 0;
+			printk(KERN_EMERG "BUG: write-lock lockup on CPU#%d, "
+					"%s/%d, %p\n",
+				raw_smp_processor_id(), current->comm,
+				current->pid, lock);
+			dump_stack();
+		}
+	}
+}
+#endif
+
 void do_raw_write_lock(rwlock_t *lock)
 {
 	debug_write_lock_before(lock);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 08fd4be..401d0c1 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);
 }
@@ -9624,3 +9623,50 @@
 #endif /* CONFIG_SCHED_WALT */
 
 __read_mostly bool sched_predl = 1;
+
+#ifdef CONFIG_SCHED_CORE_ROTATE
+int
+find_first_cpu_bit(struct task_struct *p, const cpumask_t *search_cpus,
+		   struct sched_group *sg_target, bool *avoid_prev_cpu,
+		   bool *do_rotate, struct find_first_cpu_bit_env *env)
+{
+	int i = -1;
+	unsigned long mcc;
+	int cpu = smp_processor_id();
+
+	mcc = cpu_rq(cpu)->rd->max_cpu_capacity.val;
+
+	/* do rotation only for big CPUs. */
+	*do_rotate = (cpumask_first(search_cpus) < nr_cpu_ids &&
+		     capacity_orig_of(cpumask_first(search_cpus)) == mcc);
+
+	if (*do_rotate) {
+		if (time_before_eq(jiffies, *env->avoid_prev_cpu_last +
+				   env->interval))
+			return *env->rotate_cpu_start;
+
+		spin_lock(env->rotate_lock);
+		if (time_after(jiffies, *env->avoid_prev_cpu_last +
+					env->interval)) {
+			cpumask_t tmpmask;
+
+			*env->avoid_prev_cpu_last = jiffies;
+			*avoid_prev_cpu = true;
+
+			cpumask_copy(&tmpmask, sched_group_cpus(sg_target));
+			cpumask_andnot(&tmpmask, &tmpmask, cpu_isolated_mask);
+
+			i = cpumask_next(*env->rotate_cpu_start, &tmpmask);
+			if (i >= nr_cpu_ids)
+				i = cpumask_first(&tmpmask) - 1;
+			/* Change start CPU every interval. */
+			*env->rotate_cpu_start = i;
+		} else {
+			i = *env->rotate_cpu_start;
+		}
+		spin_unlock(env->rotate_lock);
+	}
+
+	return i;
+}
+#endif
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index e56af41..ef9824a 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -19,6 +19,7 @@
 #include <linux/kthread.h>
 #include <linux/sched.h>
 #include <linux/sched/rt.h>
+#include <linux/syscore_ops.h>
 
 #include <trace/events/sched.h>
 
@@ -35,6 +36,10 @@
 	unsigned int active_cpus;
 	unsigned int num_cpus;
 	unsigned int nr_isolated_cpus;
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	unsigned long set_max;
+	unsigned long set_cur;
+#endif
 	cpumask_t cpu_mask;
 	unsigned int need_cpus;
 	unsigned int task_thres;
@@ -76,6 +81,7 @@
 static bool initialized;
 
 static unsigned int get_active_cpu_count(const struct cluster_data *cluster);
+static void cpuset_next(struct cluster_data *cluster);
 
 /* ========================= sysfs interface =========================== */
 
@@ -88,6 +94,7 @@
 		return -EINVAL;
 
 	state->min_cpus = min(val, state->max_cpus);
+	cpuset_next(state);
 	wake_up_core_ctl_thread(state);
 
 	return count;
@@ -109,6 +116,7 @@
 	val = min(val, state->num_cpus);
 	state->max_cpus = val;
 	state->min_cpus = min(state->min_cpus, state->max_cpus);
+	cpuset_next(state);
 	wake_up_core_ctl_thread(state);
 
 	return count;
@@ -629,26 +637,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);
 
@@ -702,6 +711,67 @@
 	spin_unlock_irqrestore(&state_lock, flags);
 }
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+static void cpuset_next(struct cluster_data *cluster)
+{
+	int cpus_needed = cluster->num_cpus - cluster->min_cpus;
+
+	cluster->set_cur++;
+	cluster->set_cur = min(cluster->set_cur, cluster->set_max);
+
+	/*
+	 * This loop generates bit sets from 0 to pow(num_cpus, 2) - 1.
+	 * We start loop from set_cur to set_cur - 1 and break when weight of
+	 * set_cur equals to cpus_needed.
+	 */
+
+	while (1) {
+		if (bitmap_weight(&cluster->set_cur, BITS_PER_LONG) ==
+		    cpus_needed) {
+			break;
+		}
+		cluster->set_cur++;
+		cluster->set_cur = min(cluster->set_cur, cluster->set_max);
+		if (cluster->set_cur == cluster->set_max)
+			/* roll over */
+			cluster->set_cur = 0;
+	};
+
+	pr_debug("first_cpu=%d cpus_needed=%d set_cur=0x%lx\n",
+		 cluster->first_cpu, cpus_needed, cluster->set_cur);
+}
+
+static bool should_we_isolate(int cpu, struct cluster_data *cluster)
+{
+	/* cpu should be part of cluster */
+	return !!(cluster->set_cur & (1 << (cpu - cluster->first_cpu)));
+}
+
+static void core_ctl_resume(void)
+{
+	unsigned int i = 0;
+	struct cluster_data *cluster;
+
+	/* move to next isolation cpu set */
+	for_each_cluster(cluster, i)
+		cpuset_next(cluster);
+}
+
+static struct syscore_ops core_ctl_syscore_ops = {
+	.resume	= core_ctl_resume,
+};
+
+#else
+
+static void cpuset_next(struct cluster_data *cluster) { }
+
+static bool should_we_isolate(int cpu, struct cluster_data *cluster)
+{
+	return true;
+}
+
+#endif
+
 static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
 {
 	struct cpu_data *c, *tmp;
@@ -726,6 +796,9 @@
 		if (c->is_busy)
 			continue;
 
+		if (!should_we_isolate(c->cpu, cluster))
+			continue;
+
 		spin_unlock_irqrestore(&state_lock, flags);
 
 		pr_debug("Trying to isolate CPU%u\n", c->cpu);
@@ -1029,6 +1102,11 @@
 	cluster->offline_delay_ms = 100;
 	cluster->task_thres = UINT_MAX;
 	cluster->nrrun = cluster->num_cpus;
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	cluster->set_max = cluster->num_cpus * cluster->num_cpus;
+	/* by default mark all cpus as eligible */
+	cluster->set_cur = cluster->set_max - 1;
+#endif
 	cluster->enable = true;
 	INIT_LIST_HEAD(&cluster->lru);
 	spin_lock_init(&cluster->pending_lock);
@@ -1065,6 +1143,10 @@
 	if (should_skip(cpu_possible_mask))
 		return 0;
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	register_syscore_ops(&core_ctl_syscore_ops);
+#endif
+
 	cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
 			"core_ctl/isolation:online",
 			core_ctl_isolation_online_cpu, NULL);
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 2eb966c..f27d47f 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 ***********************/
 
@@ -681,6 +682,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 +715,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 +786,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 +827,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/fair.c b/kernel/sched/fair.c
index d61c570..7b459ca 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5034,6 +5034,19 @@
 DEFINE_PER_CPU(cpumask_var_t, load_balance_mask);
 DEFINE_PER_CPU(cpumask_var_t, select_idle_mask);
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+static int rotate_cpu_start;
+static DEFINE_SPINLOCK(rotate_lock);
+static unsigned long avoid_prev_cpu_last;
+
+static struct find_first_cpu_bit_env first_cpu_bit_env = {
+	.avoid_prev_cpu_last = &avoid_prev_cpu_last,
+	.rotate_cpu_start = &rotate_cpu_start,
+	.interval = HZ,
+	.rotate_lock = &rotate_lock,
+};
+#endif
+
 #ifdef CONFIG_NO_HZ_COMMON
 /*
  * per rq 'load' arrray crap; XXX kill this.
@@ -6883,7 +6896,7 @@
 	int target_cpu, targeted_cpus = 0;
 	unsigned long task_util_boosted = 0, curr_util = 0;
 	long new_util, new_util_cum;
-	int i;
+	int i = -1;
 	int ediff = -1;
 	int cpu = smp_processor_id();
 	int min_util_cpu = -1;
@@ -6902,8 +6915,17 @@
 	enum sched_boost_policy placement_boost = task_sched_boost(p) ?
 				sched_boost_policy() : SCHED_BOOST_NONE;
 	struct related_thread_group *grp;
+	cpumask_t search_cpus;
+	int prev_cpu = task_cpu(p);
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	bool do_rotate = false;
+	bool avoid_prev_cpu = false;
+#else
+#define do_rotate false
+#define avoid_prev_cpu false
+#endif
 
-	sd = rcu_dereference(per_cpu(sd_ea, task_cpu(p)));
+	sd = rcu_dereference(per_cpu(sd_ea, prev_cpu));
 
 	if (!sd)
 		return target;
@@ -6922,7 +6944,7 @@
 		rtg_target = &grp->preferred_cluster->cpus;
 
 	if (sync && bias_to_waker_cpu(p, cpu, rtg_target)) {
-		trace_sched_task_util_bias_to_waker(p, task_cpu(p),
+		trace_sched_task_util_bias_to_waker(p, prev_cpu,
 					task_util(p), cpu, cpu, 0, need_idle);
 		return cpu;
 	}
@@ -6985,14 +7007,30 @@
 
 		target_cpu = -1;
 
+		cpumask_copy(&search_cpus, tsk_cpus_allowed(p));
+		cpumask_and(&search_cpus, &search_cpus,
+			    sched_group_cpus(sg_target));
+
+#ifdef CONFIG_SCHED_CORE_ROTATE
+		i = find_first_cpu_bit(p, &search_cpus, sg_target,
+				       &avoid_prev_cpu, &do_rotate,
+				       &first_cpu_bit_env);
+
+retry:
+#endif
 		/* Find cpu with sufficient capacity */
-		for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg_target)) {
+		while ((i = cpumask_next(i, &search_cpus)) < nr_cpu_ids) {
+			cpumask_clear_cpu(i, &search_cpus);
+
 			if (cpu_isolated(i))
 				continue;
 
 			if (isolated_candidate == -1)
 				isolated_candidate = i;
 
+			if (avoid_prev_cpu && i == prev_cpu)
+				continue;
+
 			if (is_reserved(i))
 				continue;
 
@@ -7061,9 +7099,9 @@
 						    new_util ||
 						    (target_cpu_util ==
 						     new_util &&
-						     (i == task_cpu(p) ||
+						     (i == prev_cpu ||
 						      (target_cpu !=
-						       task_cpu(p) &&
+						       prev_cpu &&
 						       target_cpu_new_util_cum >
 						       new_util_cum))))) {
 						min_idle_idx_cpu = i;
@@ -7075,6 +7113,9 @@
 					}
 				} else if (cpu_rq(i)->nr_running) {
 					target_cpu = i;
+#ifdef CONFIG_SCHED_CORE_ROTATE
+					do_rotate = false;
+#endif
 					break;
 				}
 			} else if (!need_idle) {
@@ -7114,6 +7155,19 @@
 			}
 		}
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+		if (do_rotate) {
+			/*
+			 * We started iteration somewhere in the middle of
+			 * cpumask.  Iterate once again from bit 0 to the
+			 * previous starting point bit.
+			 */
+			do_rotate = false;
+			i = -1;
+			goto retry;
+		}
+#endif
+
 		if (target_cpu == -1 ||
 		    (target_cpu != min_util_cpu && !safe_to_pack &&
 		     !is_packing_eligible(p, task_util_boosted, sg_target,
@@ -7148,7 +7202,8 @@
 		}
 	}
 
-	if (target_cpu != task_cpu(p) && !cpu_isolated(task_cpu(p))) {
+	if (target_cpu != task_cpu(p) && !avoid_prev_cpu &&
+	    !cpu_isolated(task_cpu(p))) {
 		struct energy_env eenv = {
 			.util_delta	= task_util(p),
 			.src_cpu	= task_cpu(p),
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 35382df..7feba85 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1711,6 +1711,19 @@
 	return NULL;
 }
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+static int rotate_cpu_start;
+static DEFINE_SPINLOCK(rotate_lock);
+static unsigned long avoid_prev_cpu_last;
+
+static struct find_first_cpu_bit_env first_cpu_bit_env = {
+	.avoid_prev_cpu_last = &avoid_prev_cpu_last,
+	.rotate_cpu_start = &rotate_cpu_start,
+	.interval = HZ,
+	.rotate_lock = &rotate_lock,
+};
+#endif
+
 static DEFINE_PER_CPU(cpumask_var_t, local_cpu_mask);
 
 static int find_lowest_rq(struct task_struct *task)
@@ -1719,7 +1732,7 @@
 	struct sched_group *sg, *sg_target;
 	struct cpumask *lowest_mask = this_cpu_cpumask_var_ptr(local_cpu_mask);
 	int this_cpu = smp_processor_id();
-	int cpu, best_cpu;
+	int cpu = -1, best_cpu;
 	struct cpumask search_cpu, backup_search_cpu;
 	unsigned long cpu_capacity;
 	unsigned long best_capacity;
@@ -1730,6 +1743,13 @@
 	int best_cpu_idle_idx = INT_MAX;
 	int cpu_idle_idx = -1;
 	bool placement_boost;
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	bool do_rotate = false;
+	bool avoid_prev_cpu = false;
+#else
+#define do_rotate false
+#define avoid_prev_cpu false
+#endif
 
 	/* Make sure the mask is initialized first */
 	if (unlikely(!lowest_mask))
@@ -1761,6 +1781,10 @@
 
 		sg = sd->groups;
 		do {
+			if (!cpumask_intersects(lowest_mask,
+						sched_group_cpus(sg)))
+				continue;
+
 			cpu = group_first_cpu(sg);
 			cpu_capacity = capacity_orig_of(cpu);
 
@@ -1778,20 +1802,37 @@
 		} while (sg = sg->next, sg != sd->groups);
 		rcu_read_unlock();
 
-		cpumask_and(&search_cpu, lowest_mask,
-			    sched_group_cpus(sg_target));
-		cpumask_copy(&backup_search_cpu, lowest_mask);
-		cpumask_andnot(&backup_search_cpu, &backup_search_cpu,
-			       &search_cpu);
+		if (sg_target) {
+			cpumask_and(&search_cpu, lowest_mask,
+				    sched_group_cpus(sg_target));
+			cpumask_copy(&backup_search_cpu, lowest_mask);
+			cpumask_andnot(&backup_search_cpu, &backup_search_cpu,
+				       &search_cpu);
+
+#ifdef CONFIG_SCHED_CORE_ROTATE
+			cpu = find_first_cpu_bit(task, &search_cpu, sg_target,
+						 &avoid_prev_cpu, &do_rotate,
+						 &first_cpu_bit_env);
+#endif
+		} else {
+			cpumask_copy(&search_cpu, lowest_mask);
+			cpumask_clear(&backup_search_cpu);
+			cpu = -1;
+		}
 
 retry:
-		for_each_cpu(cpu, &search_cpu) {
+		while ((cpu = cpumask_next(cpu, &search_cpu)) < nr_cpu_ids) {
+			cpumask_clear_cpu(cpu, &search_cpu);
+
 			/*
 			 * Don't use capcity_curr_of() since it will
 			 * double count rt task load.
 			 */
 			util = cpu_util(cpu);
 
+			if (avoid_prev_cpu && cpu == task_cpu(task))
+				continue;
+
 			if (__cpu_overutilized(cpu, util + tutil))
 				continue;
 
@@ -1838,6 +1879,19 @@
 			best_cpu = cpu;
 		}
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+		if (do_rotate) {
+			/*
+			 * We started iteration somewhere in the middle of
+			 * cpumask.  Iterate once again from bit 0 to the
+			 * previous starting point bit.
+			 */
+			do_rotate = false;
+			cpu = -1;
+			goto retry;
+		}
+#endif
+
 		if (best_cpu != -1) {
 			return best_cpu;
 		} else if (!cpumask_empty(&backup_search_cpu)) {
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index a5b1377..7ac731d 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2841,3 +2841,17 @@
 {
 	return sched_feat(ENERGY_AWARE);
 }
+
+#ifdef CONFIG_SCHED_CORE_ROTATE
+struct find_first_cpu_bit_env {
+	unsigned long *avoid_prev_cpu_last;
+	int *rotate_cpu_start;
+	int interval;
+	spinlock_t *rotate_lock;
+};
+
+int
+find_first_cpu_bit(struct task_struct *p, const cpumask_t *search_cpus,
+		   struct sched_group *sg_target, bool *avoid_prev_cpu,
+		   bool *do_rotate, struct find_first_cpu_bit_env *env);
+#endif
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
index 166c643..46480a7 100644
--- a/kernel/sched/sched_avg.c
+++ b/kernel/sched/sched_avg.c
@@ -123,7 +123,7 @@
 EXPORT_SYMBOL(sched_get_nr_running_avg);
 
 #define BUSY_NR_RUN		3
-#define BUSY_LOAD_FACTOR	2
+#define BUSY_LOAD_FACTOR	10
 static inline void update_last_busy_time(int cpu, bool dequeue,
 				unsigned long prev_nr_run, u64 curr_time)
 {
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/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/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/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/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