Merge "icnss: Set fw down at server exit indication"
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
index 55d06b2..1a357b1 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -28,6 +28,14 @@
 	mask of the cluster mode in the composite state ID used to define
 	cluster low power modes in PSCI.
 
+Optional properties:
+	- qcom,disable-prediction: This property is used to indicate the LPM
+	governor will not use LPM prediction for this cluster.
+	- qcom,clstr-tmr-add: This property is used as correction timer for
+	wrong prediction by lpm prediction algorithm for cluster predictions.
+	This value should be between 100 to 1500. Higher values would mean
+	longer time staying in shallower state before waking up to select a
+	deeper state in case of wrong prediction.
 	qcom,pm-cluster contains qcom,pm-cluster-level nodes which identify
 	the various low power modes that the cluster can enter. The
 	qcom,pm-cluster node should also include another cluster node or a cpu
@@ -77,8 +85,22 @@
 	- qcom,pm-cpu-levels: The different low power modes that a CPU could
 	enter. The following section explains the required properties of this
 	node.
-	-qcom,use-prediction: This optional property is used to indicate the
-	the LPM governor is to apply sleep prediction to this cluster.
+
+Optional properties:
+	- qcom,disable-prediction: This property is used to indicate the
+	LPM governor is to disable sleep prediction to this cpu.
+	- qcom,ref-stddev: This property is used as reference standard deviation
+	in lpm prediction algorithm. This value should be between 100 to 1000.
+	Higher value would result in more predictions and thereby resulting in
+	shallower low power modes.
+	- qcom,tmr-add: This property is used as correction timer for wrong
+	prediction by lpm prediction algorithm. This value should be between
+	100 to 1500. Higher values would mean longer time staying in shallower
+	state before waking up to select a deeper state in case of wrong prediction.
+	- qcom,ref-premature-cnt: This property is used as reference premature
+	count to predict next sleep state by the prediction algorithm. This value
+	should be between 1 to 5. Higher value for this parameter would result in
+	less predictions to disallow deeper low power modes.
 
 [Node bindings for qcom,pm-cpu-levels]
  Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 931ef7a..a55ef6c 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -110,6 +110,9 @@
 - SDM450
   compatible = "qcom,sdm450"
 
+- SDA450
+  compatible = "qcom,sda450"
+
 - SDM632
   compatible = "qcom,sdm632"
 
@@ -357,6 +360,8 @@
 compatible = "qcom,sdm450-mtp"
 compatible = "qcom,sdm450-cdp"
 compatible = "qcom,sdm450-qrd"
+compatible = "qcom,sda450-mtp"
+compatible = "qcom,sda450-cdp"
 compatible = "qcom,sdm632-rumi"
 compatible = "qcom,sdm632-cdp"
 compatible = "qcom,sdm632-mtp"
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 6f1d8e3..addfd46 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -1,5 +1,8 @@
 * QCEDEV (QTI Crypto Engine Device)
 
+[Root level node]
+Crypto Engine
+============
 Required properties:
   - compatible : should be "qcom,qcedev"
   - reg : should contain crypto, BAM register map.
@@ -23,6 +26,19 @@
   - 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.
 
+
+[Second level nodes]
+Context banks
+=============
+Required properties:
+  - compatible : should be "qcom,qcedev,context-bank"
+  - iommus : A phandle parsed by smmu driver. Number of entries will vary across targets.
+
+Optional properties:
+  - label - string describing iommu domain usage.
+  - virtual-addr : start of virtual address pool.
+  - virtual-size : size of virtual address pool.
+
 Example:
 
 	qcom,qcedev@fd440000 {
@@ -42,4 +58,15 @@
 				<56 512 0 0>,
 				<56 512 3936000 393600>,
 		qcom,ce-opp-freq = <100000000>;
+
+		qcom_cedev_ns_cb {
+			compatible = "qcom,qcedev,context-bank";
+			label = "ns_context";
+			iommus = <&anoc2_smmu 0x1878>,
+				<&anoc2_smmu 0x1879>,
+				<&anoc2_smmu 0x187c>,
+				<&anoc2_smmu 0x187f>;
+			virtual-addr = <0x60000000>;
+			virtual-size = <0x00200000>;
+		};
 	};
diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
index 0f1d9e1..9addf15 100644
--- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
@@ -115,6 +115,10 @@
 - qcom,use-legacy-hard-reset-offset	Boolean property to support legacy
 				hard-reset offset of the PON_RB_SPARE register for
 				some (PON gen2) platforms.
+- qcom,support-twm-config       Boolean property to allow the PON module to be
+                                configured to support TWM modes.
+- qcom,pbs-client               Phandle of the PBS client node. Should be
+                                defined if 'qcom,support-twm-config' is present.
 
 All the below properties are in the sub-node section (properties of the child
 node).
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
index 1c7c2c5..f7494c4 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt
@@ -22,7 +22,29 @@
  - focaltech,reset-gpio	: reset gpio to control the reset of chip.
  - focaltech,display-coords : display coordinates in pixels. It is a four
 				tuple consisting of min x, min y, max x and
-				max y values.
+				max y values
+ - focaltech,name	: name of the controller
+ - focaltech,group-id	: group id of this device
+ - focaltech,hard-reset-delay-ms : hard reset delay in ms
+ - focaltech,soft-reset-delay-ms : soft reset delay in ms
+ - focaltech,fw-delay-aa-ms : specify the delay in ms after programming 0xaa
+				register for firmware upgrade
+ - focaltech,fw-delay-55-ms : specify the delay in ms after programming 0x55
+				register for firmware upgrade
+ - focaltech,fw-upgrade-id1 : specify the upgrade id1 for firmware upgrade
+ - focaltech,fw-upgrade-id2 : specify the upgrade id2 for firmware upgrade
+ - focaltech,fw-delay-readid-ms : specify the read id delay in ms for firmware upgrade
+ - focaltech,fw-delay-era-flsh-ms : specify the erase flash delay in ms for firmware upgrade
+ - pinctrl-names : This should be defined if a target uses pinctrl framework.
+			See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt.
+			Specify the names of the configs that pinctrl can install in driver.
+			Following are the pinctrl configs that can be installed:
+			"pmx_ts_active" : Active configuration of pins, this should specify active
+			config defined in pin groups of interrupt and reset gpio.
+			"pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep
+			config defined in pin groups of interrupt and reset gpio.
+			"pmx_ts_release" : Release configuration of pins, this should specify
+			release config defined in pin groups of interrupt and reset gpio.
 
 Optional properties:
 
@@ -32,22 +54,67 @@
  - focaltech,i2c-pull-up : to specify pull up is required.
  - focaltech,no-force-update : to specify force update is allowed.
  - focaltech,button-map : button map of key codes. The number
-				of key codes depend on panel.
+				of key codes depend on panel
+ - focaltech,fw-name	: specify the firmware file name
+ - focaltech,fw-delay-aa-ms : specify the "aa" delay in ms for firmware upgrade
+ - focaltech,fw-delay-55-ms : specify the "55" delay in ms for firmware upgrade
+ - focaltech,fw-upgrade-id1 : specify the upgrade id1 for firmware upgrade
+ - focaltech,fw-upgrade-id2 : specify the upgrade id2 for firmware upgrade
+ - focaltech,fw-delay-readid-ms : specify the read id delay in ms for firmware upgrade
+ - focaltech,fw-delay-era-flsh-ms : specify the erase flash delay in ms for firmware upgrade
+ - focaltech,fw-auto-cal	: specify whether calibration is needed after firmware upgrade
+ - focaltech,fw-vkey-support	: specify if virtual keys are supported through firmware
+ - focaltech,ignore-id-check : specify ignore family-id check
+ - focaltech,panel-coords : panel coordinates for the chip in pixels.
+				It is a four tuple consisting of min x,
+				min y, max x and max y values
+ - focaltech,fw-name	: specify the firmware file name
+ - focaltech,psensor-support	: specify whether support the proximity sensor
+ - focaltech,gesture-support	: specify whether support gesture feature
+ - focaltech,resume-in-workqueue : specifiy whether to defer the resume to workqueue
+ - clock-names:               : Clock names used for secure touch. They are: "iface_clk", "core_clk"
+ - clocks                        : Defined if 'clock-names' DT property is defined. These clocks
+				   are associated with the underlying I2C bus.
 
 Example:
-	i2c@f9924000 {
-		ft5x06_ts@38 {
+	i2c@f9923000{
+		focaltech@38{
 			compatible = "focaltech,5x06";
 			reg = <0x38>;
 			interrupt-parent = <&msmgpio>;
-			interrupts = <61 0x2>;
-			vdd-supply = <&pm8941_l22>;
-			vcc_i2c-supply = <&pm8941_s3>;
-			focaltech,reset-gpio = <&msmgpio 60 0x00>;
-			focaltech,irq-gpio = <&msmgpio 61 0x00>;
-			focaltech,panel-coords = <0 0 480 800>;
+			interrupts = <1 0x2>;
+			vdd-supply = <&pm8110_l19>;
+			vcc_i2c-supply = <&pm8110_l14>;
+			pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release";
+			pinctrl-0 = <&ts_int_active &ts_reset_active>;
+			pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+			pinctrl-2 = <&ts_release>;
+			focaltech,name = "ft6x06";
+			focaltech,family-id = <0x06>;
+			focaltech,reset-gpio = <&msmgpio 0 0x00>;
+			focaltech,irq-gpio = <&msmgpio 1 0x00>;
 			focaltech,display-coords = <0 0 480 800>;
-			focaltech,button-map= <158 102 139 217>;
-			focaltech,family-id = <0x0a>;
+			focaltech,panel-coords = <0 0 480 800>;
+			focaltech,button-map= <139 102 158>;
+			focaltech,no-force-update;
+			focaltech,i2c-pull-up;
+			focaltech,group-id = <1>;
+			focaltech,hard-reset-delay = <20>;
+			focaltech,soft-reset-delay = <150>;
+			focaltech,num-max-touches = <2>;
+			focaltech,fw-name = "ft_8610_qrd_fw.bin";
+			focaltech,fw-delay-aa-ms = <100>;
+			focaltech,fw-delay-55-ms = <30>;
+			focaltech,fw-upgrade-id1 = <0x79>;
+			focaltech,fw-upgrade-id2 = <0x08>;
+			focaltech,fw-delay-readid-ms = <10>;
+			focaltech,fw-delay-era-flsh-ms = <2000>;
+			focaltech,fw-auto-cal;
+			focaltech,psensor-support;
+			focaltech,gesture-support;
+			/* Underlying clocks used by secure touch */
+			clock-names = "iface_clk", "core_clk";
+			clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+			<&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
 		};
 	};
diff --git a/Documentation/devicetree/bindings/misc/qpnp-misc.txt b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
index a34cbde..ada8da9 100644
--- a/Documentation/devicetree/bindings/misc/qpnp-misc.txt
+++ b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
@@ -16,6 +16,12 @@
 				if a non-zero PWM source is specified under
 				"qcom,pwm-sel" property.
 
+- qcom,support-twm-config	Enable configuration for TWM mode.
+
+- qcom,twm-mode			The TWM mode which PMIC enters post power-off.
+				Valid only if 'qcom,support-twm-config' is
+				defined. If not specified, the default mode
+				is 3.
 Example:
 	qcom,misc@900 {
 		compatible = "qcom,qpnp-misc";
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
index 392ee7b..3d054f3 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -36,6 +36,8 @@
 	Definition: For details about IIO bindings see:
 		    Documentation/devicetree/bindings/iio/iio-bindings.txt
 
+- #thermal-sensor-cells: Should be 0. See thermal.txt for a description.
+
 - qcom,rradc-base
 	Usage:      required
 	Value type: <u32>
@@ -489,6 +491,7 @@
 	qcom,slope-limit-temp-threshold = <100>;
 	qcom,slope-limit-coeffs = <10 11 12 13>;
 	qcom,battery-thermal-coefficients = [9d 50 ff];
+	#thermal-sensor-cells = <0>;
 	status = "okay";
 
 	qcom,fg-batt-soc@4000 {
@@ -517,3 +520,32 @@
 		reg = <0x4400 0x100>;
 	};
 };
+
+======================================
+Example for thermal zone configuration
+======================================
+
+thermal_zones {
+	pmi8998_fg {
+		polling-delay-passive = <200>;
+		polling-delay = <200>;
+		thermal-governor = <userspace>;
+		thermal-sensors = <&pmi8998_fg>;
+
+		pmi8998_fg_trip1: pmi8998-fg-trip0 {
+			temperature = <45000>;
+			hysteresis = <0>;
+			type = "passive";
+		};
+		pmi8998_fg_trip2: pmi8998-fg-trip2 {
+			temperature = <50000>;
+			hysteresis = <0>;
+			type = "hot";
+		};
+		pmi8998_fg_trip3: pmi8998-fg-trip3 {
+			temperature = <60000>;
+			hysteresis = <0>;
+			type = "alert";
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index f87f7db..efa67f5 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -41,6 +41,16 @@
 		    when this interrupt fires. If not specified, the
 		    default value is 3200 mV.
 
+- qcom,vbatt-empty-cold-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: The battery voltage threshold (in mV) at which the
+		    vbatt-empty interrupt fires. This threshold is only
+		    applied at cold temperature specified by
+		    'qcom,cold-temp-threshold'. The SOC is forced to 0
+		    when this interrupt fires. If not specified, the
+		    default value is 3000 mV.
+
 - qcom,vbatt-cutoff-mv
 	Usage:      optional
 	Value type: <u32>
@@ -56,6 +66,16 @@
 		    the action when this interrupt fires. If not specified
 		    the default value is 3500 mV.
 
+- qcom,vbatt-low-cold-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: The battery voltage threshold (in mV) at which the
+		    the VBAT_LOW interrupt fires. The threshold is only
+		    applied at cold temperature specified by
+		    'qcom,cold-temp-threshold'. Software can take necessary
+		    the action when this interrupt fires. If not specified
+		    the default value is 3800 mV.
+
 - qcom,qg-iterm-ma
 	Usage:      optional
 	Value type: <u32>
@@ -164,6 +184,13 @@
 		    improve the user experience. This is applicable only if
 		    "qcom,hold-soc-while-full" is specified.
 
+- qcom,cold-temp-threshold
+	Usage:      optional
+	Value type: <u32>
+	Definition: Temperature threshold in decidegree at which the low
+		    temperature specific configuration as applied. If not
+		    specified, the default value is 0 degree centigrade.
+
 ==========================================================
 Second Level Nodes - Peripherals managed by QGAUGE driver
 ==========================================================
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
index 3174ccb..ddd90e1 100644
--- a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -11,26 +11,137 @@
 - reg:
 	Usage: required
 	Value type: <prop-encoded-array>
-	Definition: Register base and length for LPG modules. The length
-		      varies based on the number of channels available in
-		      the PMIC chips.
+	Definition: Register base and length for LPG and LUT modules. LPG size
+		      or length available per channel varies depending on the
+		      number of channels in PMIC.
 
 - reg-names:
 	Usage: required
 	Value type: <string>
 	Definition: The name of the register defined in the reg property.
-		      It must be "lpg-base".
+		      It must have "lpg-base", "lut-base" is optional but
+		      it's required if any LPG channels support LUT mode.
 
 - #pwm-cells:
 	Usage: required
 	Value type: <u32>
-	Definition: See Documentation/devicetree/bindings/pwm/pwm.txt;
+	Definition: The number of cells in "pwms" property specified in
+		      PWM user nodes. It should be 2. The first cell is
+		      the PWM channel ID indexed from 0, and the second
+		      cell is the PWM default period in nanoseconds.
 
+- qcom,lut-patterns:
+	Usage: optional
+	Value type: <prop-encoded-array>
+	Definition: Duty ratios in percentages for LPG working at LUT mode.
+		      These duty ratios will be translated into PWM values
+		      and stored in LUT module. The LUT module has resource
+		      to store 47 PWM values at max and shared for all LPG
+		      channels. This property is required if any LPG channels
+		      support LUT mode.
+
+Subnode is optional if LUT mode is not required, it's required if any LPG
+channels expected to be supported in LUT mode.
+
+Subnode properties:
+Subnodes for each LPG channel (lpg@X) can be defined if any of the following
+parameters needs to be configured for that channel.
+
+- qcom,lpg-chan-id:
+	Usage: required
+	Value type: <u32>
+	Definition: The LPG channel's hardware ID indexed from 1. Allowed
+		      range is 1 - 8. Maximum value depends on the number of
+		      channels supported on PMIC.
+
+- qcom,ramp-step-ms:
+	Usage: required
+	Value type: <u32>
+	Definition: The step duration in milliseconds for LPG staying at each
+		      duty specified in the LUT pattern. Allowed range is
+		      1 - 511.
+
+- qcom,ramp-high-index:
+	Usage: required
+	Value type: <u32>
+	Definition: The high index of the LUT pattern where LPG ends up
+		      ramping to. Allowed range is 1 - 47.
+
+- qcom,ramp-low-index:
+	Usage: required
+	Value type: <u32>
+	Definition: The low index of the LUT pattern from where LPG begins
+		      ramping from. Allowed range is 0 - 46.
+
+- qcom,ramp-from-low-to-high:
+	Usage: optional
+	Value type: <empty>
+	Definition: The flag to specify the LPG ramping direction. The ramping
+		      direction is from low index to high index of the LUT
+		      pattern if it's specified.
+
+- qcom,ramp-pattern-repeat:
+	Usage: optional
+	Value type: <empty>
+	Definition: The flag to specify if LPG would be ramping with the LUT
+		      pattern repeatedly.
+
+- qcom,ramp-toggle:
+	Usage: optional
+	Value type: <empty>
+	Definition: The flag to specify if LPG would toggle the LUT pattern
+		      in ramping. If toggling enabled, LPG would return to the
+		      low index when high index is reached, or return to the high
+		      index when low index is reached.
+
+- qcom,ramp-pause-hi-count:
+	Usage: optional
+	Value type: <u32>
+	Definition: The step count that LPG stop the output when it ramped up
+		      to the high index of the LUT.
+
+- qcom,ramp-pause-lo-count:
+	Usage: optional
+	Value type: <u32>
+	Definition: The step count that LPG stop the output when it ramped up
+		      to the low index of the LUT.
 Example:
 
 	pmi8998_lpg: lpg@b100 {
 		compatible = "qcom,pwm-lpg";
-		reg = <0xb100 0x600>;
-		reg-names = "lpg-base";
+		reg = <0xb100 0x600>, <0xb000 0x100>;
+		reg-names = "lpg-base", "lut-base";
 		#pwm-cells = <2>;
+		qcom,lut-patterns = <0 14 28 42 56 70 84 100
+					100 84 70 56 42 28 14 0>;
+		lpg@3 {
+			qcom,lpg-chan-id = <3>;
+			qcom,ramp-step-ms = <200>;
+			qcom,ramp-pause-hi-count = <10>;
+			qcom,ramp-pause-lo-count = <10>;
+			qcom,ramp-low-index = <0>;
+			qcom,ramp-high-index = <15>;
+			qcom,ramp-from-low-to-high;
+			qcom,ramp-pattern-repeat;
+		};
+		lpg@4 {
+			qcom,lpg-chan-id = <4>;
+			qcom,ramp-step-ms = <200>;
+			qcom,ramp-pause-hi-count = <10>;
+			qcom,ramp-pause-lo-count = <10>;
+			qcom,ramp-low-index = <0>;
+			qcom,ramp-high-index = <15>;
+			qcom,ramp-from-low-to-high;
+			qcom,ramp-pattern-repeat;
+		};
+		lpg@5 {
+			qcom,lpg-chan-id = <5>;
+			qcom,ramp-step-ms = <200>;
+			qcom,ramp-pause-hi-count = <10>;
+			qcom,ramp-pause-lo-count = <10>;
+			qcom,ramp-low-index = <0>;
+			qcom,ramp-high-index = <15>;
+			qcom,ramp-from-low-to-high;
+			qcom,ramp-pattern-repeat;
+		};
 	};
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index de40a7c..a034acc 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -15,6 +15,7 @@
 - qcom,rpc-latency-us:	FastRPC QoS latency vote
 - qcom,adsp-remoteheap-vmid:  FastRPC remote heap VMID list
 - qcom,fastrpc-adsp-audio-pdr:  Flag to enable ADSP Audio PDR
+- qcom,fastrpc-adsp-sensors-pdr: Flag to enable Sensors PDR
 
 Optional subnodes:
 - qcom,msm_fastrpc_compute_cb :	Child nodes representing the compute context
@@ -25,12 +26,17 @@
 - iommus : 		A list of phandle and IOMMU specifier pairs that describe the
 			IOMMU master interfaces of the device
 
+Subnode Optional properties:
+- shared-cb : 		Present if context bank need to be shared
+
+
 Example:
 	qcom,msm_fastrpc {
 		compatible = "qcom,msm-fastrpc-adsp";
 		qcom,fastrpc-glink;
 		qcom,rpc-latency-us = <2343>;
 		qcom,adsp-remoteheap-vmid = <22 37>;
+		qcom,fastrpc-adsp-sensors-pdr;
 
 		qcom,msm_fastrpc_compute_cb_1 {
 			compatible = "qcom,msm-fastrpc-compute-cb";
@@ -41,6 +47,7 @@
 			compatible = "qcom,msm-fastrpc-compute-cb";
 			label = "adsprpc-smd";
 			iommus = <&lpass_q6_smmu 9>,
+			shared-cb;
 		};
 	};
 
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
index 9798ac60..72c4eaf 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
@@ -212,8 +212,9 @@
 - qcom,bst-headroom-mv
 	Usage:      optional
 	Value type:  <u16>
-	Definition:  Headroom of the boost (in mV). The minimum headroom is
-		     200mV and if not specified defaults to 200mV.
+	Definition:  Headroom of the boost (in mV). If not specified, then the
+		     default value is 200 mV (PM660L) or 150 mV (for PM855L or
+		     PMI632).
 
 =======
 Example
diff --git a/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt b/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt
index 149a01a..1417eb0 100644
--- a/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/bg_daemon.txt
@@ -6,9 +6,15 @@
 Required properties:
 - compatible : should be "qcom,bg-daemon"
 - qcom,bg-reset-gpio : gpio for the apps processor use to soft reset BG
+- ssr-reg1-supply : Power supply needed to power up the BG device.
+		When BG brought up this regulator will be in normal power mode.
+- ssr-reg2-supply : Power supply needed to power up the BG device.
+		When BG BG brought up this regulator will be in normal power mode.
 
 Example:
 	qcom,bg-daemon {
 		compatible = "qcom,bg-daemon";
 		qcom,bg-reset-gpio = <&pm660_gpios 5 0>;
+		ssr-reg1-supply = <&pm660_l3>;
+		ssr-reg2-supply = <&pm660_l9>;
 	};
diff --git a/Documentation/devicetree/bindings/sound/aqt1000_codec.txt b/Documentation/devicetree/bindings/sound/aqt1000_codec.txt
new file mode 100644
index 0000000..af615b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/aqt1000_codec.txt
@@ -0,0 +1,79 @@
+Qualcomm Technologies, Inc. for AQT1000 audio CODEC
+
+Required properties:
+
+- compatible: "qcom,aqt1000-i2c-codec" for AQT1000 Codec
+
+- qcom,aqt-rst-gpio-node: Phandle reference to the DT node having codec reset gpio
+			  configuration.
+
+- reg: Address of the codec. This property enabled I2C to get AQT1000 base address.
+
+- qcom,cdc-static-supplies: Static VREG_BOB Supply for AQT1000 codec.
+			    All other supplies are derived from this.
+
+-qcom,cdc-micbias1-mv: micbias1 output voltage in milli volts.
+		       This is used when cfilt is not user configurable
+		       and micbias1 is directly controlled with a register
+		       write.
+
+-qcom,cdc-mclk-clk-rate: Specifies the master clock rate in Hz required for
+			 codec.
+
+Optional properties:
+
+-qcom,cdc-ext-clk-rate: External clock frequency used for AQT1000.
+			All internal clocks for AQT1000 are derived from this clock.
+			If this property is not defined then default external
+			clock rate of 9.6M will be provided.
+
+- qcom,cdc-vdd-mic-bias-supply: phandle of vreg_bob supply's regulator device tree
+			   node.
+
+- qcom,cdc-vdd-mic-bias-voltage: vreg_bob supply's voltage level min and max
+				 in mV.
+- qcom,cdc-vdd-mic-bias-current: vreg_bob supply's max current in mA.
+
+- qcom,cdc-micbias-ldoh-v: LDOH output in volts (should be 3V).
+
+- qcom,cdc-micbias-cfilt1-mv: cfilt1 output voltage in milli volts.
+  cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+- qcom,cdc-micbias1-cfilt-sel: cfilt to use for micbias1
+			(should be from 1 to 3).
+
+- clock-names : clock name defined for external clock.
+- clocks : external clock defined for codec clock.
+
+Example:
+i2c@a88000 {
+	status = "ok";
+	aqt1000_cdc: aqt1000-i2c-codec@d {
+		status = "disabled";
+		compatible = "qcom,aqt1000-i2c-codec";
+		reg = <0x0d>;
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&tlmm>;
+		qcom,gpio-connect = <&tlmm 79 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aqt_intr_default>;
+
+		qcom,aqt-rst-gpio-node = <&aqt_rst_gpio>;
+
+		qcom,cdc-vdd-mic-bias-supply = <&pm660l_bob>;
+		qcom,cdc-vdd-mic-bias-voltage = <3312000 3312000>;
+		qcom,cdc-vdd-mic-bias-current = <30400>;
+		qcom,cdc-static-supplies = "cdc-vdd-mic-bias";
+
+		qcom,cdc-micbias-ldoh-v = <3>;
+
+		qcom,cdc-ext-clk-rate = <19200000>;
+		qcom,cdc-mclk-clk-rate = <9600000>;
+
+		qcom,cdc-micbias1-mv = <1800>;
+
+		clock-names = "aqt_clk";
+		clocks = <&clock_audio_lnbb AUDIO_PMIC_LNBB_CLK>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 991e26c..f13dbde 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -1729,6 +1729,9 @@
 - qcom,msm-mi2s-master: This property is used to inform machine driver
   if MSM is the clock master of mi2s. 1 means master and 0 means slave. The
   first entry is primary mi2s; the second entry is secondary mi2s, and so on.
+- qcom,mi2s-aqt-enabled: This property is used to inform machine driver
+  if AQT1000 codec is enabled or not. If this is enabled then codec name
+  for TERT_MI2S needs to be overridden with AQT codec.
 - qcom,msm-mi2s-ext-mclk: This property is used to inform machine driver
   if MCLK from MSM is used for any external audio connections. 1 means used
   as external mclk source and 0 indicate not used. The first entry is
diff --git a/Documentation/devicetree/bindings/sound/wcd_codec.txt b/Documentation/devicetree/bindings/sound/wcd_codec.txt
index 6d2ae5e..585da1a 100644
--- a/Documentation/devicetree/bindings/sound/wcd_codec.txt
+++ b/Documentation/devicetree/bindings/sound/wcd_codec.txt
@@ -439,6 +439,7 @@
 				Value from 4000 to 5550 in mV in steps of 50 mV can be given.
  - qcom,dig-cdc-base-addr: Specifies the digital codec base address for MSM digital
 				core register writes.
+ - qcom,anlg-cdc-mbhc-disable: Boolean variable that informs if AQT1000 codec is enabled or not.
 
 Example:
 
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index a4ebeeb..38ba7fc 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -565,7 +565,13 @@
 			compatible = "qcom,msm-imem-diag-dload";
 			reg = <0xc8 200>;
 		};
-};
+	};
+
+	qcom,mpm2-sleep-counter@c221000 {
+		compatible = "qcom,mpm2-sleep-counter";
+		reg = <0x0c221000 0x1000>;
+		clock-frequency = <32768>;
+	};
 
 	restart@c264000 {
 		compatible = "qcom,pshold";
@@ -654,6 +660,13 @@
 		qcom,fragmented-data;
 	};
 
+	qcom,glink-ssr-modem {
+		compatible = "qcom,glink_ssr";
+		label = "modem";
+		qcom,edge = "mpss";
+		qcom,xprt = "smem";
+	};
+
 	qcom,glink_pkt {
 		compatible = "qcom,glinkpkt";
 
@@ -813,8 +826,6 @@
 	qcom,rmnet-ipa {
 		compatible = "qcom,rmnet-ipa3";
 		qcom,rmnet-ipa-ssr;
-		qcom,ipa-loaduC;
-		qcom,ipa-advertise-sg-support;
 	};
 
 	dcc: dcc_v2@10a2000 {
diff --git a/arch/arm/configs/msm8909w-perf_defconfig b/arch/arm/configs/msm8909w-perf_defconfig
index 46052bd..75372aa 100644
--- a/arch/arm/configs/msm8909w-perf_defconfig
+++ b/arch/arm/configs/msm8909w-perf_defconfig
@@ -15,7 +15,6 @@
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
-CONFIG_CGROUP_DEBUG=y
 CONFIG_CGROUP_FREEZER=y
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
@@ -27,12 +26,14 @@
 CONFIG_SCHED_AUTOGROUP=y
 CONFIG_SCHED_TUNE=y
 CONFIG_DEFAULT_USE_ENERGY_AWARE=y
+CONFIG_RELAY=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_KALLSYMS_ALL=y
 CONFIG_BPF_SYSCALL=y
 CONFIG_EMBEDDED=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
@@ -49,7 +50,6 @@
 CONFIG_AEABI=y
 CONFIG_HIGHMEM=y
 CONFIG_CMA=y
-CONFIG_CMA_DEBUGFS=y
 CONFIG_ZSMALLOC=y
 CONFIG_BALANCE_ANON_FILE_RECLAIM=y
 CONFIG_SECCOMP=y
@@ -62,7 +62,6 @@
 CONFIG_PM_AUTOSLEEP=y
 CONFIG_PM_WAKELOCKS=y
 CONFIG_PM_WAKELOCKS_LIMIT=0
-CONFIG_PM_DEBUG=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
@@ -173,7 +172,6 @@
 CONFIG_BRIDGE_NF_EBTABLES=y
 CONFIG_BRIDGE_EBT_BROUTE=y
 CONFIG_L2TP=y
-CONFIG_L2TP_DEBUGFS=y
 CONFIG_L2TP_V3=y
 CONFIG_L2TP_IP=y
 CONFIG_L2TP_ETH=y
@@ -266,8 +264,6 @@
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_QPNP_POWER_ON=y
 CONFIG_INPUT_UINPUT=y
-CONFIG_SERIAL_MSM=y
-CONFIG_SERIAL_MSM_CONSOLE=y
 CONFIG_SERIAL_MSM_HS=y
 CONFIG_SERIAL_MSM_SMD=y
 CONFIG_DIAG_CHAR=y
@@ -464,27 +460,11 @@
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_PRINTK_TIME=y
-CONFIG_DYNAMIC_DEBUG=y
 CONFIG_DEBUG_INFO=y
 CONFIG_FRAME_WARN=2048
 CONFIG_PAGE_OWNER=y
 CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
 CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_PAGEALLOC=y
-CONFIG_SLUB_DEBUG_PANIC_ON=y
-CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
-CONFIG_SLUB_DEBUG_ON=y
-CONFIG_DEBUG_KMEMLEAK=y
-CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
-CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
-CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_DEBUG_MEMORY_INIT=y
 CONFIG_LOCKUP_DETECTOR=y
 # CONFIG_DETECT_HUNG_TASK is not set
 CONFIG_WQ_WATCHDOG=y
@@ -493,26 +473,11 @@
 CONFIG_PANIC_ON_RT_THROTTLING=y
 CONFIG_SCHEDSTATS=y
 CONFIG_SCHED_STACK_END_CHECK=y
-CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_ATOMIC_SLEEP=y
-CONFIG_DEBUG_LIST=y
-CONFIG_FAULT_INJECTION=y
-CONFIG_FAIL_PAGE_ALLOC=y
-CONFIG_FAULT_INJECTION_DEBUG_FS=y
-CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
-CONFIG_IPC_LOGGING=y
-CONFIG_QCOM_RTB=y
-CONFIG_QCOM_RTB_SEPARATE_CPUS=y
-CONFIG_FUNCTION_TRACER=y
-CONFIG_IRQSOFF_TRACER=y
-CONFIG_PREEMPT_TRACER=y
-CONFIG_BLK_DEV_IO_TRACE=y
-CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
 CONFIG_LKDTM=y
-CONFIG_MEMTEST=y
 CONFIG_PANIC_ON_DATA_CORRUPTION=y
-CONFIG_DEBUG_USER=y
+# CONFIG_ARM_UNWIND is not set
 CONFIG_PID_IN_CONTEXTIDR=y
 CONFIG_DEBUG_SET_MODULE_RONX=y
 CONFIG_CORESIGHT=y
diff --git a/arch/arm/configs/msm8909w_defconfig b/arch/arm/configs/msm8909w_defconfig
index 46052bd..086b127 100644
--- a/arch/arm/configs/msm8909w_defconfig
+++ b/arch/arm/configs/msm8909w_defconfig
@@ -33,6 +33,7 @@
 CONFIG_EMBEDDED=y
 CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
diff --git a/arch/arm/configs/msm8953-perf_defconfig b/arch/arm/configs/msm8953-perf_defconfig
index df6f5dd..e63cc3355 100644
--- a/arch/arm/configs/msm8953-perf_defconfig
+++ b/arch/arm/configs/msm8953-perf_defconfig
@@ -55,6 +55,7 @@
 CONFIG_ARCH_QCOM=y
 CONFIG_ARCH_MSM8953=y
 CONFIG_ARCH_SDM450=y
+CONFIG_ARCH_SDM632=y
 # CONFIG_VDSO is not set
 CONFIG_SMP=y
 CONFIG_SCHED_MC=y
@@ -287,6 +288,7 @@
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_FT5X06=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_HBTP_INPUT=y
 CONFIG_INPUT_QPNP_POWER_ON=y
@@ -576,6 +578,7 @@
 CONFIG_TMPFS=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm/configs/msm8953_defconfig b/arch/arm/configs/msm8953_defconfig
index 1a7f3d1..653e5a4 100644
--- a/arch/arm/configs/msm8953_defconfig
+++ b/arch/arm/configs/msm8953_defconfig
@@ -58,6 +58,7 @@
 CONFIG_ARCH_QCOM=y
 CONFIG_ARCH_MSM8953=y
 CONFIG_ARCH_SDM450=y
+CONFIG_ARCH_SDM632=y
 # CONFIG_VDSO is not set
 CONFIG_SMP=y
 CONFIG_SCHED_MC=y
@@ -292,6 +293,7 @@
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_FT5X06=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_HBTP_INPUT=y
 CONFIG_INPUT_QPNP_POWER_ON=y
@@ -592,6 +594,7 @@
 CONFIG_TMPFS=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 6b5e0b4..1ab1fbb 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -150,6 +150,22 @@
 	select HAVE_CLK_PREPARE
 	select COMMON_CLK_MSM
 
+config ARCH_SDM632
+	bool "Enable Support for Qualcomm Technologies Inc. SDM632"
+	select CPU_V7
+	select PINCTRL
+	select QCOM_SCM if SMP
+	select CLKDEV_LOOKUP
+	select HAVE_CLK
+	select HAVE_CLK_PREPARE
+	select SOC_BUS
+	select PM_OPP
+	select MFD_CORE
+	select SND_SOC_COMPRESS
+	select SND_HWDEP
+	select CPU_FREQ_QCOM
+	select COMMON_CLK_MSM
+
 config ARCH_MDM9650
 	bool "MDM9650"
 	select ARM_GIC
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index 5802657..5b93fa3 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -10,3 +10,4 @@
 obj-$(CONFIG_ARCH_SDM450) += board-sdm450.o
 obj-$(CONFIG_ARCH_MDM9650) += board-9650.o
 obj-$(CONFIG_ARCH_MDM9607) += board-9607.o
+obj-$(CONFIG_ARCH_SDM632) += board-sdm632.o
diff --git a/arch/arm/mach-qcom/board-sdm632.c b/arch/arm/mach-qcom/board-sdm632.c
new file mode 100644
index 0000000..c6a55f2
--- /dev/null
+++ b/arch/arm/mach-qcom/board-sdm632.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include "board-dt.h"
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
+static const char *sdm632_dt_match[] __initconst = {
+	"qcom,sdm632",
+	NULL
+};
+
+static void __init sdm632_init(void)
+{
+	board_dt_populate(NULL);
+}
+
+DT_MACHINE_START(SDM632_DT,
+	"Qualcomm Technologies, Inc. SDM632 (Flattened Device Tree)")
+	.init_machine		= sdm632_init,
+	.dt_compat		= sdm632_dt_match,
+MACHINE_END
diff --git a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
index c9996f8..3947406 100644
--- a/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/8909w-pm660.dtsi
@@ -450,7 +450,7 @@
 			pins = "gpio12";
 			function = "normal";
 			output-enable;
-			qcom,drive-strength = "medium";
+			qcom,drive-strength = <2>;
 		};
 	};
 
@@ -459,7 +459,7 @@
 			pins = "gpio5";
 			function = "func1";
 			output-enable;
-			qcom,drive-strength = "medium";
+			qcom,drive-strength = <2>;
 			bias-disable;
 		};
 	};
@@ -469,6 +469,15 @@
 	qcom,support-twm-config;
 };
 
+&pm660_pbs {
+	status = "okay";
+};
+
+&pm660_pon {
+	qcom,support-twm-config;
+	qcom,pbs-client = <&pm660_pbs>;
+};
+
 / {
 	/delete-node/ qcom,battery-data;
 	mtp_batterydata: qcom,battery-data {
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 7457bbb..85c4df1 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -317,6 +317,7 @@
 msm8953-mtp-overlay.dtbo-base := sdm450.dtb \
 	msm8953.dtb \
 	apq8053.dtb \
+	sda450.dtb \
 	msm8953-pmi8940.dtb \
 	msm8953-pmi8937.dtb \
 	sdm450-pmi8940.dtb \
@@ -324,6 +325,7 @@
 msm8953-cdp-overlay.dtbo-base := sdm450.dtb \
 	msm8953.dtb \
 	apq8053.dtb \
+	sda450.dtb \
 	msm8953-pmi8940.dtb \
 	msm8953-pmi8937.dtb
 msm8953-rcm-overlay.dtbo-base := sdm450.dtb \
@@ -346,10 +348,12 @@
 sdm450-cdp-s2-overlay.dtbo-base := sdm450-pmi632.dtb \
 	sdm632.dtb \
 	sdm632-pm8004.dtb \
-	msm8953-pmi632.dtb
+	msm8953-pmi632.dtb \
+	sda450-pmi632.dtb
 sdm450-mtp-s3-overlay.dtbo-base := sdm450-pmi632.dtb \
 	sdm632.dtb \
-	sdm632-pm8004.dtb
+	sdm632-pm8004.dtb \
+	sda450-pmi632.dtb
 sdm450-qrd-sku4-overlay.dtbo-base := sdm450-pmi632.dtb \
 	sdm632.dtb \
 	sdm632-pm8004.dtb
@@ -422,13 +426,17 @@
 dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
 	sdm450-cdp.dtb \
 	sdm450-mtp.dtb \
+	sda450-cdp.dtb \
+	sda450-mtp.dtb \
 	sdm450-qrd.dtb \
 	sdm450-pmi8940-mtp.dtb \
 	sdm450-pmi8937-mtp.dtb \
 	sdm450-iot-mtp.dtb \
 	sdm450-qrd-sku4.dtb \
 	sdm450-pmi632-cdp-s2.dtb \
-	sdm450-pmi632-mtp-s3.dtb
+	sdm450-pmi632-mtp-s3.dtb \
+	sda450-pmi632-cdp-s2.dtb \
+	sda450-pmi632-mtp-s3.dtb
 
 dtb-$(CONFIG_ARCH_SDM632) += sdm632-rumi.dtb \
 	sdm632-cdp-s2.dtb \
diff --git a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
index c203e17..48fbc40 100644
--- a/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8053-lite-dragon.dtsi
@@ -92,7 +92,7 @@
 	android {
 		vbmeta {
 			compatible = "android,vbmeta";
-			parts = "vbmeta,boot,system,vendor,bluetooth,modem";
+			parts = "vbmeta,boot,system,vendor,bluetooth,modem,oem";
 		};
 		fstab {
 			/delete-node/ system;
diff --git a/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
index 74f2be0..858429b 100644
--- a/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909-ion.dtsi
@@ -39,6 +39,12 @@
 			qcom,ion-heap-type = "DMA";
 		};
 
+		qcom,ion-heap@19 { /* QSEECOM TA HEAP */
+			reg = <19>;
+			memory-region = <&qseecom_ta_mem>;
+			qcom,ion-heap-type = "DMA";
+		};
+
 		adsp_heap: qcom,ion-heap@22 { /* MODEM HEAP */
 			reg = <22>;
 			memory-region = <&adsp_mem>;
diff --git a/arch/arm64/boot/dts/qcom/msm8909.dtsi b/arch/arm64/boot/dts/qcom/msm8909.dtsi
index ec16e60e..eed3623 100644
--- a/arch/arm64/boot/dts/qcom/msm8909.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8909.dtsi
@@ -194,6 +194,14 @@
 			size = <0 0x0800000>;
 		};
 
+		qseecom_ta_mem: qseecom_ta_region {
+			compatible = "shared-dma-pool";
+			alloc-ranges = <0 0x00000000 0 0xffffffff>;
+			reusable;
+			alignment = <0 0x400000>;
+			size = <0 0x400000>;
+		};
+
 		audio_mem: audio_region@0 {
 			compatible = "shared-dma-pool";
 			reusable;
diff --git a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
index 8f2eac0..d9fa6fb 100644
--- a/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
+++ b/arch/arm64/boot/dts/qcom/msm8909w-bg-wtp-v2.dts
@@ -69,6 +69,17 @@
 		/delete-node/ it7260@46;
 	};
 
+	qcom,blackghost {
+		compatible = "qcom,pil-blackghost";
+		qcom,firmware-name = "bg-wear";
+		/* GPIO inputs from blackghost */
+		qcom,bg2ap-status-gpio = <&msm_gpio 97 0>;
+		qcom,bg2ap-errfatal-gpio = <&msm_gpio 95 0>;
+		/* GPIO output to blackghost */
+		qcom,ap2bg-status-gpio = <&msm_gpio 17 0>;
+		qcom,ap2bg-errfatal-gpio = <&msm_gpio 23 0>;
+	};
+
 	qcom,msm-ssc-sensors {
 		compatible = "qcom,msm-ssc-sensors";
 	};
@@ -155,6 +166,8 @@
 	qcom,bg-daemon {
 		compatible = "qcom,bg-daemon";
 		qcom,bg-reset-gpio = <&pm660_gpios 5 0>;
+		ssr-reg1-supply = <&pm660_l3>;
+		ssr-reg2-supply = <&pm660_l9>;
 	};
 
 	qcom,bcl {
diff --git a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
index d857c82..ab86021 100644
--- a/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
+++ b/arch/arm64/boot/dts/qcom/msm8917-pmi8937-qrd-sku5.dts
@@ -61,3 +61,46 @@
 	qcom,esd-check-enabled;
 	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
 };
+
+&soc {
+	i2c@78b7000 {
+		status = "ok";
+		focaltech@38 {
+			compatible = "focaltech,5x06";
+			reg = <0x38>;
+			interrupt-parent = <&tlmm>;
+			interrupts = <65 0x2>;
+			vdd-supply = <&pm8937_l10>;
+			vcc_i2c-supply = <&pm8937_l5>;
+			/* pins used by touchscreen */
+			pinctrl-names = "pmx_ts_active",
+						"pmx_ts_suspend",
+						"pmx_ts_release";
+			pinctrl-0 = <&ts_int_active &ts_reset_active>;
+			pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+			pinctrl-2 = <&ts_release>;
+			focaltech,name = "ft5436";
+			focaltech,family-id = <0x06>;
+			focaltech,reset-gpio = <&tlmm 64 0x0>;
+			focaltech,irq-gpio = <&tlmm 65 0x2008>;
+			focaltech,display-coords = <0 0 720 1280>;
+			focaltech,panel-coords = <0 0 720 1400>;
+			focaltech,button-map= <139 102 158>;
+			focaltech,no-force-update;
+			focaltech,i2c-pull-up;
+			focaltech,group-id = <1>;
+			focaltech,hard-reset-delay-ms = <20>;
+			focaltech,soft-reset-delay-ms = <200>;
+			focaltech,num-max-touches = <5>;
+			focaltech,fw-delay-aa-ms = <30>;
+			focaltech,fw-delay-55-ms = <30>;
+			focaltech,fw-upgrade-id1 = <0x79>;
+			focaltech,fw-upgrade-id2 = <0x08>;
+			focaltech,fw-delay-readid-ms = <10>;
+			focaltech,fw-delay-era-flsh-ms = <2000>;
+			focaltech,fw-auto-cal;
+			focaltech,ignore-id-check;
+			focaltech,resume-in-workqueue;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
index 9fde72c..716ef73 100644
--- a/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937-interposer-sdm439.dtsi
@@ -27,6 +27,12 @@
 };
 
 &soc {
+	qcom,csiphy@1b34000 {
+		compatible = "qcom,csiphy-v3.4.2", "qcom,csiphy";
+	};
+	qcom,csiphy@1b35000 {
+		compatible = "qcom,csiphy-v3.4.2", "qcom,csiphy";
+	};
 	qcom,csid@1b30000 {
 		qcom,mipi-csi-vdd-supply = <&pm8953_l2>;
 	};
diff --git a/arch/arm64/boot/dts/qcom/msm8937.dtsi b/arch/arm64/boot/dts/qcom/msm8937.dtsi
index 632c924..7b9f74b 100644
--- a/arch/arm64/boot/dts/qcom/msm8937.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8937.dtsi
@@ -191,6 +191,17 @@
 		      <0x0b002000 0x1000>;
 	};
 
+	dcc: dcc@b3000 {
+		compatible = "qcom,dcc";
+		reg = <0xb3000 0x1000>,
+		      <0xb4000 0x2000>;
+		reg-names = "dcc-base", "dcc-ram-base";
+
+		clocks = <&clock_gcc clk_gcc_dcc_clk>;
+		clock-names = "apb_pclk";
+		qcom,save-reg;
+	};
+
 	wakegic: wake-gic {
 		compatible = "qcom,mpm-gic-msm8937", "qcom,mpm-gic";
 		interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
index 8782325..9b78253 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
@@ -42,6 +42,7 @@
 		qcom,nq-ven = <&tlmm 16 0x00>;
 		qcom,nq-firm = <&tlmm 62 0x00>;
 		qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+		qcom,nq-esepwr = <&tlmm 141 0x00>;
 		interrupt-parent = <&tlmm>;
 		qcom,clk-src = "BBCLK2";
 		interrupts = <17 0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
index 08a343e..b182a25 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp-overlay.dts
@@ -17,7 +17,81 @@
 #include "msm8953-mtp.dtsi"
 
 / {
-	model = "Ext Codec MTP";
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 Ext Codec MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
 	qcom,board-id= <8 1>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
 
+&int_codec {
+	status = "disabled";
+};
+
+&pmic_analog_codec {
+	status = "disabled";
+};
+
+&wsa881x_i2c_f {
+	status = "disabled";
+};
+
+&wsa881x_i2c_45 {
+	status = "disabled";
+};
+
+&cdc_pri_mi2s_gpios {
+	status = "disabled";
+};
+
+&wsa881x_analog_vi_gpio {
+	status = "disabled";
+};
+
+&wsa881x_analog_clk_gpio {
+	status = "disabled";
+};
+
+&wsa881x_analog_reset_gpio {
+	status = "disabled";
+};
+
+&cdc_comp_gpios {
+	status = "disabled";
+};
+
+&slim_msm {
+	status = "okay";
+};
+
+&dai_slim {
+	status = "okay";
+};
+
+&wcd9xxx_intc {
+	status = "okay";
+};
+
+&clock_audio {
+	status = "okay";
+};
+
+&wcd9335 {
+	status = "okay";
+};
+
+&cdc_us_euro_sw {
+	status = "okay";
+};
+
+&cdc_quin_mi2s_gpios {
+	status = "okay";
+};
+
+&wcd_rst_gpio {
+	status = "okay";
+};
+
+&ext_codec {
+	qcom,model = "msm8953-tasha-snd-card";
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
index e3a5b4a..b80583e 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
@@ -25,75 +25,3 @@
 	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
 
-&int_codec {
-	status = "disabled";
-};
-
-&pmic_analog_codec {
-	status = "disabled";
-};
-
-&wsa881x_i2c_f {
-	status = "disabled";
-};
-
-&wsa881x_i2c_45 {
-	status = "disabled";
-};
-
-&cdc_pri_mi2s_gpios {
-	status = "disabled";
-};
-
-&wsa881x_analog_vi_gpio {
-	status = "disabled";
-};
-
-&wsa881x_analog_clk_gpio {
-	status = "disabled";
-};
-
-&wsa881x_analog_reset_gpio {
-	status = "disabled";
-};
-
-&cdc_comp_gpios {
-	status = "disabled";
-};
-
-&slim_msm {
-	status = "okay";
-};
-
-&dai_slim {
-	status = "okay";
-};
-
-&wcd9xxx_intc {
-	status = "okay";
-};
-
-&clock_audio {
-	status = "okay";
-};
-
-&wcd9335 {
-	status = "okay";
-};
-
-&cdc_us_euro_sw {
-	status = "okay";
-};
-
-&cdc_quin_mi2s_gpios {
-	status = "okay";
-};
-
-&wcd_rst_gpio {
-	status = "okay";
-};
-
-&ext_codec {
-	qcom,model = "msm8953-tasha-snd-card";
-	status = "okay";
-};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi b/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
index b62d12d..0e635d4 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
@@ -18,3 +18,61 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart_console_active>;
 };
+
+&sdhc_1 {
+	/* device core power supply */
+	vdd-supply = <&pm8953_l8>;
+	qcom,vdd-voltage-level = <2900000 2900000>;
+	qcom,vdd-current-level = <200 570000>;
+
+	/* device communication power supply */
+	vdd-io-supply = <&pm8953_l5>;
+	qcom,vdd-io-always-on;
+	qcom,vdd-io-lpm-sup;
+	qcom,vdd-io-voltage-level = <1800000 1800000>;
+	qcom,vdd-io-current-level = <200 325000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on  &sdc1_rclk_on>;
+	pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+	qcom,clk-rates = <400000 20000000 25000000 50000000 100000000 192000000
+								384000000>;
+	qcom,nonremovable;
+	qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+	status = "ok";
+};
+
+&sdhc_2 {
+	/* device core power supply */
+	vdd-supply = <&pm8953_l11>;
+	qcom,vdd-voltage-level = <2950000 2950000>;
+	qcom,vdd-current-level = <15000 800000>;
+
+	/* device communication power supply */
+	vdd-io-supply = <&pm8953_l12>;
+	qcom,vdd-io-voltage-level = <1800000 2950000>;
+	qcom,vdd-io-current-level = <200 22000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+
+	#address-cells = <0>;
+	interrupt-parent = <&sdhc_2>;
+	interrupts = <0 1 2>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0xffffffff>;
+	interrupt-map = <0 &intc 0 125 0
+		1 &intc 0 221 0
+		2 &tlmm 133 0>;
+	interrupt-names = "hc_irq", "pwr_irq", "status_irq";
+	cd-gpios = <&tlmm 133 0x1>;
+
+	qcom,clk-rates = <400000 20000000 25000000 50000000 100000000
+								200000000>;
+	qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index b861573..cc4bc7f 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -41,6 +41,7 @@
 		qcom,nq-ven = <&tlmm 16 0x00>;
 		qcom,nq-firm = <&tlmm 62 0x00>;
 		qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+		qcom,nq-esepwr = <&tlmm 141 0x00>;
 		interrupt-parent = <&tlmm>;
 		qcom,clk-src = "BBCLK2";
 		interrupts = <17 0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
index c8fcfd5..7d73a69 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -746,39 +746,67 @@
 			};
 		};
 
-		pmx_rd_nfc_int {
-			/*qcom,pins = <&gp 17>;*/
-			pins = "gpio17";
-			qcom,pin-func = <0>;
-			qcom,num-grp-pins = <1>;
-			label = "pmx_nfc_int";
+		nfc {
+			nfc_int_active: nfc_int_active {
+				/* active state */
+				mux {
+					/* GPIO 17 NFC Read Interrupt */
+					pins = "gpio17";
+					function = "gpio";
+				};
 
-			nfc_int_active: active {
-				drive-strength = <6>;
-				bias-pull-up;
+				config {
+					pins = "gpio17";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
 			};
 
-			nfc_int_suspend: suspend {
-				drive-strength = <6>;
-				bias-pull-up;
-			};
-		};
+			nfc_int_suspend: nfc_int_suspend {
+				/* sleep state */
+				mux {
+					/* GPIO 17 NFC Read Interrupt */
+					pins = "gpio17";
+					function = "gpio";
+				};
 
-		pmx_nfc_reset {
-			/*qcom,pins = <&gp 16>;*/
-			pins = "gpio16";
-			qcom,pin-func = <0>;
-			qcom,num-grp-pins = <1>;
-			label = "pmx_nfc_disable";
-
-			nfc_disable_active: active {
-				drive-strength = <6>;
-				bias-pull-up;
+				config {
+					pins = "gpio17";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
 			};
 
-			nfc_disable_suspend: suspend {
-				drive-strength = <6>;
-				bias-disable;
+			nfc_disable_active: nfc_disable_active {
+				/* active state */
+				mux {
+					/* 16: NFC ENABLE 62: FW DNLD */
+					/* 141: ESE Enable */
+					pins = "gpio16", "gpio62", "gpio141";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio16", "gpio62", "gpio141";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
+			};
+
+			nfc_disable_suspend: nfc_disable_suspend {
+				/* sleep state */
+				mux {
+					/* 16: NFC ENABLE 62: FW DNLD */
+					/* 141: ESE Enable */
+					pins = "gpio16", "gpio62", "gpio141";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio16", "gpio62", "gpio141";
+					drive-strength = <2>; /* 2 MA */
+					bias-disable;
+				};
 			};
 		};
 
diff --git a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
index 253e87e..1f0ad88 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
@@ -69,6 +69,7 @@
 		qcom,nq-ven = <&tlmm 16 0x00>;
 		qcom,nq-firm = <&tlmm 62 0x00>;
 		qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
+		qcom,nq-esepwr = <&tlmm 141 0x00>;
 		interrupt-parent = <&tlmm>;
 		qcom,clk-src = "BBCLK2";
 		interrupts = <17 0>;
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 50ee0e8..b33a7db 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -349,11 +349,6 @@
 		compatible = "qcom,mem-dump";
 		memory-region = <&dump_mem>;
 
-		rpmh_dump {
-			qcom,dump-size = <0x2000000>;
-			qcom,dump-id = <0xec>;
-		};
-
 		fcm_dump {
 			qcom,dump-size = <0x8400>;
 			qcom,dump-id = <0xee>;
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index fa10500..e8a82aa 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -32,7 +32,7 @@
 			reg = <0x900 0x100>;
 		};
 
-		qcom,power-on@800 {
+		pm660_pon: qcom,power-on@800 {
 			compatible = "qcom,qpnp-power-on";
 			reg = <0x800 0x100>;
 			interrupts = <0x0 0x8 0x0 IRQ_TYPE_NONE>,
@@ -349,6 +349,12 @@
 			};
 		};
 
+		pm660_pbs: qcom,pbs@7400 {
+			compatible = "qcom,qpnp-pbs";
+			reg = <0x7400 0x100>;
+			status = "disabled";
+		};
+
 		bcl_sensor: bcl@4200 {
 			compatible = "qcom,msm-bcl-lmh";
 			reg = <0x4200 0xff>,
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 8bb9dbe..b8f8b68 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -29,7 +29,9 @@
 		pmi632_pon: qcom,power-on@800 {
 			compatible = "qcom,qpnp-power-on";
 			reg = <0x800 0x100>;
+
 			qcom,secondary-pon-reset;
+			qcom,s3-src = "kpdpwr";
 		};
 
 		pmi632_vadc: vadc@3100 {
@@ -464,9 +466,6 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
-			qcom,vbatt-empty-mv = <3300>;
-			qcom,vbatt-low-mv = <3500>;
-			qcom,vbatt-cutoff-mv = <3400>;
 			qcom,qg-iterm-ma = <100>;
 			qcom,hold-soc-while-full;
 			qcom,linearize-soc;
@@ -476,11 +475,12 @@
 			qcom,qgauge@4800 {
 				status = "okay";
 				reg = <0x4800 0x100>;
-				interrupts = <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x2 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x3 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x4 IRQ_TYPE_EDGE_BOTH>;
+				interrupts =
+					<0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
+					<0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
+					<0x2 0x48 0x2 IRQ_TYPE_EDGE_RISING>,
+					<0x2 0x48 0x3 IRQ_TYPE_EDGE_RISING>,
+					<0x2 0x48 0x4 IRQ_TYPE_EDGE_RISING>;
 				interrupt-names = "qg-batt-missing",
 						  "qg-vbat-low",
 						  "qg-vbat-empty",
diff --git a/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
index aa316ba..63bb25f 100644
--- a/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
@@ -166,6 +166,10 @@
 	qcom,battery-data = <&qcs_batterydata>;
 };
 
+&pm660_charger {
+	qcom,battery-data = <&qcs_batterydata>;
+};
+
 &int_codec {
 	qcom,model = "sdm670-360cam-snd-card";
 	qcom,audio-routing =
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
index 8af4254..f28f19e2 100644
--- a/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-ascent-3450mah.dtsi
@@ -32,6 +32,11 @@
 				4201000  4300000  2760000
 				4301000  4350000  2070000>;
 
+	/* COOL = 5 DegC, WARM = 40 DegC */
+	qcom,jeita-soft-thresholds = <0x44bd 0x1fc4>;
+	/* COLD = 0 DegC, HOT = 45 DegC */
+	qcom,jeita-hard-thresholds = <0x4aa5 0x1bfb>;
+
 	qcom,fcc1-temp-lut {
 		qcom,lut-col-legend = <0 10 25 40 50>;
 		qcom,lut-data = <3377 3428 3481 3496 3500>;
diff --git a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
index 6bcfd37..3b18010 100644
--- a/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/qg-batterydata-mlp356477-2800mah.dtsi
@@ -29,6 +29,11 @@
 				 151 450  4400000
 				 451 550  4150000>;
 
+	/* COOL = 15 DegC, WARM = 45 DegC */
+	qcom,jeita-soft-thresholds = <0x4621 0x20b8>;
+	/* COLD = 0 DegC, HOT = 55 DegC */
+	qcom,jeita-hard-thresholds = <0x58cd 0x181d>;
+
 	qcom,fcc1-temp-lut {
 		qcom,lut-col-legend = <0 10 25 40 50>;
 		qcom,lut-data = <2715 2788 2861 2898 2908>;
diff --git a/arch/arm64/boot/dts/qcom/sda450-cdp.dts b/arch/arm64/boot/dts/qcom/sda450-cdp.dts
new file mode 100644
index 0000000..e8f22b8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-cdp.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-cdp.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 CDP";
+	compatible = "qcom,sda450-cdp", "qcom,sda450", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-mtp.dts b/arch/arm64/boot/dts/qcom/sda450-mtp.dts
new file mode 100644
index 0000000..06002e0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-mtp.dts
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-mtp.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 MTP";
+	compatible = "qcom,sda450-mtp", "qcom,sda450", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
+/{
+	mtp_batterydata: qcom,battery-data {
+		qcom,batt-id-range-pct = <15>;
+		#include "batterydata-itech-3000mah.dtsi"
+		#include "batterydata-ascent-3450mAh.dtsi"
+	};
+};
+
+&qpnp_fg {
+	qcom,battery-data = <&mtp_batterydata>;
+};
+
+&qpnp_smbcharger {
+	qcom,battery-data = <&mtp_batterydata>;
+	qcom,chg-led-sw-controls;
+	qcom,chg-led-support;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts
new file mode 100644
index 0000000..14c5e22
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-cdp-s2.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632-cdp-s2.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450 + PMI632 CDP S2";
+	compatible = "qcom,sda450-cdp", "qcom,sda450", "qcom,cdp";
+	qcom,board-id = <1 2>;
+	qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
new file mode 100644
index 0000000..c907977
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632-mtp-s3.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632-mtp-s3.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450 + PMI632 MTP S3";
+	compatible = "qcom,sda450-mtp", "qcom,sda450", "qcom,mtp";
+	qcom,board-id = <8 3>;
+	qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450-pmi632.dts b/arch/arm64/boot/dts/qcom/sda450-pmi632.dts
new file mode 100644
index 0000000..1bb0b47
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450-pmi632.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "sdm450-pmi632.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450 + PMI632 SOC";
+	compatible = "qcom,sda450";
+	qcom,pmic-id = <0x010016 0x25 0x0 0x0>;
+	qcom,pmic-name = "PMI632";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450.dts b/arch/arm64/boot/dts/qcom/sda450.dts
new file mode 100644
index 0000000..13b1622
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "sda450.dtsi"
+#include "pmi8950.dtsi"
+#include "msm8953-pmi8950.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450 + PMI8950 SOC";
+	compatible = "qcom,sda450";
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+	qcom,pmic-name = "PMI8950";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sda450.dtsi b/arch/arm64/boot/dts/qcom/sda450.dtsi
new file mode 100644
index 0000000..ba99fe9
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda450.dtsi
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm450.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA450";
+	compatible = "qcom,sda450";
+	qcom,msm-id = <351 0x0>;
+	qcom,msm-name = "SDA450";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
index 5075862..300c83a 100644
--- a/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439-pmi632.dtsi
@@ -39,6 +39,10 @@
 	qcom,battery-data = <&mtp_batterydata>;
 };
 
+&pmi632_charger {
+	qcom,battery-data = <&mtp_batterydata>;
+};
+
 &pm8953_typec {
 	status = "disabled";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm439.dtsi b/arch/arm64/boot/dts/qcom/sdm439.dtsi
index 6a77e64..812c0ad 100644
--- a/arch/arm64/boot/dts/qcom/sdm439.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm439.dtsi
@@ -23,13 +23,13 @@
 
 &soc {
 	qcom,csid@1b30000 {
-		/delete-property/ qcom,mipi-csi-vdd-supply;
+		qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
 	};
 	qcom,csid@1b30400 {
-		/delete-property/ qcom,mipi-csi-vdd-supply;
+		qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
 	};
 	qcom,csid@1b30800 {
-		/delete-property/ qcom,mipi-csi-vdd-supply;
+		qcom,mipi-csi-vdd-supply = <&pm8953_l23>;
 	};
 
 	msm_cpufreq: qcom,msm-cpufreq {
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index a4b6054..5c127bc 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -45,6 +45,10 @@
 	qcom,battery-data = <&mtp_batterydata>;
 };
 
+&pmi632_charger {
+	qcom,battery-data = <&mtp_batterydata>;
+};
+
 &pm8953_gpios {
 	bklt_en {
 		bklt_en_default: bklt_en_default {
diff --git a/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi b/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi
index c53bb56..de1bd1f 100644
--- a/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm632-cpu.dtsi
@@ -248,7 +248,7 @@
 				1363200	88
 				1536000	112
 				1670400	151
-				1785600	192
+				1804800	194
 			>;
 			idle-cost-data = <
 				20 16 12 8
@@ -261,9 +261,9 @@
 				1036800	1739
 				1401600	2819
 				1555200	3532
-				1785600	4985
+				1804800	5038
 				1996000	6624
-				2082800	6905
+				2016000	6688
 			>;
 			idle-cost-data = <
 				100 80 60 40
@@ -277,7 +277,7 @@
 				1363200	28
 				1536000	35
 				1670400	43
-				1785600	54
+				1804800	54
 			>;
 			idle-cost-data = <
 				4 3 2 1
@@ -290,9 +290,9 @@
 				1036800	132
 				1401600	193
 				1555200	233
-				1785600	289
+				1804800	292
 				1996000	374
-				2082800	386
+				2016000	377
 			>;
 			idle-cost-data = <
 				4 3 2 1
diff --git a/arch/arm64/boot/dts/qcom/sdm670-aqt1000-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-aqt1000-cdp-overlay.dts
index dc6a841..001bcd4 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-aqt1000-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-aqt1000-cdp-overlay.dts
@@ -19,8 +19,9 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
+#include "sdm670-int-cdc-aqt.dtsi"
 #include "sdm670-cdp.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-int-cdc-aqt-overlay.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L AQT CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
index 2b3cb39..4d6b32f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
@@ -320,6 +320,57 @@
 	wdsp_glink: qcom,wcd-dsp-glink {
 		compatible = "qcom,wcd-dsp-glink";
 	};
+
+	aqt_rst_gpio: aqt_cdc_pinctrl {
+		status = "disabled";
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&aqt_rst_active>;
+		pinctrl-1 = <&aqt_rst_idle>;
+	};
+
+	tert_mi2s_gpios: tert_mi2s_pinctrl {
+		status = "disabled";
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&ter_i2s_data0_active &ter_i2s_data1_active
+			     &ter_i2s_sck_active>;
+		pinctrl-1 = <&ter_i2s_data0_sleep &ter_i2s_data1_sleep
+			     &ter_i2s_sck_sleep>;
+	};
+
+	i2c@a88000 {
+		aqt1000_cdc: aqt1000-i2c-codec@d {
+			status = "disabled";
+			compatible = "qcom,aqt1000-i2c-codec";
+			reg = <0x0d>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+			interrupt-parent = <&tlmm>;
+			qcom,gpio-connect = <&tlmm 79 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&aqt_intr_default>;
+
+			qcom,aqt-rst-gpio-node = <&aqt_rst_gpio>;
+
+			qcom,cdc-vdd-mic-bias-supply = <&pm660l_bob>;
+			qcom,cdc-vdd-mic-bias-voltage = <3312000 3312000>;
+			qcom,cdc-vdd-mic-bias-current = <30400>;
+
+			qcom,cdc-static-supplies = "cdc-vdd-mic-bias";
+
+			qcom,cdc-micbias-ldoh-v = <3>;
+
+			qcom,cdc-ext-clk-rate = <19200000>;
+			qcom,cdc-mclk-clk-rate = <9600000>;
+
+			qcom,cdc-micbias1-mv = <1800>;
+
+			clock-names = "aqt_clk";
+			clocks = <&clock_audio_lnbb AUDIO_PMIC_LNBB_CLK>;
+		};
+	};
 };
 
 &slim_aud {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-int-cdc-aqt-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-int-cdc-aqt-overlay.dtsi
new file mode 100644
index 0000000..5f70790
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-int-cdc-aqt-overlay.dtsi
@@ -0,0 +1,85 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&int_codec {
+	qcom,model = "sdm670-aqt-snd-card";
+	qcom,msm-mi2s-master = <1>, <1>, <0>, <1>, <1>;
+	qcom,tert-mi2s-gpios = <&tert_mi2s_gpios>;
+	qcom,us-euro-gpios = <&wcd_usbc_analog_en2_gpio>;
+	asoc-codec = <&stub_codec>, <&msm_digital_codec>,
+		     <&pmic_analog_codec>, <&msm_sdw_codec>,
+		     <&ext_disp_audio_codec>, <&aqt1000_cdc>;
+	qcom,audio-routing =
+		"RX_BIAS", "INT_MCLK0",
+		"SPK_RX_BIAS", "INT_MCLK0",
+		"INT_LDO_H", "INT_MCLK0",
+		"RX_I2S_CLK", "INT_MCLK0",
+		"TX_I2S_CLK", "INT_MCLK0",
+		"MIC BIAS External", "Handset Mic",
+		"AQT MIC BIAS1", "Headset Mic",
+		"MIC BIAS External", "Secondary Mic",
+		"AMIC1", "MIC BIAS External",
+		"AMIC2", "AQT MIC BIAS1",
+		"AMIC3", "MIC BIAS External",
+		"DMIC1", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic1",
+		"DMIC2", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic2",
+		"DMIC3", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic3",
+		"DMIC4", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic4",
+		"SpkrLeft IN", "SPK1 OUT",
+		"SpkrRight IN", "SPK2 OUT",
+		"PDM_IN_RX1", "PDM_OUT_RX1",
+		"PDM_IN_RX2", "PDM_OUT_RX2",
+		"PDM_IN_RX3", "PDM_OUT_RX3",
+		"ADC1_IN", "ADC1_OUT",
+		"ADC2_IN", "ADC2_OUT",
+		"ADC3_IN", "ADC3_OUT";
+	asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec",
+			   "analog-codec", "msm_sdw_codec",
+			   "msm-ext-disp-audio-codec-rx", "aqt1000-i2c-codec";
+	qcom,mi2s-aqt-enabled;
+};
+
+&pmic_analog_codec {
+	qcom,anlg-cdc-mbhc-disable;
+};
+
+&soc {
+	i2c@a88000 {
+		status = "ok";
+	};
+	wcd_usbc_analog_en2_gpio: msm_cdc_pinctrl_usbc_audio_en2 {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&wcd_usbc_analog_en2_active>;
+		pinctrl-1 = <&wcd_usbc_analog_en2_idle>;
+	};
+};
+
+&tert_mi2s_gpios {
+	status = "ok";
+};
+
+&clock_audio_lnbb {
+	status = "ok";
+};
+
+&aqt1000_cdc {
+	status = "ok";
+};
+
+&aqt_rst_gpio {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-int-cdc-aqt.dtsi b/arch/arm64/boot/dts/qcom/sdm670-int-cdc-aqt.dtsi
new file mode 100644
index 0000000..56e60f2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-int-cdc-aqt.dtsi
@@ -0,0 +1,64 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm670-audio-overlay.dtsi"
+
+&dai_mi2s2 {
+	qcom,msm-mi2s-rx-lines = <1>;
+	qcom,msm-mi2s-tx-lines = <2>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
+	qcom,platform-reset-gpio = <&tlmm 90 0>;
+	qcom,panel-mode-gpio = <&tlmm 4 0>;
+};
+
+&dsi_dual_nt35597_truly_cmd_display {
+	qcom,platform-reset-gpio = <&tlmm 90 0>;
+	qcom,panel-mode-gpio = <&tlmm 4 0>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	qcom,platform-reset-gpio = <&tlmm 90 0>;
+	qcom,panel-mode-gpio = <&tlmm 4 0>;
+};
+
+&dsi_nt35597_truly_dsc_video_display {
+	qcom,platform-reset-gpio = <&tlmm 90 0>;
+	qcom,panel-mode-gpio = <&tlmm 4 0>;
+};
+
+&sde_dsi_active {
+	mux {
+		pins = "gpio90", "gpio4";
+		function = "gpio";
+	};
+
+	config {
+		pins = "gpio90", "gpio4";
+		drive-strength = <8>;   /* 8 mA */
+		bias-disable = <0>;   /* no pull */
+	};
+};
+
+&sde_dsi_suspend {
+	mux {
+		pins = "gpio90", "gpio4";
+		function = "gpio";
+	};
+
+	config {
+		pins = "gpio90", "gpio4";
+		drive-strength = <2>;   /* 2 mA */
+		bias-pull-down;         /* PULL DOWN */
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 68f51e2..cc55127 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -141,6 +141,10 @@
 	qcom,battery-data = <&mtp_batterydata>;
 };
 
+&pm660_charger {
+	qcom,battery-data = <&mtp_batterydata>;
+};
+
 &tlmm {
 	smb_int_default: smb_int_default {
 		mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index d4ce0b1..0461429 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -1725,6 +1725,132 @@
 			};
 		};
 
+		aqt_intr {
+			aqt_intr_default: aqt_intr_default{
+				mux {
+					pins = "gpio79";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio79";
+					drive-strength = <2>; /* 2 mA */
+					bias-pull-down; /* pull down */
+					input-enable;
+				};
+			};
+		};
+
+		aqt_rst_gpio {
+			aqt_rst_idle: aqt_rst_idle{
+				mux {
+					pins = "gpio80";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio80";
+					drive-strength = <8>;
+					bias-pull-down;
+					output-low;
+				};
+			};
+			aqt_rst_active: aqt_rst_active{
+				mux {
+					pins = "gpio80";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio80";
+					drive-strength = <8>;
+					bias-disable;
+					output-high;
+				};
+			};
+		};
+
+		ter_i2s_sck_ws {
+			ter_i2s_sck_sleep: ter_i2s_sck_sleep {
+				mux {
+					pins = "gpio75", "gpio76";
+					function = "ter_mi2s";
+				};
+
+				config {
+					pins = "gpio75", "gpio76";
+					drive-strength = <2>;   /* 2 mA */
+				};
+			};
+
+			ter_i2s_sck_active: ter_i2s_sck_active {
+				mux {
+					pins = "gpio75", "gpio76";
+					function = "ter_mi2s";
+				};
+
+				config {
+					pins = "gpio75", "gpio76";
+					drive-strength = <8>;   /* 8 mA */
+					input-enable;
+				};
+			};
+		};
+
+		ter_i2s_data0 {
+			ter_i2s_data0_sleep: ter_i2s_data0_sleep {
+				mux {
+					pins = "gpio77";
+					function = "ter_mi2s";
+				};
+
+				config {
+					pins = "gpio77";
+					drive-strength = <2>;   /* 2 mA */
+				};
+			};
+
+			ter_i2s_data0_active: ter_i2s_data0_active {
+				mux {
+					pins = "gpio77";
+					function = "ter_mi2s";
+				};
+
+				config {
+					pins = "gpio77";
+					drive-strength = <8>;   /* 8 mA */
+					input-enable;
+				};
+			};
+		};
+
+		ter_i2s_data1 {
+			ter_i2s_data1_sleep: ter_i2s_data1_sleep {
+				mux {
+					pins = "gpio78";
+					function = "ter_mi2s";
+				};
+
+				config {
+					pins = "gpio78";
+					drive-strength = <2>;   /* 2 mA */
+				};
+			};
+
+			ter_i2s_data1_active: ter_i2s_data1_active {
+				mux {
+					pins = "gpio78";
+					function = "ter_mi2s";
+				};
+
+				config {
+					pins = "gpio78";
+					drive-strength = <8>;   /* 8 mA */
+					output-high;
+				};
+			};
+		};
+
 		pmx_sde: pmx_sde {
 			sde_dsi_active: sde_dsi_active {
 				mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
index c54b8db..bdcd039 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm.dtsi
@@ -23,6 +23,7 @@
 			label = "L3";
 			qcom,psci-mode-shift = <4>;
 			qcom,psci-mode-mask = <0xfff>;
+			qcom,clstr-tmr-add = <1000>;
 
 			qcom,pm-cluster-level@0 { /* D1 */
 				reg = <0>;
@@ -77,7 +78,9 @@
 				#size-cells = <0>;
 				qcom,psci-mode-shift = <0>;
 				qcom,psci-mode-mask = <0xf>;
-				qcom,use-prediction;
+				qcom,ref-stddev = <500>;
+				qcom,tmr-add = <1000>;
+				qcom,ref-premature-cnt = <1>;
 				qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4
 									&CPU5>;
 
@@ -131,6 +134,7 @@
 				#size-cells = <0>;
 				qcom,psci-mode-shift = <0>;
 				qcom,psci-mode-mask = <0xf>;
+				qcom,disable-prediction;
 				qcom,cpu = <&CPU6 &CPU7>;
 
 				qcom,pm-cpu-level@0 { /* C1 */
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-aqt1000-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-aqt1000-cdp-overlay.dts
index c33d062..1260e05 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-aqt1000-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-aqt1000-cdp-overlay.dts
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,9 +19,10 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
+#include "sdm670-int-cdc-aqt.dtsi"
 #include "sdm670-cdp.dtsi"
+#include "sdm670-int-cdc-aqt-overlay.dtsi"
 #include "pm660a.dtsi"
-#include "sdm670-audio-overlay.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A AQT CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm710-aqt1000-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm710-aqt1000-cdp-overlay.dts
index 7106bbb..31c99de 100644
--- a/arch/arm64/boot/dts/qcom/sdm710-aqt1000-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm710-aqt1000-cdp-overlay.dts
@@ -19,8 +19,9 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
+#include "sdm670-int-cdc-aqt.dtsi"
 #include "sdm670-cdp.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-int-cdc-aqt-overlay.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM710 PM660 + PM660L AQT CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm710-pm660a-aqt1000-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm710-pm660a-aqt1000-cdp-overlay.dts
index 145e3c1..50b3470 100644
--- a/arch/arm64/boot/dts/qcom/sdm710-pm660a-aqt1000-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm710-pm660a-aqt1000-cdp-overlay.dts
@@ -21,7 +21,8 @@
 
 #include "sdm670-cdp.dtsi"
 #include "pm660a.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-int-cdc-aqt.dtsi"
+#include "sdm670-int-cdc-aqt-overlay.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM710 PM660 + PM660A AQT CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
index 929239a..350f156 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
@@ -21,6 +21,7 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 			label = "L3";
+			qcom,clstr-tmr-add = <1000>;
 			qcom,psci-mode-shift = <4>;
 			qcom,psci-mode-mask = <0xfff>;
 
@@ -52,7 +53,9 @@
 				#size-cells = <0>;
 				qcom,psci-mode-shift = <0>;
 				qcom,psci-mode-mask = <0xf>;
-				qcom,use-prediction;
+				qcom,ref-stddev = <500>;
+				qcom,tmr-add = <1000>;
+				qcom,ref-premature-cnt = <1>;
 				qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
 
 				qcom,pm-cpu-level@0 { /* C1 */
@@ -95,6 +98,9 @@
 				#size-cells = <0>;
 				qcom,psci-mode-shift = <0>;
 				qcom,psci-mode-mask = <0xf>;
+				qcom,ref-stddev = <100>;
+				qcom,tmr-add = <100>;
+				qcom,ref-premature-cnt = <3>;
 				qcom,cpu = <&CPU4 &CPU5 &CPU6 &CPU7>;
 
 				qcom,pm-cpu-level@0 { /* C1 */
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index ffc68d2..22b4b90 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2570,9 +2570,31 @@
 			 <&clock_gcc GCC_CE1_AXI_CLK>;
 		qcom,ce-opp-freq = <171430000>;
 		qcom,request-bw-before-clk;
+
 		qcom,smmu-s1-enable;
 		iommus = <&apps_smmu 0x706 0x1>,
 			 <&apps_smmu 0x716 0x1>;
+
+		qcom_cedev_ns_cb {
+			compatible = "qcom,qcedev,context-bank";
+			label = "ns_context";
+			iommus = <&apps_smmu 0x712 0>,
+			       <&apps_smmu 0x71f 0>;
+			virtual-addr = <0x60000000>;
+			virtual-size = <0x40000000>;
+		};
+
+		qcom_cedev_s_cb {
+			compatible = "qcom,qcedev,context-bank";
+			label = "secure_context";
+			iommus = <&apps_smmu 0x713 0>,
+			       <&apps_smmu 0x71c 0>,
+			       <&apps_smmu 0x71d 0>,
+			       <&apps_smmu 0x71e 0>;
+			virtual-addr = <0x60200000>;
+			virtual-size = <0x40000000>;
+			qcom,secure-context-bank;
+		};
 	};
 
 	qcom_msmhdcp: qcom,msm_hdcp {
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 0fe026f..a101b43 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -587,6 +587,7 @@
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index b15ef8a..1cfee45 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -607,6 +607,7 @@
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 85b7f58..6bfe775 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -244,6 +244,7 @@
 CONFIG_IPC_ROUTER=y
 CONFIG_IPC_ROUTER_SECURITY=y
 CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_AQT_REGMAP=y
 CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
 CONFIG_DMA_CMA=y
 CONFIG_ZRAM=y
@@ -618,11 +619,6 @@
 CONFIG_SLUB_DEBUG_PANIC_ON=y
 CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
 CONFIG_PAGE_POISONING=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
 CONFIG_SLUB_DEBUG_ON=y
 CONFIG_DEBUG_KMEMLEAK=y
 CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 8f213f4..f5dc2a8 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -545,7 +545,6 @@
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
 CONFIG_QMP_DEBUGFS_CLIENT=y
-CONFIG_MEM_SHARE_QMI_SERVICE=y
 CONFIG_QSEE_IPC_IRQ_BRIDGE=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_ARM_MEMLAT_MON=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index ac25fb9..64cdeae 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -564,7 +564,6 @@
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
 CONFIG_QMP_DEBUGFS_CLIENT=y
-CONFIG_MEM_SHARE_QMI_SERVICE=y
 CONFIG_MSM_REMOTEQDSS=y
 CONFIG_QSEE_IPC_IRQ_BRIDGE=y
 CONFIG_QCOM_BIMC_BWMON=y
@@ -618,11 +617,6 @@
 CONFIG_SLUB_DEBUG_PANIC_ON=y
 CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
 CONFIG_PAGE_POISONING=y
-CONFIG_DEBUG_OBJECTS=y
-CONFIG_DEBUG_OBJECTS_FREE=y
-CONFIG_DEBUG_OBJECTS_TIMERS=y
-CONFIG_DEBUG_OBJECTS_WORK=y
-CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
 CONFIG_SLUB_DEBUG_ON=y
 CONFIG_DEBUG_KMEMLEAK=y
 CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 85997c0..e52727c 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -181,6 +181,85 @@
 #define ESR_ELx_SYS64_ISS_SYS_CNTFRQ	(ESR_ELx_SYS64_ISS_SYS_VAL(3, 3, 0, 14, 0) | \
 					 ESR_ELx_SYS64_ISS_DIR_READ)
 
+/* ISS field definitions for CP15 AArch32 access traps */
+#define ESR_ELx_CP15_32_ISS_CV_SHIFT	24
+#define ESR_ELx_CP15_32_ISS_CV_MASK \
+				(UL(0x1) << ESR_ELx_CP15_32_ISS_CV_SHIFT)
+#define ESR_ELx_CP15_32_ISS_DIR_MASK	0x1
+#define ESR_ELx_CP15_32_ISS_DIR_READ	0x1
+#define ESR_ELx_CP15_32_ISS_DIR_WRITE	0x0
+
+#define ESR_ELx_CP15_32_ISS_RT_SHIFT	5
+#define ESR_ELx_CP15_32_ISS_RT_MASK \
+				(UL(0x1f) << ESR_ELx_CP15_32_ISS_RT_SHIFT)
+#define ESR_ELx_CP15_32_ISS_CRM_SHIFT	1
+#define ESR_ELx_CP15_32_ISS_CRM_MASK \
+				(UL(0xf) << ESR_ELx_CP15_32_ISS_CRM_SHIFT)
+#define ESR_ELx_CP15_32_ISS_CRN_SHIFT	10
+#define ESR_ELx_CP15_32_ISS_CRN_MASK \
+				(UL(0xf) << ESR_ELx_CP15_32_ISS_CRN_SHIFT)
+#define ESR_ELx_CP15_32_ISS_OP1_SHIFT	14
+#define ESR_ELx_CP15_32_ISS_OP1_MASK \
+				(UL(0x7) << ESR_ELx_CP15_32_ISS_OP1_SHIFT)
+#define ESR_ELx_CP15_32_ISS_OP2_SHIFT	17
+#define ESR_ELx_CP15_32_ISS_OP2_MASK \
+				(UL(0x7) << ESR_ELx_CP15_32_ISS_OP2_SHIFT)
+#define ESR_ELx_CP15_32_ISS_COND_SHIFT	20
+#define ESR_ELx_CP15_32_ISS_COND_MASK \
+				(UL(0xf) << ESR_ELx_CP15_32_ISS_COND_SHIFT)
+#define ESR_ELx_CP15_32_ISS_SYS_MASK	(ESR_ELx_CP15_32_ISS_OP1_MASK | \
+					 ESR_ELx_CP15_32_ISS_OP2_MASK | \
+					 ESR_ELx_CP15_32_ISS_CRN_MASK | \
+					 ESR_ELx_CP15_32_ISS_CRM_MASK)
+#define ESR_ELx_CP15_32_ISS_SYS_VAL(op1, op2, crn, crm) \
+				(((op1) << ESR_ELx_CP15_32_ISS_OP1_SHIFT) | \
+				 ((op2) << ESR_ELx_CP15_32_ISS_OP2_SHIFT) | \
+				 ((crn) << ESR_ELx_CP15_32_ISS_CRN_SHIFT) | \
+				 ((crm) << ESR_ELx_CP15_32_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_32_ISS_SYS_OP_MASK	(ESR_ELx_CP15_32_ISS_SYS_MASK | \
+					 ESR_ELx_CP15_32_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_32_ISS_SYS_CNTFRQ	\
+				(ESR_ELx_CP15_32_ISS_SYS_VAL(0, 0, 14, 0) | \
+				 ESR_ELx_CP15_32_ISS_DIR_READ)
+
+/* ISS field definitions for CP15 AArch32 64-bit access traps */
+#define ESR_ELx_CP15_64_ISS_CV_SHIFT	24
+#define ESR_ELx_CP15_64_ISS_CV_MASK \
+				(UL(0x1) << ESR_ELx_CP15_64_ISS_CV_SHIFT)
+#define ESR_ELx_CP15_64_ISS_DIR_MASK	0x1
+#define ESR_ELx_CP15_64_ISS_DIR_READ	0x1
+#define ESR_ELx_CP15_64_ISS_DIR_WRITE	0x0
+
+#define ESR_ELx_CP15_64_ISS_RT_SHIFT	5
+#define ESR_ELx_CP15_64_ISS_RT_MASK \
+				(UL(0x1f) << ESR_ELx_CP15_64_ISS_RT_SHIFT)
+#define ESR_ELx_CP15_64_ISS_CRM_SHIFT	1
+#define ESR_ELx_CP15_64_ISS_CRM_MASK \
+				(UL(0xf) << ESR_ELx_CP15_64_ISS_CRM_SHIFT)
+#define ESR_ELx_CP15_64_ISS_RT2_SHIFT	10
+#define ESR_ELx_CP15_64_ISS_RT2_MASK \
+				(UL(0x1f) << ESR_ELx_CP15_64_ISS_RT2_SHIFT)
+#define ESR_ELx_CP15_64_ISS_OP1_SHIFT	16
+#define ESR_ELx_CP15_64_ISS_OP1_MASK \
+				(UL(0xf) << ESR_ELx_CP15_64_ISS_OP1_SHIFT)
+#define ESR_ELx_CP15_64_ISS_COND_SHIFT	20
+#define ESR_ELx_CP15_64_ISS_COND_MASK \
+				(UL(0xf) << ESR_ELx_CP15_64_ISS_COND_SHIFT)
+#define ESR_ELx_CP15_64_ISS_SYS_MASK	(ESR_ELx_CP15_64_ISS_OP1_MASK | \
+					 ESR_ELx_CP15_64_ISS_CRM_MASK)
+#define ESR_ELx_CP15_64_ISS_SYS_VAL(op1, crm) \
+				(((op1) << ESR_ELx_CP15_64_ISS_OP1_SHIFT) | \
+				 ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_64_ISS_SYS_OP_MASK	(ESR_ELx_CP15_64_ISS_SYS_MASK | \
+					 ESR_ELx_CP15_64_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_64_ISS_SYS_CNTVCT	\
+				(ESR_ELx_CP15_64_ISS_SYS_VAL(1, 14) | \
+				 ESR_ELx_CP15_64_ISS_DIR_READ)
+
 #ifndef __ASSEMBLY__
 #include <asm/types.h>
 
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 458773a..69f6cae 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -130,8 +130,12 @@
 #ifdef CONFIG_COMPAT
 #define compat_thumb_mode(regs) \
 	(((regs)->pstate & COMPAT_PSR_T_BIT))
+#define compat_arm_instr_set(regs) \
+	(((regs)->pstate & (COMPAT_PSR_T_BIT | COMPAT_PSR_J_BIT)) == 0)
 #else
 #define compat_thumb_mode(regs) (0)
+#define compat_arm_instr_set(regs) (0)
+
 #endif
 
 #define user_mode(regs)	\
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 3b22fa3..f8ba35d 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -601,9 +601,9 @@
 	cmp	x24, #ESR_ELx_EC_UNKNOWN	// unknown exception in EL0
 	b.eq	el0_undef
 	cmp	x24, #ESR_ELx_EC_CP15_32	// CP15 MRC/MCR trap
-	b.eq	el0_undef
+	b.eq	el0_cp15_32_compat
 	cmp	x24, #ESR_ELx_EC_CP15_64	// CP15 MRRC/MCRR trap
-	b.eq	el0_undef
+	b.eq	el0_cp15_64_compat
 	cmp	x24, #ESR_ELx_EC_CP14_MR	// CP14 MRC/MCR trap
 	b.eq	el0_undef
 	cmp	x24, #ESR_ELx_EC_CP14_LS	// CP14 LDC/STC trap
@@ -622,6 +622,28 @@
 	mov     sc_nr, #__NR_compat_syscalls
 	b	el0_svc_naked
 
+el0_cp15_32_compat:
+	/*
+	 * AArch32 CP15 MRC/MCR trap handling
+	 */
+	enable_dbg_and_irq
+	ct_user_exit
+	mov	x0, x25
+	mov	x1, sp
+	bl	do_cp15_32_instr_compat
+	b	ret_to_user
+
+el0_cp15_64_compat:
+	/*
+	 * AArch32 CP15 MRRC/MCRR trap handling
+	 */
+	enable_dbg_and_irq
+	ct_user_exit
+	mov	x0, x25
+	mov	x1, sp
+	bl	do_cp15_64_instr_compat
+	b	ret_to_user
+
 	.align	6
 el0_irq_compat:
 	kernel_entry 0, 32
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index cd53836..fcd2b33 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -606,6 +606,124 @@
 	force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
 }
 
+#ifdef CONFIG_COMPAT
+static void cntfrq_cp15_32_read_handler(unsigned int esr, struct pt_regs *regs)
+{
+	int rt =
+	  (esr & ESR_ELx_CP15_32_ISS_RT_MASK) >> ESR_ELx_CP15_32_ISS_RT_SHIFT;
+	int cv =
+	  (esr & ESR_ELx_CP15_32_ISS_CV_MASK) >> ESR_ELx_CP15_32_ISS_CV_SHIFT;
+	int cond =
+	  (esr & ESR_ELx_CP15_32_ISS_COND_MASK) >>
+		ESR_ELx_CP15_32_ISS_COND_SHIFT;
+	bool read_reg = 1;
+
+	if (rt == 13 && !compat_arm_instr_set(regs))
+		read_reg = 0;
+
+	if (cv && cond != 0xf &&
+	    !(*aarch32_opcode_cond_checks[cond])(regs->pstate & 0xffffffff))
+		read_reg = 0;
+
+	if (read_reg)
+		regs->regs[rt] = read_sysreg(cntfrq_el0);
+	regs->pc += 4;
+}
+
+struct cp15_32_hook {
+	unsigned int esr_mask;
+	unsigned int esr_val;
+	void (*handler)(unsigned int esr, struct pt_regs *regs);
+};
+
+static struct cp15_32_hook cp15_32_hooks[] = {
+	{
+		/* Trap CP15 AArch32 read access to CNTFRQ_EL0 */
+		.esr_mask = ESR_ELx_CP15_32_ISS_SYS_OP_MASK,
+		.esr_val = ESR_ELx_CP15_32_ISS_SYS_CNTFRQ,
+		.handler = cntfrq_cp15_32_read_handler,
+	},
+	{},
+};
+
+asmlinkage void __exception do_cp15_32_instr_compat(unsigned int esr,
+						    struct pt_regs *regs)
+{
+	struct cp15_32_hook *hook;
+
+	for (hook = cp15_32_hooks; hook->handler; hook++)
+		if ((hook->esr_mask & esr) == hook->esr_val) {
+			hook->handler(esr, regs);
+			return;
+		}
+
+	force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
+}
+
+static void cntvct_cp15_64_read_handler(unsigned int esr, struct pt_regs *regs)
+{
+	int rt =
+	  (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT;
+	int rt2 =
+	  (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT;
+	int cv =
+	  (esr & ESR_ELx_CP15_64_ISS_CV_MASK) >> ESR_ELx_CP15_64_ISS_CV_SHIFT;
+	int cond =
+	  (esr & ESR_ELx_CP15_64_ISS_COND_MASK) >>
+		ESR_ELx_CP15_64_ISS_COND_SHIFT;
+	bool read_reg = 1;
+
+	if (rt == 15 || rt2 == 15 || rt == rt2)
+		read_reg = 0;
+
+	if ((rt == 13 || rt2 == 13) && !compat_arm_instr_set(regs))
+		read_reg = 0;
+
+	if (cv && cond != 0xf &&
+	    !(*aarch32_opcode_cond_checks[cond])(regs->pstate & 0xffffffff))
+		read_reg = 0;
+
+	if (read_reg) {
+		u64 cval =  arch_counter_get_cntvct();
+
+		regs->regs[rt] = cval & 0xffffffff;
+		regs->regs[rt2] = cval >> 32;
+	}
+	regs->pc += 4;
+}
+
+struct cp15_64_hook {
+	unsigned int esr_mask;
+	unsigned int esr_val;
+	void (*handler)(unsigned int esr, struct pt_regs *regs);
+};
+
+static struct cp15_64_hook cp15_64_hooks[] = {
+	{
+		/* Trap CP15 AArch32 read access to CNTVCT_EL0 */
+		.esr_mask = ESR_ELx_CP15_64_ISS_SYS_OP_MASK,
+		.esr_val = ESR_ELx_CP15_64_ISS_SYS_CNTVCT,
+		.handler = cntvct_cp15_64_read_handler,
+	},
+	{},
+};
+
+asmlinkage void __exception do_cp15_64_instr_compat(unsigned int esr,
+						    struct pt_regs *regs)
+{
+	struct cp15_64_hook *hook;
+
+	for (hook = cp15_64_hooks; hook->handler; hook++)
+		if ((hook->esr_mask & esr) == hook->esr_val) {
+			hook->handler(esr, regs);
+			return;
+		}
+
+	force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0);
+}
+
+#endif
+
 long compat_arm_syscall(struct pt_regs *regs);
 
 asmlinkage long do_ni_syscall(struct pt_regs *regs)
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 45fc564..9afd9a9 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -33,6 +33,17 @@
 config REGMAP_SWR
 	tristate
 
+config AQT_REGMAP
+	depends on SND_SOC
+	bool "For regmap on AQT"
+	select REGMAP_IRQ
+	default n
+	help
+	   Say 'y' here to enable regmap_irq for aqt1000 codec.
+	   This config is intended for enabling REGMAP_IRQ for
+	   AQT1000 codec. AQT codec uses pm_runtime and calls
+	   regmap_irq as a part of init.
+
 config REGMAP_ALLOW_WRITE_DEBUGFS
 	depends on REGMAP && DEBUG_FS
 	bool "Allow REGMAP debugfs write"
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 96314f4..7f8a158 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -66,6 +66,9 @@
 #define AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME   "audio_pdr_adsprpc"
 #define AUDIO_PDR_ADSP_SERVICE_NAME              "avs/audio"
 
+#define SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME   "sensors_pdr_adsprpc"
+#define SENSORS_PDR_ADSP_SERVICE_NAME              "tms/servreg"
+
 #define RPC_TIMEOUT	(5 * HZ)
 #define BALIGN		128
 #define NUM_CHANNELS	4	/* adsp, mdsp, slpi, cdsp*/
@@ -120,7 +123,7 @@
 
 static int fastrpc_glink_open(int cid);
 static void fastrpc_glink_close(void *chan, int cid);
-static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *nb,
+static int fastrpc_pdr_notifier_cb(struct notifier_block *nb,
 					unsigned long code,
 					void *data);
 static struct dentry *debugfs_root;
@@ -230,6 +233,7 @@
 	int faults;
 	int secure;
 	int coherent;
+	int sharedcb;
 };
 
 struct fastrpc_session_ctx {
@@ -366,6 +370,7 @@
 	int pd;
 	char *spdname;
 	int file_close;
+	int sharedcb;
 	struct fastrpc_apps *apps;
 	struct hlist_head perf;
 	struct dentry *debugfs_file;
@@ -391,7 +396,13 @@
 				.spdname =
 					AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
 				.pdrnb.notifier_call =
-						fastrpc_audio_pdr_notifier_cb,
+						fastrpc_pdr_notifier_cb,
+			},
+			{
+				.spdname =
+				SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME,
+				.pdrnb.notifier_call =
+						fastrpc_pdr_notifier_cb,
 			}
 		},
 	},
@@ -582,16 +593,20 @@
 	return -ENOTTY;
 }
 
-static int dma_alloc_memory(dma_addr_t *region_phys, void **vaddr, size_t size)
+static int dma_alloc_memory(dma_addr_t *region_phys, size_t size)
 {
 	struct fastrpc_apps *me = &gfa;
+	void *vaddr = NULL;
+	unsigned long dma_attrs = 0;
 
 	if (me->dev == NULL) {
 		pr_err("device adsprpc-mem is not initialized\n");
 		return -ENODEV;
 	}
-	*vaddr = dma_alloc_coherent(me->dev, size, region_phys, GFP_KERNEL);
-	if (!*vaddr) {
+	dma_attrs |= DMA_ATTR_SKIP_ZEROING | DMA_ATTR_NO_KERNEL_MAPPING;
+	vaddr = dma_alloc_attrs(me->dev, size, region_phys, GFP_KERNEL,
+								dma_attrs);
+	if (!vaddr) {
 		pr_err("ADSPRPC: Failed to allocate %x remote heap memory\n",
 						(unsigned int)size);
 		return -ENOMEM;
@@ -665,14 +680,17 @@
 	}
 	if (map->flags == ADSP_MMAP_HEAP_ADDR ||
 				map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		unsigned long dma_attrs = 0;
 
 		if (me->dev == NULL) {
 			pr_err("failed to free remote heap allocation\n");
 			return;
 		}
 		if (map->phys) {
-			dma_free_coherent(me->dev, map->size,
-				(void *)map->va, (dma_addr_t)map->phys);
+			dma_attrs |=
+			DMA_ATTR_SKIP_ZEROING | DMA_ATTR_NO_KERNEL_MAPPING;
+			dma_free_attrs(me->dev, map->size, (void *)map->va,
+					(dma_addr_t)map->phys, dma_attrs);
 		}
 	} else if (map->flags == FASTRPC_DMAHANDLE_NOMAP) {
 		if (!IS_ERR_OR_NULL(map->handle))
@@ -729,7 +747,6 @@
 	struct fastrpc_mmap *map = NULL;
 	unsigned long attrs;
 	dma_addr_t region_phys = 0;
-	void *region_vaddr = NULL;
 	unsigned long flags;
 	int err = 0, vmid;
 
@@ -749,13 +766,12 @@
 				mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
 		map->apps = me;
 		map->fl = NULL;
-		VERIFY(err, !dma_alloc_memory(&region_phys, &region_vaddr,
-						 len));
+		VERIFY(err, !dma_alloc_memory(&region_phys, len));
 		if (err)
 			goto bail;
 		map->phys = (uintptr_t)region_phys;
 		map->size = len;
-		map->va = (uintptr_t)region_vaddr;
+		map->va = (uintptr_t)map->phys;
 	} else if (mflags == FASTRPC_DMAHANDLE_NOMAP) {
 		ion_phys_addr_t iphys;
 
@@ -1755,7 +1771,7 @@
 		if (err)
 			goto bail;
 
-		VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp.ctx & ~1)) &&
+		VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp.ctx & ~3)) &&
 			me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC));
 		if (err)
 			goto bail;
@@ -1951,7 +1967,8 @@
 	VERIFY(err, 0 == (err = fastrpc_channel_open(fl)));
 	if (err)
 		goto bail;
-	if (init->flags == FASTRPC_INIT_ATTACH) {
+	if (init->flags == FASTRPC_INIT_ATTACH ||
+			init->flags == FASTRPC_INIT_ATTACH_SENSORS) {
 		remote_arg_t ra[1];
 		int tgid = fl->tgid;
 
@@ -1963,7 +1980,12 @@
 		ioctl.fds = NULL;
 		ioctl.attrs = NULL;
 		ioctl.crc = NULL;
-		fl->pd = 0;
+		if (init->flags == FASTRPC_INIT_ATTACH)
+			fl->pd = 0;
+		else if (init->flags == FASTRPC_INIT_ATTACH_SENSORS) {
+			fl->spdname = SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME;
+			fl->pd = 2;
+		}
 		VERIFY(err, !(err = fastrpc_internal_invoke(fl,
 			FASTRPC_MODE_PARALLEL, 1, &ioctl)));
 		if (err)
@@ -2073,6 +2095,7 @@
 		if (err)
 			goto bail;
 
+		fl->pd = 1;
 		inbuf.pgid = current->tgid;
 		inbuf.namelen = init->filelen;
 		inbuf.pageslen = 0;
@@ -2536,15 +2559,17 @@
 static void fastrpc_context_list_dtor(struct fastrpc_file *fl);
 
 static int fastrpc_session_alloc_locked(struct fastrpc_channel_ctx *chan,
-			int secure, struct fastrpc_session_ctx **session)
+	int secure, int sharedcb, struct fastrpc_session_ctx **session)
 {
 	struct fastrpc_apps *me = &gfa;
 	int idx = 0, err = 0;
 
 	if (chan->sesscount) {
 		for (idx = 0; idx < chan->sesscount; ++idx) {
-			if (!chan->session[idx].used &&
-				chan->session[idx].smmu.secure == secure) {
+			if ((sharedcb && chan->session[idx].smmu.sharedcb) ||
+					(!chan->session[idx].used &&
+					chan->session[idx].smmu.secure
+					== secure && !sharedcb)) {
 				chan->session[idx].used = 1;
 				break;
 			}
@@ -2600,7 +2625,7 @@
 	if (err)
 		goto bail;
 
-	VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp->ctx & ~1)) &&
+	VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp->ctx & ~3)) &&
 		me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC));
 	if (err)
 		goto bail;
@@ -2645,7 +2670,7 @@
 
 	mutex_lock(&me->smd_mutex);
 	if (!*session)
-		err = fastrpc_session_alloc_locked(chan, secure, session);
+		err = fastrpc_session_alloc_locked(chan, secure, 0, session);
 	mutex_unlock(&me->smd_mutex);
 	return err;
 }
@@ -2663,7 +2688,7 @@
 static int fastrpc_file_free(struct fastrpc_file *fl)
 {
 	struct hlist_node *n = NULL;
-	struct fastrpc_mmap *map = NULL;
+	struct fastrpc_mmap *map = NULL, *lmap = NULL;
 	struct fastrpc_perf *perf = NULL, *fperf = NULL;
 	int cid;
 
@@ -2687,9 +2712,15 @@
 	fastrpc_context_list_dtor(fl);
 	fastrpc_buf_list_free(fl);
 	mutex_lock(&fl->fl_map_mutex);
-	hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
-		fastrpc_mmap_free(map, 1);
-	}
+	do {
+		lmap = NULL;
+		hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
+			hlist_del_init(&map->hn);
+			lmap = map;
+			break;
+		}
+		fastrpc_mmap_free(lmap, 1);
+	} while (lmap);
 	mutex_unlock(&fl->fl_map_mutex);
 	if (fl->refcount && (fl->ssrcount == fl->apps->channel[cid].ssrcount))
 		kref_put_mutex(&fl->apps->channel[cid].kref,
@@ -3085,7 +3116,7 @@
 		fl->cid = cid;
 		fl->ssrcount = fl->apps->channel[cid].ssrcount;
 		VERIFY(err, !fastrpc_session_alloc_locked(
-				&fl->apps->channel[cid], 0, &fl->sctx));
+			&fl->apps->channel[cid], 0, fl->sharedcb, &fl->sctx));
 		if (err)
 			goto bail;
 	}
@@ -3124,6 +3155,9 @@
 		} else
 			pm_qos_update_request(&fl->pm_qos_req, latency);
 		break;
+	case FASTRPC_CONTROL_SMMU:
+		fl->sharedcb = cp->smmu.sharedcb;
+		break;
 	default:
 		err = -ENOTTY;
 		break;
@@ -3364,7 +3398,7 @@
 	return NOTIFY_DONE;
 }
 
-static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *pdrnb,
+static int fastrpc_pdr_notifier_cb(struct notifier_block *pdrnb,
 					unsigned long code,
 					void *data)
 {
@@ -3401,7 +3435,7 @@
 {
 	struct fastrpc_static_pd *spd;
 	struct pd_qmi_client_data *pdr = data;
-	int curr_state = 0;
+	int curr_state = 0, i = 0;
 
 	spd = container_of(nb, struct fastrpc_static_pd, get_service_nb);
 	if (opcode == LOCATOR_DOWN) {
@@ -3409,15 +3443,21 @@
 		return NOTIFY_DONE;
 	}
 
-	if (pdr->total_domains == 1) {
-		spd->pdrhandle = service_notif_register_notifier(
-				pdr->domain_list[0].name,
-				pdr->domain_list[0].instance_id,
+	for (i = 0; i < pdr->total_domains; i++) {
+		if ((!strcmp(pdr->domain_list[i].name,
+					"msm/adsp/audio_pd")) ||
+					(!strcmp(pdr->domain_list[i].name,
+					"msm/adsp/sensor_pd"))) {
+			spd->pdrhandle =
+				service_notif_register_notifier(
+				pdr->domain_list[i].name,
+				pdr->domain_list[i].instance_id,
 				&spd->pdrnb, &curr_state);
-		if (IS_ERR(spd->pdrhandle))
-			pr_err("ADSPRPC: Unable to register notifier\n");
-	} else
-		pr_err("ADSPRPC: Service returned invalid domains\n");
+			if (IS_ERR(spd->pdrhandle))
+				pr_err("ADSPRPC: Unable to register notifier\n");
+			break;
+		}
+	}
 
 	return NOTIFY_DONE;
 }
@@ -3476,6 +3516,8 @@
 	sess->used = 0;
 	sess->smmu.coherent = of_property_read_bool(dev->of_node,
 						"dma-coherent");
+	sess->smmu.sharedcb = of_property_read_bool(dev->of_node,
+						"shared-cb");
 	sess->smmu.secure = of_property_read_bool(dev->of_node,
 						"qcom,secure-context-bank");
 	if (sess->smmu.secure)
@@ -3715,6 +3757,24 @@
 			pr_err("ADSPRPC: Get service location failed: %d\n",
 								ret);
 	}
+	if (of_property_read_bool(dev->of_node,
+					"qcom,fastrpc-adsp-sensors-pdr")) {
+		int session;
+
+		VERIFY(err, !fastrpc_get_adsp_session(
+			SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+		if (err)
+			goto spdbail;
+		me->channel[0].spd[session].get_service_nb.notifier_call =
+					fastrpc_get_service_location_notify;
+		ret = get_service_location(
+				SENSORS_PDR_SERVICE_LOCATION_CLIENT_NAME,
+				SENSORS_PDR_ADSP_SERVICE_NAME,
+				&me->channel[0].spd[session].get_service_nb);
+		if (ret)
+			pr_err("ADSPRPC: Get service location failed: %d\n",
+								ret);
+	}
 spdbail:
 	err = 0;
 	VERIFY(err, !of_platform_populate(pdev->dev.of_node,
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index bb7b654..de0dd01 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -66,6 +66,7 @@
 #define FASTRPC_INIT_ATTACH      0
 #define FASTRPC_INIT_CREATE      1
 #define FASTRPC_INIT_CREATE_STATIC  2
+#define FASTRPC_INIT_ATTACH_SENSORS 3
 
 /* Retrives number of input buffers from the scalars parameter */
 #define REMOTE_SCALARS_INBUFS(sc)        (((sc) >> 16) & 0x0ff)
@@ -229,11 +230,15 @@
 	uint32_t enable;	//!latency control enable
 	uint32_t level;		//!level of control
 };
-
+#define FASTRPC_CONTROL_SMMU   (2)
+struct fastrpc_ctrl_smmu {
+	uint32_t sharedcb;
+};
 struct fastrpc_ioctl_control {
 	uint32_t req;
 	union {
 		struct fastrpc_ctrl_latency lp;
+		struct fastrpc_ctrl_smmu smmu;
 	};
 };
 
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index a469eb9..286418f 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -26,6 +26,7 @@
 #include <linux/reboot.h>
 #include <asm/current.h>
 #include <soc/qcom/restart.h>
+#include <linux/vmalloc.h>
 #ifdef CONFIG_DIAG_OVER_USB
 #include <linux/usb/usbdiag.h>
 #endif
@@ -259,7 +260,7 @@
 	switch (type) {
 	case DCI_BUF_PRIMARY:
 		buffer->capacity = IN_BUF_SIZE;
-		buffer->data = kzalloc(buffer->capacity, GFP_KERNEL);
+		buffer->data = vzalloc(buffer->capacity);
 		if (!buffer->data)
 			return -ENOMEM;
 		break;
@@ -269,7 +270,7 @@
 		break;
 	case DCI_BUF_CMD:
 		buffer->capacity = DIAG_MAX_REQ_SIZE + DCI_BUF_SIZE;
-		buffer->data = kzalloc(buffer->capacity, GFP_KERNEL);
+		buffer->data = vzalloc(buffer->capacity);
 		if (!buffer->data)
 			return -ENOMEM;
 		break;
@@ -687,7 +688,7 @@
 	byte_mask = 0x01 << (item_num % 8);
 	offset = equip_id * 514;
 
-	if (offset + byte_index > DCI_LOG_MASK_SIZE) {
+	if (offset + byte_index >= DCI_LOG_MASK_SIZE) {
 		pr_err("diag: In %s, invalid offset: %d, log_code: %d, byte_index: %d\n",
 				__func__, offset, log_code, byte_index);
 		return 0;
@@ -714,7 +715,7 @@
 	bit_index = event_id % 8;
 	byte_mask = 0x1 << bit_index;
 
-	if (byte_index > DCI_EVENT_MASK_SIZE) {
+	if (byte_index >= DCI_EVENT_MASK_SIZE) {
 		pr_err("diag: In %s, invalid, event_id: %d, byte_index: %d\n",
 				__func__, event_id, byte_index);
 		return 0;
@@ -2726,7 +2727,7 @@
 		create_dci_event_mask_tbl(temp->event_mask_composite);
 	}
 
-	partial_pkt.data = kzalloc(MAX_DCI_PACKET_SZ, GFP_KERNEL);
+	partial_pkt.data = vzalloc(MAX_DCI_PACKET_SZ);
 	if (!partial_pkt.data)
 		return -ENOMEM;
 
@@ -2780,7 +2781,7 @@
 		goto err;
 
 	if (driver->apps_dci_buf == NULL) {
-		driver->apps_dci_buf = kzalloc(DCI_BUF_SIZE, GFP_KERNEL);
+		driver->apps_dci_buf = vzalloc(DCI_BUF_SIZE);
 		if (driver->apps_dci_buf == NULL)
 			goto err;
 	}
@@ -2797,12 +2798,12 @@
 	return DIAG_DCI_NO_ERROR;
 err:
 	pr_err("diag: Could not initialize diag DCI buffers");
-	kfree(driver->apps_dci_buf);
+	vfree(driver->apps_dci_buf);
 	driver->apps_dci_buf = NULL;
 
 	if (driver->diag_dci_wq)
 		destroy_workqueue(driver->diag_dci_wq);
-	kfree(partial_pkt.data);
+	vfree(partial_pkt.data);
 	partial_pkt.data = NULL;
 	mutex_destroy(&driver->dci_mutex);
 	mutex_destroy(&dci_log_mask_mutex);
@@ -2822,9 +2823,9 @@
 
 void diag_dci_exit(void)
 {
-	kfree(partial_pkt.data);
+	vfree(partial_pkt.data);
 	partial_pkt.data = NULL;
-	kfree(driver->apps_dci_buf);
+	vfree(driver->apps_dci_buf);
 	driver->apps_dci_buf = NULL;
 	mutex_destroy(&driver->dci_mutex);
 	mutex_destroy(&dci_log_mask_mutex);
@@ -2962,7 +2963,7 @@
 	new_entry->in_service = 0;
 	INIT_LIST_HEAD(&new_entry->list_write_buf);
 	mutex_init(&new_entry->write_buf_mutex);
-	new_entry->dci_log_mask =  kzalloc(DCI_LOG_MASK_SIZE, GFP_KERNEL);
+	new_entry->dci_log_mask =  vzalloc(DCI_LOG_MASK_SIZE);
 	if (!new_entry->dci_log_mask) {
 		pr_err("diag: Unable to create log mask for client, %d",
 							driver->dci_client_id);
@@ -2970,14 +2971,14 @@
 	}
 	create_dci_log_mask_tbl(new_entry->dci_log_mask, DCI_LOG_MASK_CLEAN);
 
-	new_entry->dci_event_mask =  kzalloc(DCI_EVENT_MASK_SIZE, GFP_KERNEL);
+	new_entry->dci_event_mask =  vzalloc(DCI_EVENT_MASK_SIZE);
 	if (!new_entry->dci_event_mask)
 		goto fail_alloc;
 	create_dci_event_mask_tbl(new_entry->dci_event_mask);
 
 	new_entry->buffers = kzalloc(new_entry->num_buffers *
 				     sizeof(struct diag_dci_buf_peripheral_t),
-				     GFP_KERNEL);
+					GFP_KERNEL);
 	if (!new_entry->buffers) {
 		pr_err("diag: Unable to allocate buffers for peripherals in %s\n",
 								__func__);
@@ -3001,7 +3002,7 @@
 		if (!proc_buf->buf_primary)
 			goto fail_alloc;
 		proc_buf->buf_cmd = kzalloc(sizeof(struct diag_dci_buffer_t),
-					    GFP_KERNEL);
+					GFP_KERNEL);
 		if (!proc_buf->buf_cmd)
 			goto fail_alloc;
 		err = diag_dci_init_buffer(proc_buf->buf_primary,
@@ -3034,7 +3035,7 @@
 			if (proc_buf) {
 				mutex_destroy(&proc_buf->health_mutex);
 				if (proc_buf->buf_primary) {
-					kfree(proc_buf->buf_primary->data);
+					vfree(proc_buf->buf_primary->data);
 					proc_buf->buf_primary->data = NULL;
 					mutex_destroy(
 					   &proc_buf->buf_primary->data_mutex);
@@ -3042,7 +3043,7 @@
 				kfree(proc_buf->buf_primary);
 				proc_buf->buf_primary = NULL;
 				if (proc_buf->buf_cmd) {
-					kfree(proc_buf->buf_cmd->data);
+					vfree(proc_buf->buf_cmd->data);
 					proc_buf->buf_cmd->data = NULL;
 					mutex_destroy(
 					   &proc_buf->buf_cmd->data_mutex);
@@ -3051,9 +3052,9 @@
 				proc_buf->buf_cmd = NULL;
 			}
 		}
-		kfree(new_entry->dci_event_mask);
+		vfree(new_entry->dci_event_mask);
 		new_entry->dci_event_mask = NULL;
-		kfree(new_entry->dci_log_mask);
+		vfree(new_entry->dci_log_mask);
 		new_entry->dci_log_mask = NULL;
 		kfree(new_entry->buffers);
 		new_entry->buffers = NULL;
@@ -3088,7 +3089,7 @@
 	 * Clear the client's log and event masks, update the cumulative
 	 * masks and send the masks to peripherals
 	 */
-	kfree(entry->dci_log_mask);
+	vfree(entry->dci_log_mask);
 	entry->dci_log_mask = NULL;
 	diag_dci_invalidate_cumulative_log_mask(token);
 	if (token == DCI_LOCAL_PROC)
@@ -3096,7 +3097,7 @@
 	ret = dci_ops_tbl[token].send_log_mask(token);
 	if (ret != DIAG_DCI_NO_ERROR)
 		return ret;
-	kfree(entry->dci_event_mask);
+	vfree(entry->dci_event_mask);
 	entry->dci_event_mask = NULL;
 	diag_dci_invalidate_cumulative_event_mask(token);
 	if (token == DCI_LOCAL_PROC)
@@ -3159,12 +3160,12 @@
 		}
 
 		mutex_lock(&proc_buf->buf_primary->data_mutex);
-		kfree(proc_buf->buf_primary->data);
+		vfree(proc_buf->buf_primary->data);
 		proc_buf->buf_primary->data = NULL;
 		mutex_unlock(&proc_buf->buf_primary->data_mutex);
 
 		mutex_lock(&proc_buf->buf_cmd->data_mutex);
-		kfree(proc_buf->buf_cmd->data);
+		vfree(proc_buf->buf_cmd->data);
 		proc_buf->buf_cmd->data = NULL;
 		mutex_unlock(&proc_buf->buf_cmd->data_mutex);
 
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index ba321b0..0d3389a 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1715,8 +1715,8 @@
 static int diag_switch_logging(struct diag_logging_mode_param_t *param)
 {
 	int new_mode, i = 0;
-	int curr_mode, err = 0;
-	uint8_t do_switch = 1, peripheral = 0;
+	int curr_mode, err = 0, peripheral = 0;
+	uint8_t do_switch = 1;
 	uint32_t peripheral_mask = 0, pd_mask = 0;
 
 	if (!param)
@@ -1730,6 +1730,10 @@
 
 	if (param->pd_mask) {
 		pd_mask = diag_translate_mask(param->pd_mask);
+		param->diag_id = 0;
+		param->pd_val = 0;
+		param->peripheral = -EINVAL;
+
 		for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
 			if (pd_mask & (1 << i)) {
 				if (diag_search_diagid_by_pd(i, &param->diag_id,
@@ -1739,6 +1743,12 @@
 				}
 			}
 		}
+
+		DIAG_LOG(DIAG_DEBUG_USERSPACE,
+			"diag: pd_mask = %d, diag_id = %d, peripheral = %d, pd_val = %d\n",
+			param->pd_mask, param->diag_id,
+			param->peripheral, param->pd_val);
+
 		if (!param->diag_id ||
 			(param->pd_val < UPD_WLAN) ||
 			(param->pd_val >= NUM_MD_SESSIONS)) {
@@ -1748,22 +1758,26 @@
 			return -EINVAL;
 		}
 
-		DIAG_LOG(DIAG_DEBUG_USERSPACE,
-			"diag: pd_mask = %d, diag_id = %d, peripheral = %d, pd_val = %d\n",
-			param->pd_mask, param->diag_id,
-			param->peripheral, param->pd_val);
-
 		peripheral = param->peripheral;
+		if ((peripheral < PERIPHERAL_MODEM) ||
+			(peripheral >= NUM_PERIPHERALS)) {
+			DIAG_LOG(DIAG_DEBUG_USERSPACE,
+			"Invalid peripheral: %d\n", peripheral);
+			return -EINVAL;
+		}
 		i = param->pd_val - UPD_WLAN;
+		mutex_lock(&driver->md_session_lock);
 		if (driver->md_session_map[peripheral] &&
 			(MD_PERIPHERAL_MASK(peripheral) &
 			diag_mux->mux_mask) &&
 			!driver->pd_session_clear[i]) {
 			DIAG_LOG(DIAG_DEBUG_USERSPACE,
 			"diag_fr: User PD is already logging onto active peripheral logging\n");
+			mutex_unlock(&driver->md_session_lock);
 			driver->pd_session_clear[i] = 0;
 			return -EINVAL;
 		}
+		mutex_unlock(&driver->md_session_lock);
 		peripheral_mask =
 			diag_translate_mask(param->pd_mask);
 		param->peripheral_mask = peripheral_mask;
diff --git a/drivers/clk/msm/clock-cpu-8939.c b/drivers/clk/msm/clock-cpu-8939.c
index b07a0a8..dd1745a 100644
--- a/drivers/clk/msm/clock-cpu-8939.c
+++ b/drivers/clk/msm/clock-cpu-8939.c
@@ -489,6 +489,11 @@
 	struct dev_pm_opp *oppl;
 	int j = 1;
 
+	if (!cpudev) {
+		pr_warn("clock-cpu: NULL CPU device\n");
+		return -ENODEV;
+	}
+
 	rcu_read_lock();
 	/* Check if the regulator driver has already populated OPP tables */
 	oppl = dev_pm_opp_find_freq_exact(vregdev, 2, true);
diff --git a/drivers/clk/msm/clock-cpu-8953.c b/drivers/clk/msm/clock-cpu-8953.c
index 1b643cf..4ba2543 100644
--- a/drivers/clk/msm/clock-cpu-8953.c
+++ b/drivers/clk/msm/clock-cpu-8953.c
@@ -499,6 +499,11 @@
 	bool first = true;
 	int j = 1;
 
+	if (!dev) {
+		pr_warn("clock-cpu: NULL CPU device\n");
+		return -ENODEV;
+	}
+
 	while (1) {
 		rate = c->fmax[j++];
 		level = find_vdd_level(c, rate);
diff --git a/drivers/clk/msm/clock-cpu-sdm632.c b/drivers/clk/msm/clock-cpu-sdm632.c
index ee83e1a..58a2520 100644
--- a/drivers/clk/msm/clock-cpu-sdm632.c
+++ b/drivers/clk/msm/clock-cpu-sdm632.c
@@ -686,6 +686,11 @@
 	long ret, uv, corner;
 	int j = 1;
 
+	if (!cpudev) {
+		pr_warn("clock-cpu: NULL CPU device\n");
+		return -ENODEV;
+	}
+
 	while (1) {
 		rate = c->fmax[j++];
 
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index 61c0ae8..ad0be45 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -178,8 +178,8 @@
 	struct kernel_param kp;
 	struct lpm_level_avail *avail = get_avail_ptr(kobj, attr);
 
-	if (!avail)
-		pr_info("Error\n");
+	if (WARN_ON(!avail))
+		return -EINVAL;
 
 	kp.arg = &avail->latency_us;
 
@@ -197,8 +197,15 @@
 {
 	int ret = 0;
 	struct kernel_param kp;
+	struct lpm_level_avail *avail = get_avail_ptr(kobj, attr);
 
-	kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr));
+	if (WARN_ON(!avail))
+		return -EINVAL;
+
+	kp.arg = get_enabled_ptr(attr, avail);
+	if (WARN_ON(!kp.arg))
+		return -EINVAL;
+
 	ret = param_get_bool(buf, &kp);
 	if (ret > 0) {
 		strlcat(buf, "\n", PAGE_SIZE);
@@ -453,6 +460,17 @@
 	if (ret)
 		goto fail;
 
+	key = "qcom,disable-prediction";
+	c->lpm_prediction = !(of_property_read_bool(node, key));
+
+	if (c->lpm_prediction) {
+		key = "qcom,clstr-tmr-add";
+		ret = of_property_read_u32(node, key, &c->tmr_add);
+		if (ret || c->tmr_add < TIMER_ADD_LOW ||
+					c->tmr_add > TIMER_ADD_HIGH)
+			c->tmr_add = DEFAULT_TIMER_ADD;
+	}
+
 	/* Set default_level to 0 as default */
 	c->default_level = 0;
 
@@ -713,8 +731,28 @@
 	if (ret)
 		goto failed_parse_params;
 
-	key = "qcom,use-prediction";
-	cpu->lpm_prediction = of_property_read_bool(node, key);
+	key = "qcom,disable-prediction";
+	cpu->lpm_prediction = !(of_property_read_bool(node, key));
+
+	if (cpu->lpm_prediction) {
+		key = "qcom,ref-stddev";
+		ret = of_property_read_u32(node, key, &cpu->ref_stddev);
+		if (ret || cpu->ref_stddev < STDDEV_LOW ||
+					cpu->ref_stddev > STDDEV_HIGH)
+			cpu->ref_stddev = DEFAULT_STDDEV;
+
+		key = "qcom,tmr-add";
+		ret = of_property_read_u32(node, key, &cpu->tmr_add);
+		if (ret || cpu->tmr_add < TIMER_ADD_LOW ||
+					cpu->tmr_add > TIMER_ADD_HIGH)
+			cpu->tmr_add = DEFAULT_TIMER_ADD;
+
+		key = "qcom,ref-premature-cnt";
+		ret = of_property_read_u32(node, key, &cpu->ref_premature_cnt);
+		if (ret || cpu->ref_premature_cnt < PREMATURE_CNT_LOW ||
+				cpu->ref_premature_cnt > PREMATURE_CNT_HIGH)
+			cpu->ref_premature_cnt = DEFAULT_PREMATURE_CNT;
+	}
 
 	key = "parse_cpu";
 	ret = parse_cpu(node, cpu);
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 1db0c74..9ae640d 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -92,15 +92,6 @@
 static bool lpm_prediction = true;
 module_param_named(lpm_prediction, lpm_prediction, bool, 0664);
 
-static uint32_t ref_stddev = 500;
-module_param_named(ref_stddev, ref_stddev, uint, 0664);
-
-static uint32_t tmr_add = 1000;
-module_param_named(tmr_add, tmr_add, uint, 0664);
-
-static uint32_t ref_premature_cnt = 1;
-module_param_named(ref_premature_cnt, ref_premature_cnt, uint, 0664);
-
 static uint32_t bias_hyst;
 module_param_named(bias_hyst, bias_hyst, uint, 0664);
 
@@ -119,7 +110,7 @@
 static DEFINE_PER_CPU(struct lpm_cpu*, cpu_lpm);
 static bool suspend_in_progress;
 static struct hrtimer lpm_hrtimer;
-static struct hrtimer histtimer;
+static DEFINE_PER_CPU(struct hrtimer, histtimer);
 static struct lpm_debug *lpm_debug;
 static phys_addr_t lpm_debug_phys;
 static const int num_dbg_elements = 0x100;
@@ -348,7 +339,10 @@
 
 static void histtimer_cancel(void)
 {
-	hrtimer_try_to_cancel(&histtimer);
+	unsigned int cpu = raw_smp_processor_id();
+	struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu);
+
+	hrtimer_try_to_cancel(cpu_histtimer);
 }
 
 static enum hrtimer_restart histtimer_fn(struct hrtimer *h)
@@ -364,9 +358,11 @@
 {
 	uint64_t time_ns = time_us * NSEC_PER_USEC;
 	ktime_t hist_ktime = ns_to_ktime(time_ns);
+	unsigned int cpu = raw_smp_processor_id();
+	struct hrtimer *cpu_histtimer = &per_cpu(histtimer, cpu);
 
-	histtimer.function = histtimer_fn;
-	hrtimer_start(&histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED);
+	cpu_histtimer->function = histtimer_fn;
+	hrtimer_start(cpu_histtimer, hist_ktime, HRTIMER_MODE_REL_PINNED);
 }
 
 static void cluster_timer_init(struct lpm_cluster *cluster)
@@ -496,7 +492,7 @@
 	 * ignore one maximum sample and retry
 	 */
 	if (((avg > stddev * 6) && (divisor >= (MAXSAMPLES - 1)))
-					|| stddev <= ref_stddev) {
+					|| stddev <= cpu->ref_stddev) {
 		history->stime = ktime_to_us(ktime_get()) + avg;
 		return avg;
 	} else if (divisor  > (MAXSAMPLES - 1)) {
@@ -521,7 +517,7 @@
 					total += history->resi[i];
 				}
 			}
-			if (failed >= ref_premature_cnt) {
+			if (failed >= cpu->ref_premature_cnt) {
 				*idx_restrict = j;
 				do_div(total, failed);
 				for (i = 0; i < j; i++) {
@@ -545,8 +541,9 @@
 static inline void invalidate_predict_history(struct cpuidle_device *dev)
 {
 	struct lpm_history *history = &per_cpu(hist, dev->cpu);
+	struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, dev->cpu);
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !lpm_cpu->lpm_prediction)
 		return;
 
 	if (history->hinvalid) {
@@ -561,8 +558,9 @@
 	struct lpm_history *history;
 	int i;
 	unsigned int cpu;
+	struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, raw_smp_processor_id());
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !lpm_cpu->lpm_prediction)
 		return;
 
 	for_each_possible_cpu(cpu) {
@@ -681,8 +679,8 @@
 	if ((predicted || (idx_restrict != (cpu->nlevels + 1)))
 			&& ((best_level >= 0)
 			&& (best_level < (cpu->nlevels-1)))) {
-		htime = predicted + tmr_add;
-		if (htime == tmr_add)
+		htime = predicted + cpu->tmr_add;
+		if (htime == cpu->tmr_add)
 			htime = idx_restrict_time;
 		else if (htime > max_residency[best_level])
 			htime = max_residency[best_level];
@@ -746,14 +744,14 @@
 			next_event.tv64 = next_event_c->tv64;
 		}
 
-		if (from_idle && lpm_prediction) {
+		if (from_idle && lpm_prediction && cluster->lpm_prediction) {
 			history = &per_cpu(hist, cpu);
 			if (history->stime && (history->stime < prediction))
 				prediction = history->stime;
 		}
 	}
 
-	if (from_idle && lpm_prediction) {
+	if (from_idle && lpm_prediction && cluster->lpm_prediction) {
 		if (prediction > ktime_to_us(ktime_get()))
 			*pred_time = prediction - ktime_to_us(ktime_get());
 	}
@@ -772,7 +770,7 @@
 	struct cluster_history *history = &cluster->history;
 	int64_t cur_time = ktime_to_us(ktime_get());
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !cluster->lpm_prediction)
 		return 0;
 
 	if (history->hinvalid) {
@@ -847,7 +845,7 @@
 	struct lpm_cluster *cluster =
 			container_of(history, struct lpm_cluster, history);
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !cluster->lpm_prediction)
 		return;
 
 	if ((history->entry_idx == -1) || (history->entry_idx == idx)) {
@@ -908,7 +906,7 @@
 	struct lpm_cluster *cluster = lpm_root_node;
 	struct list_head *list;
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !cluster->lpm_prediction)
 		return;
 
 	clear_cl_history_each(&cluster->history);
@@ -1034,7 +1032,7 @@
 			cluster->child_cpus.bits[0], from_idle);
 		lpm_stats_cluster_enter(cluster->stats, idx);
 
-		if (from_idle && lpm_prediction)
+		if (from_idle && lpm_prediction && cluster->lpm_prediction)
 			update_cluster_history_time(&cluster->history, idx,
 						ktime_to_us(ktime_get()));
 	}
@@ -1066,7 +1064,8 @@
 	if (predicted && (idx < (cluster->nlevels - 1))) {
 		struct power_params *pwr_params = &cluster->levels[idx].pwr;
 
-		clusttimer_start(cluster, pwr_params->max_residency + tmr_add);
+		clusttimer_start(cluster, pwr_params->max_residency +
+							cluster->tmr_add);
 	}
 
 	return 0;
@@ -1120,7 +1119,8 @@
 						&cluster->levels[0].pwr;
 
 			clusttimer_start(cluster,
-					pwr_params->max_residency + tmr_add);
+					pwr_params->max_residency +
+					cluster->tmr_add);
 
 			goto failed;
 		}
@@ -1335,8 +1335,9 @@
 {
 	struct lpm_history *history = &per_cpu(hist, dev->cpu);
 	uint32_t tmr = 0;
+	struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, dev->cpu);
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !lpm_cpu->lpm_prediction)
 		return;
 
 	if (history->htmr_wkup) {
@@ -1394,7 +1395,7 @@
 	update_history(dev, idx);
 	trace_cpu_idle_exit(idx, success);
 	local_irq_enable();
-	if (lpm_prediction) {
+	if (lpm_prediction && cpu->lpm_prediction) {
 		histtimer_cancel();
 		clusttimer_cancel();
 	}
@@ -1671,6 +1672,8 @@
 {
 	int ret;
 	int size;
+	unsigned int cpu;
+	struct hrtimer *cpu_histtimer;
 	struct kobject *module_kobj = NULL;
 	struct md_region md_entry;
 
@@ -1695,7 +1698,11 @@
 	suspend_set_ops(&lpm_suspend_ops);
 	freeze_set_ops(&lpm_freeze_ops);
 	hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	hrtimer_init(&histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	for_each_possible_cpu(cpu) {
+		cpu_histtimer = &per_cpu(histtimer, cpu);
+		hrtimer_init(cpu_histtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	}
+
 	cluster_timer_init(lpm_root_node);
 
 	size = num_dbg_elements * sizeof(struct lpm_debug);
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index 1ff69c8..0b598c0 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,15 @@
 #define NR_LPM_LEVELS 8
 #define MAXSAMPLES 5
 #define CLUST_SMPL_INVLD_TIME 40000
+#define DEFAULT_PREMATURE_CNT 3
+#define DEFAULT_STDDEV 100
+#define DEFAULT_TIMER_ADD 100
+#define TIMER_ADD_LOW 100
+#define TIMER_ADD_HIGH 1500
+#define STDDEV_LOW 100
+#define STDDEV_HIGH 1000
+#define PREMATURE_CNT_LOW 1
+#define PREMATURE_CNT_HIGH 5
 
 struct power_params {
 	uint32_t latency_us;		/* Enter + Exit latency */
@@ -43,6 +52,9 @@
 	int nlevels;
 	unsigned int psci_mode_shift;
 	unsigned int psci_mode_mask;
+	uint32_t ref_stddev;
+	uint32_t ref_premature_cnt;
+	uint32_t tmr_add;
 	bool lpm_prediction;
 	struct cpuidle_driver *drv;
 	struct lpm_cluster *parent;
@@ -97,6 +109,8 @@
 	int min_child_level;
 	int default_level;
 	int last_level;
+	uint32_t tmr_add;
+	bool lpm_prediction;
 	struct list_head cpu;
 	spinlock_t sync_lock;
 	struct cpumask child_cpus;
diff --git a/drivers/crypto/msm/Makefile b/drivers/crypto/msm/Makefile
index 9ecb646..b712fc1 100644
--- a/drivers/crypto/msm/Makefile
+++ b/drivers/crypto/msm/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_CRYPTO_DEV_QCOM_MSM_QCE) += qce50.o
 obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev.o
+obj-$(CONFIG_CRYPTO_DEV_QCEDEV) += qcedev_smmu.o
 obj-$(CONFIG_CRYPTO_DEV_QCRYPTO) += qcrypto.o
 obj-$(CONFIG_CRYPTO_DEV_OTA_CRYPTO) += ota_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_QCOM_ICE) += ice.o
diff --git a/drivers/crypto/msm/compat_qcedev.c b/drivers/crypto/msm/compat_qcedev.c
index d61b6f3..41b13a7d 100644
--- a/drivers/crypto/msm/compat_qcedev.c
+++ b/drivers/crypto/msm/compat_qcedev.c
@@ -1,7 +1,7 @@
 /*
  * QTI CE 32-bit compatibility syscall for 64-bit systems
  *
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -254,6 +254,75 @@
 	return err;
 }
 
+static int compat_xfer_qcedev_map_buf_req(
+			struct compat_qcedev_map_buf_req __user *data32,
+			struct qcedev_map_buf_req __user *data, bool to_get)
+{
+	int rc = 0, i = 0, fd = -1;
+	uint32_t fd_size, fd_offset, num_fds, buf_vaddr;
+
+	if (to_get) {
+		/* copy from compat struct */
+		for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+			rc |= get_user(fd, &data32->fd[i]);
+			rc |= put_user(fd, &data->fd[i]);
+			rc |= get_user(fd_size, &data32->fd_size[i]);
+			rc |= put_user(fd_size, &data->fd_size[i]);
+			rc |= get_user(fd_offset, &data32->fd_offset[i]);
+			rc |= put_user(fd_offset, &data->fd_offset[i]);
+			rc |= get_user(buf_vaddr, &data32->buf_vaddr[i]);
+			rc |= put_user(buf_vaddr, &data->buf_vaddr[i]);
+		}
+
+		rc |= get_user(num_fds, &data32->num_fds);
+		rc |= put_user(num_fds, &data->num_fds);
+	} else {
+		/* copy to compat struct */
+		for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+			rc |= get_user(fd, &data->fd[i]);
+			rc |= put_user(fd, &data32->fd[i]);
+			rc |= get_user(fd_size, &data->fd_size[i]);
+			rc |= put_user(fd_size, &data32->fd_size[i]);
+			rc |= get_user(fd_offset, &data->fd_offset[i]);
+			rc |= put_user(fd_offset, &data32->fd_offset[i]);
+			rc |= get_user(buf_vaddr, &data->buf_vaddr[i]);
+			rc |= put_user(buf_vaddr, &data32->buf_vaddr[i]);
+		}
+		rc |= get_user(num_fds, &data->num_fds);
+		rc |= put_user(num_fds, &data32->num_fds);
+	}
+
+	return rc;
+}
+
+static int compat_xfer_qcedev_unmap_buf_req(
+			struct compat_qcedev_unmap_buf_req __user *data32,
+			struct qcedev_unmap_buf_req __user *data, bool to_get)
+{
+	int i = 0, rc = 0, fd = -1;
+	uint32_t num_fds;
+
+	if (to_get) {
+		/* copy from compat struct */
+		for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+			rc |= get_user(fd, &data32->fd[i]);
+			rc |= put_user(fd, &data->fd[i]);
+		}
+		rc |= get_user(num_fds, &data32->num_fds);
+		rc |= put_user(num_fds, &data->num_fds);
+	} else {
+		/* copy to compat struct */
+		for (i = 0; i < QCEDEV_MAX_BUFFERS; i++) {
+			rc |= get_user(fd, &data->fd[i]);
+			rc |= put_user(fd, &data32->fd[i]);
+		}
+		rc |= get_user(num_fds, &data->num_fds);
+		rc |= put_user(num_fds, &data32->num_fds);
+	}
+	return rc;
+}
+
+
 static int compat_get_qcedev_sha_op_req(
 		struct compat_qcedev_sha_op_req __user *data32,
 		struct qcedev_sha_op_req __user *data)
@@ -359,6 +428,10 @@
 		return QCEDEV_IOCTL_GET_SHA_REQ;
 	case COMPAT_QCEDEV_IOCTL_GET_CMAC_REQ:
 		return QCEDEV_IOCTL_GET_CMAC_REQ;
+	case COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ:
+		return QCEDEV_IOCTL_MAP_BUF_REQ;
+	case COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ:
+		return QCEDEV_IOCTL_UNMAP_BUF_REQ;
 	default:
 		return cmd;
 	}
@@ -412,6 +485,46 @@
 		err = compat_put_qcedev_sha_op_req(data32, data);
 		return ret ? ret : err;
 	}
+	case COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ: {
+		struct compat_qcedev_map_buf_req __user *data32;
+		struct qcedev_map_buf_req __user *data;
+		int err;
+
+		data32 = compat_ptr(arg);
+		data = compat_alloc_user_space(sizeof(*data));
+		if (!data)
+			return -EINVAL;
+
+		err = compat_xfer_qcedev_map_buf_req(data32, data, true);
+		if (err)
+			return err;
+
+		ret = qcedev_ioctl(file, convert_cmd(cmd), (unsigned long)data);
+		err = compat_xfer_qcedev_map_buf_req(data32, data, false);
+		return ret ? ret : err;
+
+		break;
+	}
+	case COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ: {
+		struct compat_qcedev_unmap_buf_req __user *data32;
+		struct qcedev_unmap_buf_req __user *data;
+		int err;
+
+		data32 = compat_ptr(arg);
+		data = compat_alloc_user_space(sizeof(*data));
+		if (!data)
+			return -EINVAL;
+
+		err = compat_xfer_qcedev_unmap_buf_req(data32, data, true);
+		if (err)
+			return err;
+
+		ret = qcedev_ioctl(file, convert_cmd(cmd), (unsigned long)data);
+		err = compat_xfer_qcedev_unmap_buf_req(data32, data, false);
+		return ret ? ret : err;
+
+		break;
+	}
 	default:
 		return -ENOIOCTLCMD;
 	}
diff --git a/drivers/crypto/msm/compat_qcedev.h b/drivers/crypto/msm/compat_qcedev.h
index 6c041cb..4cefd0a 100644
--- a/drivers/crypto/msm/compat_qcedev.h
+++ b/drivers/crypto/msm/compat_qcedev.h
@@ -151,6 +151,33 @@
 	enum qcedev_sha_alg_enum		alg;
 };
 
+/**
+ * struct compact_qcedev_map_buf_req - Holds the mapping request information
+ * fd (IN):            Array of fds.
+ * num_fds (IN):       Number of fds in fd[].
+ * fd_size (IN):       Array of sizes corresponding to each fd in fd[].
+ * fd_offset (IN):     Array of offset corresponding to each fd in fd[].
+ * vaddr (OUT):        Array of mapped virtual address corresponding to
+ *                     each fd in fd[].
+ */
+struct compat_qcedev_map_buf_req {
+	compat_long_t	fd[QCEDEV_MAX_BUFFERS];
+	compat_ulong_t	num_fds;
+	compat_ulong_t	fd_size[QCEDEV_MAX_BUFFERS];
+	compat_ulong_t	fd_offset[QCEDEV_MAX_BUFFERS];
+	compat_u64      buf_vaddr[QCEDEV_MAX_BUFFERS];
+};
+
+/**
+ * struct compat_qcedev_unmap_buf_req - Holds the hashing request information
+ * fd (IN):	       Array of fds to unmap
+ * num_fds (IN):       Number of fds in fd[].
+ */
+struct	compat_qcedev_unmap_buf_req {
+	compat_long_t	fd[QCEDEV_MAX_BUFFERS];
+	compat_ulong_t	num_fds;
+};
+
 struct file;
 extern long compat_qcedev_ioctl(struct file *file,
 			unsigned int cmd, unsigned long arg);
@@ -173,6 +200,9 @@
 	_IO(QCEDEV_IOC_MAGIC, 8)
 #define COMPAT_QCEDEV_IOCTL_GET_CMAC_REQ	\
 	_IOWR(QCEDEV_IOC_MAGIC, 9, struct compat_qcedev_sha_op_req)
-
+#define COMPAT_QCEDEV_IOCTL_MAP_BUF_REQ	\
+	_IOWR(QCEDEV_IOC_MAGIC, 10, struct compat_qcedev_map_buf_req)
+#define COMPAT_QCEDEV_IOCTL_UNMAP_BUF_REQ \
+	_IOWR(QCEDEV_IOC_MAGIC, 11, struct compat_qcedev_unmap_buf_req)
 #endif /* CONFIG_COMPAT */
 #endif /* _UAPI_COMPAT_QCEDEV__H */
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 958fb91..c8d1158 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -1,7 +1,7 @@
 /*
  * QTI CE device driver.
  *
- * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -35,6 +35,7 @@
 #include <crypto/hash.h>
 #include "qcedevi.h"
 #include "qce.h"
+#include "qcedev_smmu.h"
 
 #include <linux/compat.h>
 #include "compat_qcedev.h"
@@ -59,6 +60,14 @@
 static DEFINE_MUTEX(qcedev_sent_bw_req);
 static DEFINE_MUTEX(hash_access_lock);
 
+MODULE_DEVICE_TABLE(of, qcedev_match);
+
+static const struct of_device_id qcedev_match[] = {
+	{	.compatible = "qcom,qcedev"},
+	{	.compatible = "qcom,qcedev,context-bank"},
+	{}
+};
+
 static int qcedev_control_clocks(struct qcedev_control *podev, bool enable)
 {
 	unsigned int control_flag;
@@ -259,6 +268,9 @@
 	file->private_data = handle;
 	if (podev->platform_support.bus_scale_table != NULL)
 		qcedev_ce_high_bw_req(podev, true);
+
+	mutex_init(&handle->registeredbufs.lock);
+	INIT_LIST_HEAD(&handle->registeredbufs.list);
 	return 0;
 }
 
@@ -1857,6 +1869,62 @@
 		}
 		break;
 
+	case QCEDEV_IOCTL_MAP_BUF_REQ:
+		{
+			unsigned long long vaddr = 0;
+			struct qcedev_map_buf_req map_buf = { {0} };
+			int i = 0;
+
+			if (copy_from_user(&map_buf,
+					(void __user *)arg, sizeof(map_buf)))
+				return -EFAULT;
+
+			for (i = 0; i < map_buf.num_fds; i++) {
+				err = qcedev_check_and_map_buffer(handle,
+						map_buf.fd[i],
+						map_buf.fd_offset[i],
+						map_buf.fd_size[i],
+						&vaddr);
+				if (err) {
+					pr_err(
+						"%s: err: failed to map fd(%d) - %d\n",
+						__func__, map_buf.fd[i], err);
+					return err;
+				}
+				map_buf.buf_vaddr[i] = vaddr;
+				pr_info("%s: info: vaddr = %llx\n",
+					__func__, vaddr);
+			}
+
+			if (copy_to_user((void __user *)arg, &map_buf,
+					sizeof(map_buf)))
+				return -EFAULT;
+			break;
+		}
+
+	case QCEDEV_IOCTL_UNMAP_BUF_REQ:
+		{
+			struct qcedev_unmap_buf_req unmap_buf = { { 0 } };
+			int i = 0;
+
+			if (copy_from_user(&unmap_buf,
+					(void __user *)arg, sizeof(unmap_buf)))
+				return -EFAULT;
+
+			for (i = 0; i < unmap_buf.num_fds; i++) {
+				err = qcedev_check_and_unmap_buffer(handle,
+						unmap_buf.fd[i]);
+				if (err) {
+					pr_err(
+						"%s: err: failed to unmap fd(%d) - %d\n",
+						 __func__,
+						unmap_buf.fd[i], err);
+					return err;
+				}
+			}
+			break;
+		}
+
 	default:
 		return -ENOTTY;
 	}
@@ -1864,7 +1932,7 @@
 	return err;
 }
 
-static int qcedev_probe(struct platform_device *pdev)
+static int qcedev_probe_device(struct platform_device *pdev)
 {
 	void *handle = NULL;
 	int rc = 0;
@@ -1877,6 +1945,8 @@
 	INIT_LIST_HEAD(&podev->ready_commands);
 	podev->active_command = NULL;
 
+	INIT_LIST_HEAD(&podev->context_banks);
+
 	spin_lock_init(&podev->lock);
 
 	tasklet_init(&podev->done_tasklet, req_done, (unsigned long)podev);
@@ -1934,11 +2004,33 @@
 	}
 
 	rc = misc_register(&podev->miscdevice);
-	if (rc >= 0)
-		return 0;
+	if (rc) {
+		pr_err("%s: err: register failed for misc: %d\n", __func__, rc);
+		goto exit_qce_close;
+	}
+
+	podev->mem_client = qcedev_mem_new_client(MEM_ION);
+	if (!podev->mem_client) {
+		pr_err("%s: err: qcedev_mem_new_client failed\n", __func__);
+		goto err;
+	}
+
+	rc = of_platform_populate(pdev->dev.of_node, qcedev_match,
+			NULL, &pdev->dev);
+	if (rc) {
+		pr_err("%s: err: of_platform_populate failed: %d\n",
+			__func__, rc);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	if (podev->mem_client)
+		qcedev_mem_delete_client(podev->mem_client);
+	podev->mem_client = NULL;
 
 	misc_deregister(&podev->miscdevice);
-
 exit_qce_close:
 	if (handle)
 		qce_close(handle);
@@ -1947,11 +2039,23 @@
 exit_unregister_bus_scale:
 	if (podev->platform_support.bus_scale_table != NULL)
 		msm_bus_scale_unregister_client(podev->bus_scale_handle);
+	podev->bus_scale_handle = 0;
 	platform_set_drvdata(pdev, NULL);
 	podev->pdev = NULL;
 	podev->qce = NULL;
 
 	return rc;
+}
+
+static int qcedev_probe(struct platform_device *pdev)
+{
+	if (of_device_is_compatible(pdev->dev.of_node, "qcom,qcedev"))
+		return qcedev_probe_device(pdev);
+	else if (of_device_is_compatible(pdev->dev.of_node,
+		"qcom,qcedev,context-bank"))
+		return qcedev_parse_context_bank(pdev);
+
+	return -EINVAL;
 };
 
 static int qcedev_remove(struct platform_device *pdev)
@@ -2017,12 +2121,6 @@
 	return 0;
 }
 
-static const struct of_device_id qcedev_match[] = {
-	{	.compatible = "qcom,qcedev",
-	},
-	{}
-};
-
 static struct platform_driver qcedev_plat_driver = {
 	.probe = qcedev_probe,
 	.remove = qcedev_remove,
diff --git a/drivers/crypto/msm/qcedev_smmu.c b/drivers/crypto/msm/qcedev_smmu.c
new file mode 100644
index 0000000..c99b493
--- /dev/null
+++ b/drivers/crypto/msm/qcedev_smmu.c
@@ -0,0 +1,546 @@
+/* Qti (or) Qualcomm Technologies Inc CE device driver.
+ *
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <asm/dma-iommu.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/qcedev.h>
+#include "qcedevi.h"
+#include "qcedev_smmu.h"
+#include "soc/qcom/secure_buffer.h"
+
+static bool compare_ion_buffers(struct qcedev_mem_client *mem_client,
+					struct ion_handle *hndl, int fd);
+
+static int qcedev_setup_context_bank(struct context_bank_info *cb,
+				struct device *dev)
+{
+	int rc = 0;
+	int secure_vmid = VMID_INVAL;
+	struct bus_type *bus;
+
+	if (!dev || !cb) {
+		pr_err("%s err: invalid input params\n", __func__);
+		return -EINVAL;
+	}
+	cb->dev = dev;
+
+	bus = cb->dev->bus;
+	if (IS_ERR_OR_NULL(bus)) {
+		pr_err("%s err: failed to get bus type\n", __func__);
+		rc = PTR_ERR(bus) ?: -ENODEV;
+		goto remove_cb;
+	}
+
+	cb->mapping = arm_iommu_create_mapping(bus, cb->start_addr, cb->size);
+	if (IS_ERR_OR_NULL(cb->mapping)) {
+		pr_err("%s err: failed to create mapping\n", __func__);
+		rc = PTR_ERR(cb->mapping) ?: -ENODEV;
+		goto remove_cb;
+	}
+
+	if (cb->is_secure) {
+		/* Hardcoded since we only have this vmid.*/
+		secure_vmid = VMID_CP_BITSTREAM;
+		rc = iommu_domain_set_attr(cb->mapping->domain,
+			DOMAIN_ATTR_SECURE_VMID, &secure_vmid);
+		if (rc) {
+			pr_err("%s err: programming secure vmid failed %s %d\n",
+				__func__, dev_name(dev), rc);
+			goto release_mapping;
+		}
+	}
+
+	rc = arm_iommu_attach_device(cb->dev, cb->mapping);
+	if (rc) {
+		pr_err("%s err: Failed to attach %s - %d\n",
+			__func__, dev_name(dev), rc);
+		goto release_mapping;
+	}
+
+	pr_info("%s Attached %s and create mapping\n", __func__, dev_name(dev));
+	pr_info("%s Context Bank name:%s, is_secure:%d, start_addr:%#x\n",
+			__func__, cb->name, cb->is_secure, cb->start_addr);
+	pr_info("%s size:%#x, dev:%pK, mapping:%pK\n", __func__, cb->size,
+			cb->dev, cb->mapping);
+	return rc;
+
+release_mapping:
+	arm_iommu_release_mapping(cb->mapping);
+remove_cb:
+	return rc;
+}
+
+int qcedev_parse_context_bank(struct platform_device *pdev)
+{
+	struct qcedev_control *podev;
+	struct context_bank_info *cb = NULL;
+	struct device_node *np = NULL;
+	int rc = 0;
+
+	if (!pdev) {
+		pr_err("%s err: invalid platform devices\n", __func__);
+		return -EINVAL;
+	}
+	if (!pdev->dev.parent) {
+		pr_err("%s err: failed to find a parent for %s\n",
+			__func__, dev_name(&pdev->dev));
+		return -EINVAL;
+	}
+
+	podev = dev_get_drvdata(pdev->dev.parent);
+	np = pdev->dev.of_node;
+	cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL);
+	if (!cb) {
+		pr_err("%s ERROR = Failed to allocate cb\n", __func__);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&cb->list);
+	list_add_tail(&cb->list, &podev->context_banks);
+
+	rc = of_property_read_string(np, "label", &cb->name);
+	if (rc)
+		pr_debug("%s ERROR = Unable to read label\n", __func__);
+
+	rc = of_property_read_u32(np, "virtual-addr", &cb->start_addr);
+	if (rc) {
+		pr_err("%s err: cannot read virtual region addr %d\n",
+			__func__, rc);
+		goto err_setup_cb;
+	}
+
+	rc = of_property_read_u32(np, "virtual-size", &cb->size);
+	if (rc) {
+		pr_err("%s err: cannot read virtual region size %d\n",
+			__func__, rc);
+		goto err_setup_cb;
+	}
+
+	cb->is_secure = of_property_read_bool(np, "qcom,secure-context-bank");
+
+	rc = qcedev_setup_context_bank(cb, &pdev->dev);
+	if (rc) {
+		pr_err("%s err: cannot setup context bank %d\n", __func__, rc);
+		goto err_setup_cb;
+	}
+
+	return 0;
+
+err_setup_cb:
+	devm_kfree(&pdev->dev, cb);
+	list_del(&cb->list);
+	return rc;
+}
+
+struct qcedev_mem_client *qcedev_mem_new_client(enum qcedev_mem_type mtype)
+{
+	struct qcedev_mem_client *mem_client = NULL;
+	struct ion_client *clnt = NULL;
+
+	switch (mtype) {
+	case MEM_ION:
+		clnt = msm_ion_client_create("qcedev_client");
+		if (!clnt)
+			pr_err("%s: err: failed to allocate ion client\n",
+				__func__);
+		break;
+
+	default:
+		pr_err("%s: err: Mem type not supported\n", __func__);
+	}
+
+	if (clnt) {
+		mem_client = kzalloc(sizeof(*mem_client), GFP_KERNEL);
+		if (!mem_client)
+			goto err;
+		mem_client->mtype = mtype;
+		mem_client->client = clnt;
+	}
+
+	return mem_client;
+
+err:
+	if (clnt)
+		ion_client_destroy(clnt);
+	return NULL;
+}
+
+void qcedev_mem_delete_client(struct qcedev_mem_client *mem_client)
+{
+	if (mem_client && mem_client->client)
+		ion_client_destroy(mem_client->client);
+
+	kfree(mem_client);
+}
+
+static bool is_iommu_present(struct qcedev_handle *qce_hndl)
+{
+	return !list_empty(&qce_hndl->cntl->context_banks);
+}
+
+static struct context_bank_info *get_context_bank(
+		struct qcedev_handle *qce_hndl, bool is_secure)
+{
+	struct qcedev_control *podev = qce_hndl->cntl;
+	struct context_bank_info *cb = NULL, *match = NULL;
+
+	list_for_each_entry(cb, &podev->context_banks, list) {
+		if (cb->is_secure == is_secure) {
+			match = cb;
+			break;
+		}
+	}
+	return match;
+}
+
+static int ion_map_buffer(struct qcedev_handle *qce_hndl,
+		struct qcedev_mem_client *mem_client, int fd,
+		unsigned int fd_size, struct qcedev_reg_buf_info *binfo)
+{
+	struct ion_client *clnt = mem_client->client;
+	struct ion_handle *hndl = NULL;
+	unsigned long ion_flags = 0;
+	int rc = 0;
+	struct dma_buf *buf = NULL;
+	struct dma_buf_attachment *attach = NULL;
+	struct sg_table *table = NULL;
+	struct context_bank_info *cb = NULL;
+
+	buf = dma_buf_get(fd);
+	if (IS_ERR_OR_NULL(buf))
+		return -EINVAL;
+
+	hndl = ion_import_dma_buf(clnt, buf);
+	if (IS_ERR_OR_NULL(hndl)) {
+		pr_err("%s: err: invalid ion_handle\n", __func__);
+		rc = -ENOMEM;
+		goto import_buf_err;
+	}
+
+	rc = ion_handle_get_flags(clnt, hndl, &ion_flags);
+	if (rc) {
+		pr_err("%s: err: failed to get ion flags: %d\n", __func__, rc);
+		goto map_err;
+	}
+
+	if (is_iommu_present(qce_hndl)) {
+		cb = get_context_bank(qce_hndl, ion_flags & ION_FLAG_SECURE);
+		if (!cb) {
+			pr_err("%s: err: failed to get context bank info\n",
+				__func__);
+			rc = -EIO;
+			goto map_err;
+		}
+
+		/* Prepare a dma buf for dma on the given device */
+		attach = dma_buf_attach(buf, cb->dev);
+		if (IS_ERR_OR_NULL(attach)) {
+			rc = PTR_ERR(attach) ?: -ENOMEM;
+			pr_err("%s: err: failed to attach dmabuf\n", __func__);
+			goto map_err;
+		}
+
+		/* Get the scatterlist for the given attachment */
+		table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+		if (IS_ERR_OR_NULL(table)) {
+			rc = PTR_ERR(table) ?: -ENOMEM;
+			pr_err("%s: err: failed to map table\n", __func__);
+			goto map_table_err;
+		}
+
+		/* Map a scatterlist into an SMMU */
+		rc = msm_dma_map_sg_lazy(cb->dev, table->sgl, table->nents,
+				DMA_BIDIRECTIONAL, buf);
+		if (rc != table->nents) {
+			pr_err(
+				"%s: err: mapping failed with rc(%d), expected rc(%d)\n",
+				__func__, rc, table->nents);
+			rc = -ENOMEM;
+			goto map_sg_err;
+		}
+
+		if (table->sgl) {
+			binfo->ion_buf.iova = table->sgl->dma_address;
+			binfo->ion_buf.mapped_buf_size = sg_dma_len(table->sgl);
+			if (binfo->ion_buf.mapped_buf_size < fd_size) {
+				pr_err("%s: err: mapping failed, size mismatch",
+						__func__);
+				rc = -ENOMEM;
+				goto map_sg_err;
+			}
+
+
+		} else {
+			pr_err("%s: err: sg list is NULL\n", __func__);
+			rc = -ENOMEM;
+			goto map_sg_err;
+		}
+
+		binfo->ion_buf.mapping_info.dev = cb->dev;
+		binfo->ion_buf.mapping_info.mapping = cb->mapping;
+		binfo->ion_buf.mapping_info.table = table;
+		binfo->ion_buf.mapping_info.attach = attach;
+		binfo->ion_buf.mapping_info.buf = buf;
+		binfo->ion_buf.hndl = hndl;
+	} else {
+		pr_err("%s: err: smmu not enabled\n", __func__);
+		rc = -EIO;
+		goto map_err;
+	}
+
+	return 0;
+
+map_sg_err:
+	dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL);
+map_table_err:
+	dma_buf_detach(buf, attach);
+map_err:
+	if (hndl)
+		ion_free(clnt, hndl);
+import_buf_err:
+	dma_buf_put(buf);
+	return rc;
+}
+
+static int ion_unmap_buffer(struct qcedev_handle *qce_hndl,
+		struct qcedev_reg_buf_info *binfo)
+{
+	struct dma_mapping_info *mapping_info = &binfo->ion_buf.mapping_info;
+	struct qcedev_mem_client *mem_client = qce_hndl->cntl->mem_client;
+
+	if (is_iommu_present(qce_hndl)) {
+		msm_dma_unmap_sg(mapping_info->dev, mapping_info->table->sgl,
+			 mapping_info->table->nents, DMA_BIDIRECTIONAL,
+			 mapping_info->buf);
+		dma_buf_unmap_attachment(mapping_info->attach,
+			mapping_info->table, DMA_BIDIRECTIONAL);
+		dma_buf_detach(mapping_info->buf, mapping_info->attach);
+		dma_buf_put(mapping_info->buf);
+
+		if (binfo->ion_buf.hndl)
+			ion_free(mem_client->client, binfo->ion_buf.hndl);
+
+	}
+	return 0;
+}
+
+static int qcedev_map_buffer(struct qcedev_handle *qce_hndl,
+		struct qcedev_mem_client *mem_client, int fd,
+		unsigned int fd_size, struct qcedev_reg_buf_info *binfo)
+{
+	int rc = 0;
+
+	switch (mem_client->mtype) {
+	case MEM_ION:
+		rc = ion_map_buffer(qce_hndl, mem_client, fd, fd_size, binfo);
+		break;
+	default:
+		pr_err("%s: err: Mem type not supported\n", __func__);
+		break;
+	}
+
+	if (rc)
+		pr_err("%s: err: failed to map buffer\n", __func__);
+
+	return rc;
+}
+
+static int qcedev_unmap_buffer(struct qcedev_handle *qce_hndl,
+		struct qcedev_mem_client *mem_client,
+		struct qcedev_reg_buf_info *binfo)
+{
+	int rc = 0;
+
+	switch (mem_client->mtype) {
+	case MEM_ION:
+		rc = ion_unmap_buffer(qce_hndl, binfo);
+		break;
+	default:
+		pr_err("%s: err: Mem type not supported\n", __func__);
+		break;
+	}
+
+	if (rc)
+		pr_err("%s: err: failed to unmap buffer\n", __func__);
+
+	return rc;
+}
+
+static bool compare_ion_buffers(struct qcedev_mem_client *mem_client,
+		struct ion_handle *hndl, int fd)
+{
+	bool match = false;
+	struct ion_handle *fd_hndl = NULL;
+	struct dma_buf *dma_buf;
+
+	dma_buf = dma_buf_get(fd);
+	if (IS_ERR_OR_NULL(dma_buf))
+		return false;
+
+	fd_hndl = ion_import_dma_buf(mem_client->client, dma_buf);
+	if (IS_ERR_OR_NULL(fd_hndl)) {
+		match = false;
+		goto err_exit;
+	}
+
+	match = fd_hndl == hndl ? true : false;
+
+	if (fd_hndl)
+		ion_free(mem_client->client, fd_hndl);
+err_exit:
+	dma_buf_put(dma_buf);
+	return match;
+}
+
+int qcedev_check_and_map_buffer(void *handle,
+		int fd, unsigned int offset, unsigned int fd_size,
+		unsigned long long *vaddr)
+{
+	bool found = false;
+	struct qcedev_reg_buf_info *binfo = NULL, *temp = NULL;
+	struct qcedev_mem_client *mem_client = NULL;
+	struct qcedev_handle *qce_hndl = handle;
+	int rc = 0;
+	unsigned long mapped_size = 0;
+
+	if (!handle || !vaddr || fd < 0 || offset >= fd_size) {
+		pr_err("%s: err: invalid input arguments\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!qce_hndl->cntl || !qce_hndl->cntl->mem_client) {
+		pr_err("%s: err: invalid qcedev handle\n", __func__);
+		return -EINVAL;
+	}
+	mem_client = qce_hndl->cntl->mem_client;
+
+	if (mem_client->mtype != MEM_ION)
+		return -EPERM;
+
+	/* Check if the buffer fd is already mapped */
+	mutex_lock(&qce_hndl->registeredbufs.lock);
+	list_for_each_entry(temp, &qce_hndl->registeredbufs.list, list) {
+		found = compare_ion_buffers(mem_client, temp->ion_buf.hndl, fd);
+		if (found) {
+			*vaddr = temp->ion_buf.iova;
+			mapped_size = temp->ion_buf.mapped_buf_size;
+			atomic_inc(&temp->ref_count);
+			break;
+		}
+	}
+	mutex_unlock(&qce_hndl->registeredbufs.lock);
+
+	/* If buffer fd is not mapped then create a fresh mapping */
+	if (!found) {
+		pr_debug("%s: info: ion fd not registered with driver\n",
+			__func__);
+		binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+		if (!binfo) {
+			pr_err("%s: err: failed to allocate binfo\n",
+				__func__);
+			rc = -ENOMEM;
+			goto error;
+		}
+		rc = qcedev_map_buffer(qce_hndl, mem_client, fd,
+							fd_size, binfo);
+		if (rc) {
+			pr_err("%s: err: failed to map fd (%d) error = %d\n",
+				__func__, fd, rc);
+			goto error;
+		}
+
+		*vaddr = binfo->ion_buf.iova;
+		mapped_size = binfo->ion_buf.mapped_buf_size;
+		atomic_inc(&binfo->ref_count);
+
+		/* Add buffer mapping information to regd buffer list */
+		mutex_lock(&qce_hndl->registeredbufs.lock);
+		list_add_tail(&binfo->list, &qce_hndl->registeredbufs.list);
+		mutex_unlock(&qce_hndl->registeredbufs.lock);
+	}
+
+	/* Make sure the offset is within the mapped range */
+	if (offset >= mapped_size) {
+		pr_err(
+			"%s: err: Offset (%u) exceeds mapped size(%lu) for fd: %d\n",
+			__func__, offset, mapped_size, fd);
+		rc = -ERANGE;
+		goto unmap;
+	}
+
+	/* return the mapped virtual address adjusted by offset */
+	*vaddr += offset;
+
+	return 0;
+
+unmap:
+	if (!found)
+		qcedev_unmap_buffer(handle, mem_client, binfo);
+
+error:
+	kfree(binfo);
+	return rc;
+}
+
+int qcedev_check_and_unmap_buffer(void *handle, int fd)
+{
+	struct qcedev_reg_buf_info *binfo = NULL, *dummy = NULL;
+	struct qcedev_mem_client *mem_client = NULL;
+	struct qcedev_handle *qce_hndl = handle;
+	bool found = false;
+
+	if (!handle || fd < 0) {
+		pr_err("%s: err: invalid input arguments\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!qce_hndl->cntl || !qce_hndl->cntl->mem_client) {
+		pr_err("%s: err: invalid qcedev handle\n", __func__);
+		return -EINVAL;
+	}
+	mem_client = qce_hndl->cntl->mem_client;
+
+	if (mem_client->mtype != MEM_ION)
+		return -EPERM;
+
+	/* Check if the buffer fd is mapped and present in the regd list. */
+	mutex_lock(&qce_hndl->registeredbufs.lock);
+	list_for_each_entry_safe(binfo, dummy,
+		&qce_hndl->registeredbufs.list, list) {
+
+		found = compare_ion_buffers(mem_client,
+				binfo->ion_buf.hndl, fd);
+		if (found) {
+			atomic_dec(&binfo->ref_count);
+
+			/* Unmap only if there are no more references */
+			if (atomic_read(&binfo->ref_count) == 0) {
+				qcedev_unmap_buffer(qce_hndl,
+					mem_client, binfo);
+				list_del(&binfo->list);
+				kfree(binfo);
+			}
+			break;
+		}
+	}
+	mutex_unlock(&qce_hndl->registeredbufs.lock);
+
+	if (!found) {
+		pr_err("%s: err: calling unmap on unknown fd %d\n",
+			__func__, fd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/drivers/crypto/msm/qcedev_smmu.h b/drivers/crypto/msm/qcedev_smmu.h
new file mode 100644
index 0000000..17e688a
--- /dev/null
+++ b/drivers/crypto/msm/qcedev_smmu.h
@@ -0,0 +1,89 @@
+/* Qti (or) Qualcomm Technologies Inc CE device driver.
+ *
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRIVERS_CRYPTO_PARSE_H_
+#define _DRIVERS_CRYPTO_PARSE_H_
+
+#include <asm/dma-iommu.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-direction.h>
+#include <linux/iommu.h>
+#include <linux/msm_dma_iommu_mapping.h>
+#include <linux/msm_ion.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+struct context_bank_info {
+	struct list_head list;
+	const char *name;
+	u32 buffer_type;
+	u32 start_addr;
+	u32 size;
+	bool is_secure;
+	struct device *dev;
+	struct dma_iommu_mapping *mapping;
+};
+
+enum qcedev_mem_type {
+	MEM_ION,
+};
+
+struct qcedev_mem_client {
+	enum qcedev_mem_type mtype;
+	void *client;
+};
+
+struct dma_mapping_info {
+	struct device *dev;
+	struct dma_iommu_mapping *mapping;
+	struct sg_table *table;
+	struct dma_buf_attachment *attach;
+	struct dma_buf *buf;
+};
+
+struct qcedev_ion_buf_info {
+	struct ion_handle *hndl;
+	struct dma_mapping_info mapping_info;
+	ion_phys_addr_t iova;
+	unsigned long mapped_buf_size;
+};
+
+struct qcedev_reg_buf_info {
+	struct list_head list;
+	union {
+		struct qcedev_ion_buf_info ion_buf;
+	};
+	atomic_t ref_count;
+};
+
+struct qcedev_buffer_list {
+	struct list_head list;
+	struct mutex lock;
+};
+
+int qcedev_parse_context_bank(struct platform_device *pdev);
+struct qcedev_mem_client *qcedev_mem_new_client(enum qcedev_mem_type mtype);
+void qcedev_mem_delete_client(struct qcedev_mem_client *mem_client);
+int qcedev_check_and_map_buffer(void *qce_hndl,
+		int fd, unsigned int offset, unsigned int fd_size,
+		unsigned long long *vaddr);
+int qcedev_check_and_unmap_buffer(void *handle, int fd);
+
+extern struct qcedev_reg_buf_info *global_binfo_in;
+extern struct qcedev_reg_buf_info *global_binfo_out;
+extern struct qcedev_reg_buf_info *global_binfo_res;
+#endif
+
diff --git a/drivers/crypto/msm/qcedevi.h b/drivers/crypto/msm/qcedevi.h
index c26ed71..f99adaf 100644
--- a/drivers/crypto/msm/qcedevi.h
+++ b/drivers/crypto/msm/qcedevi.h
@@ -1,6 +1,6 @@
 /* QTI crypto Driver
  *
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -21,6 +21,7 @@
 #include <linux/platform_data/qcom_crypto_device.h>
 #include <linux/fips_status.h>
 #include "qce.h"
+#include "qcedev_smmu.h"
 
 #define CACHE_LINE_SIZE 32
 #define CE_SHA_BLOCK_SIZE SHA256_BLOCK_SIZE
@@ -107,6 +108,8 @@
 	struct qcedev_async_req *active_command;
 	spinlock_t lock;
 	struct tasklet_struct done_tasklet;
+	struct list_head context_banks;
+	struct qcedev_mem_client *mem_client;
 };
 
 struct qcedev_handle {
@@ -114,6 +117,8 @@
 	struct qcedev_control *cntl;
 	/* qce internal sha context*/
 	struct qcedev_sha_ctxt sha_ctxt;
+	/* qcedev mapped buffer list */
+	struct qcedev_buffer_list registeredbufs;
 };
 
 void qcedev_cipher_req_cb(void *cookie, unsigned char *icv,
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index 6b9d0e7..f7e9eb3 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -382,7 +382,8 @@
 		.minor = 5,
 		.patchid = ANY_ID,
 		.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_PREEMPTION |
-			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC,
+			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
+			ADRENO_IOCOHERENT,
 		.sqefw_name = "a630_sqe.fw",
 		.zap_name = "a615_zap",
 		.gpudev = &adreno_a6xx_gpudev,
@@ -400,7 +401,8 @@
 		.minor = 6,
 		.patchid = ANY_ID,
 		.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_PREEMPTION |
-			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC,
+			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_IFPC |
+			ADRENO_IOCOHERENT,
 		.sqefw_name = "a630_sqe.fw",
 		.zap_name = "a615_zap",
 		.gpudev = &adreno_a6xx_gpudev,
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index 97b0cb2..6c8e664 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -749,6 +749,9 @@
 	if (context->flags & KGSL_CONTEXT_SECURE)
 		flags |= KGSL_MEMFLAGS_SECURE;
 
+	if (kgsl_is_compat_task())
+		flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
 	/*
 	 * gpumem_alloc_entry takes an extra refcount. Put it only when
 	 * destroying the context to keep the context record valid
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 2e617429..bb9f9ff 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2402,6 +2402,9 @@
 	if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
 		param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
 
+	if (kgsl_is_compat_task())
+		param->flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
 	entry->memdesc.flags = param->flags;
 
 	if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
@@ -2692,8 +2695,10 @@
 	if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
 		param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
 
-	entry->memdesc.flags = ((uint64_t) param->flags)
-		| KGSL_MEMFLAGS_FORCE_32BIT;
+	entry->memdesc.flags = (uint64_t) param->flags;
+
+	if (kgsl_is_compat_task())
+		entry->memdesc.flags |= KGSL_MEMFLAGS_FORCE_32BIT;
 
 	if (!kgsl_mmu_use_cpu_map(mmu))
 		entry->memdesc.flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
@@ -3197,6 +3202,9 @@
 	struct kgsl_gpuobj_alloc *param = data;
 	struct kgsl_mem_entry *entry;
 
+	if (kgsl_is_compat_task())
+		param->flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
 	entry = gpumem_alloc_entry(dev_priv, param->size, param->flags);
 
 	if (IS_ERR(entry))
@@ -3224,7 +3232,9 @@
 
 	/* Legacy functions doesn't support these advanced features */
 	flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
-	flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+
+	if (kgsl_is_compat_task())
+		flags |= KGSL_MEMFLAGS_FORCE_32BIT;
 
 	entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags);
 
@@ -3248,7 +3258,8 @@
 	struct kgsl_mem_entry *entry;
 	uint64_t flags = param->flags;
 
-	flags |= KGSL_MEMFLAGS_FORCE_32BIT;
+	if (kgsl_is_compat_task())
+		flags |= KGSL_MEMFLAGS_FORCE_32BIT;
 
 	entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags);
 
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index f4a2de5..1fa2717 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -27,6 +27,7 @@
 #include <linux/uaccess.h>
 #include <linux/kthread.h>
 #include <asm/cacheflush.h>
+#include <linux/compat.h>
 
 /*
  * --- kgsl drawobj flags ---
@@ -628,4 +629,9 @@
 
 	kernfs_create_link(dst->sd, dst_name, old);
 }
+
+static inline bool kgsl_is_compat_task(void)
+{
+	return (BITS_PER_LONG == 32) || is_compat_task();
+}
 #endif /* __KGSL_H */
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index c4296c8..0ce72f6 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1052,7 +1052,7 @@
 
 	if (pagetable->name != KGSL_MMU_GLOBAL_PT &&
 		pagetable->name != KGSL_MMU_SECURE_PT) {
-		if ((BITS_PER_LONG == 32) || is_compat_task()) {
+		if (kgsl_is_compat_task()) {
 			pt->svm_start = KGSL_IOMMU_SVM_BASE32;
 			pt->svm_end = KGSL_IOMMU_SECURE_BASE(mmu);
 		} else {
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index bedb812..9d2ab01 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -839,9 +839,10 @@
 	if (drvdata->mode == CS_MODE_SYSFS)
 		goto out;
 
-	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
 		drvdata->mode = CS_MODE_SYSFS;
 		tmc_etr_enable_hw(drvdata);
+	}
 
 	drvdata->enable = true;
 	drvdata->sticky_enable = true;
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
index 36d07c5..93c28ef 100644
--- a/drivers/input/misc/qpnp-power-on.c
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -31,6 +31,8 @@
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/input/qpnp-power-on.h>
+#include <linux/qpnp/qpnp-pbs.h>
+#include <linux/qpnp/qpnp-misc.h>
 #include <linux/power_supply.h>
 
 #define PMIC_VER_8941           0x01
@@ -203,6 +205,7 @@
 	struct list_head	list;
 	struct delayed_work	bark_work;
 	struct dentry		*debugfs;
+	struct device_node      *pbs_dev_node;
 	int			pon_trigger_reason;
 	int			pon_power_off_reason;
 	int			num_pon_reg;
@@ -220,6 +223,7 @@
 	u8			pon_ver;
 	u8			warm_reset_reason1;
 	u8			warm_reset_reason2;
+	u8                      twm_state;
 	bool			is_spon;
 	bool			store_hard_reset_reason;
 	bool			resin_hard_reset_disable;
@@ -227,8 +231,10 @@
 	bool			ps_hold_hard_reset_disable;
 	bool			ps_hold_shutdown_disable;
 	bool			kpdpwr_dbc_enable;
+	bool                    support_twm_config;
 	bool			resin_pon_reset;
 	ktime_t			kpdpwr_last_release_time;
+	struct notifier_block   pon_nb;
 	bool			legacy_hard_reset_offset;
 };
 
@@ -483,6 +489,7 @@
 
 static DEVICE_ATTR(debounce_us, 0664, qpnp_pon_dbc_show, qpnp_pon_dbc_store);
 
+#define PON_TWM_ENTRY_PBS_BIT           BIT(0)
 static int qpnp_pon_reset_config(struct qpnp_pon *pon,
 		enum pon_power_off_type type)
 {
@@ -490,6 +497,19 @@
 	bool disable = false;
 	u16 rst_en_reg;
 
+	/* Ignore the PS_HOLD reset config if TWM ENTRY is enabled */
+	if (pon->support_twm_config && pon->twm_state == PMIC_TWM_ENABLE) {
+		rc = qpnp_pbs_trigger_event(pon->pbs_dev_node,
+					PON_TWM_ENTRY_PBS_BIT);
+		if (rc < 0) {
+			pr_err("Unable to trigger PBS trigger for TWM entry rc=%d\n",
+							rc);
+			return rc;
+		}
+		pr_crit("PMIC configured for TWM entry\n");
+		return 0;
+	}
+
 	if (pon->pon_ver == QPNP_PON_GEN1_V1)
 		rst_en_reg = QPNP_PON_PS_HOLD_RST_CTL(pon);
 	else
@@ -2087,6 +2107,35 @@
 	return 0;
 }
 
+static int pon_twm_notifier_cb(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct qpnp_pon *pon = container_of(nb, struct qpnp_pon, pon_nb);
+
+	if (action != PMIC_TWM_CLEAR &&
+			action != PMIC_TWM_ENABLE) {
+		pr_debug("Unsupported option %lu\n", action);
+		return NOTIFY_OK;
+	}
+
+	pon->twm_state = (u8)action;
+	pr_debug("TWM state = %d\n", pon->twm_state);
+
+	return NOTIFY_OK;
+}
+
+static int pon_register_twm_notifier(struct qpnp_pon *pon)
+{
+	int rc;
+
+	pon->pon_nb.notifier_call = pon_twm_notifier_cb;
+	rc = qpnp_misc_twm_notifier_register(&pon->pon_nb);
+	if (rc < 0)
+		pr_err("Failed to register pon_twm_notifier_cb rc=%d\n", rc);
+
+	return rc;
+}
+
 static int qpnp_pon_probe(struct platform_device *pdev)
 {
 	struct qpnp_pon *pon;
@@ -2364,6 +2413,22 @@
 		goto err_out;
 	}
 
+	if (of_property_read_bool(pon->pdev->dev.of_node,
+					"qcom,support-twm-config")) {
+		pon->support_twm_config = true;
+		rc = pon_register_twm_notifier(pon);
+		if (rc < 0) {
+			pr_err("Failed to register TWM notifier rc=%d\n", rc);
+			return rc;
+		}
+		pon->pbs_dev_node = of_parse_phandle(pon->pdev->dev.of_node,
+						"qcom,pbs-client", 0);
+		if (!pon->pbs_dev_node) {
+			pr_err("Missing qcom,pbs-client property\n");
+			return -EINVAL;
+		}
+	}
+
 	rc = of_property_read_u32(pon->pdev->dev.of_node,
 				"qcom,pon-dbc-delay", &delay);
 	if (rc) {
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index fc5ca80..b705f4a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1205,6 +1205,24 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called colibri_vf50_ts.
 
+config TOUCHSCREEN_FT5X06_PSENSOR
+       tristate "FocalTech proximity feature support"
+       depends on TOUCHSCREEN_FT5X06 && SENSORS
+       help
+         Say Y here if you want to support ft5x06's proximity
+         feature.
+
+         If unsure, say N.
+
+config TOUCHSCREEN_FT5X06_GESTURE
+       tristate "FocalTech gesture feature support"
+       depends on TOUCHSCREEN_FT5X06
+       help
+         Say Y here if you want to support ft5x06's gesture
+         feature.
+
+         If unsure, say N.
+
 config TOUCHSCREEN_ROHM_BU21023
 	tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
 	depends on I2C
@@ -1240,4 +1258,14 @@
          To compile this driver as a module, choose M here: the
          module will be called ft5x06_ts.
 
+config FT_SECURE_TOUCH
+	bool "Secure Touch support for Focaltech Touchscreen"
+	depends on TOUCHSCREEN_FT5X06
+	help
+	  Say Y here
+	  -Focaltech touch driver is connected
+	  -To enable secure touch for Focaltech touch driver
+
+	  If unsure, say N.
+
 endif
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index 2ee1d79..7f320b3 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -3,7 +3,7 @@
  * FocalTech ft5x06 TouchScreen driver.
  *
  * Copyright (c) 2010  Focal tech Ltd.
- * Copyright (c) 2012-2015, 2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -18,6 +18,7 @@
 
 #include <linux/i2c.h>
 #include <linux/input.h>
+#include <linux/input/mt.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -26,74 +27,512 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
 #include <linux/input/ft5x06_ts.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
 
-#ifdef CONFIG_HAS_EARLYSUSPEND
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
 #include <linux/earlysuspend.h>
 /* Early-suspend level */
-#define FT5X06_SUSPEND_LEVEL 1
+#define FT_SUSPEND_LEVEL 1
 #endif
 
-#define CFG_MAX_TOUCH_POINTS	5
+#if defined(CONFIG_FT_SECURE_TOUCH)
+#include <linux/completion.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *data);
+#endif
 
-#define FT_STARTUP_DLY		150
-#define FT_RESET_DLY		20
+#define FT_DRIVER_VERSION	0x02
+
+#define FT_META_REGS		3
+#define FT_ONE_TCH_LEN		6
+#define FT_TCH_LEN(x)		(FT_META_REGS + FT_ONE_TCH_LEN * x)
 
 #define FT_PRESS		0x7F
 #define FT_MAX_ID		0x0F
-#define FT_TOUCH_STEP		6
 #define FT_TOUCH_X_H_POS	3
 #define FT_TOUCH_X_L_POS	4
 #define FT_TOUCH_Y_H_POS	5
 #define FT_TOUCH_Y_L_POS	6
+#define FT_TD_STATUS		2
 #define FT_TOUCH_EVENT_POS	3
 #define FT_TOUCH_ID_POS		5
+#define FT_TOUCH_DOWN		0
+#define FT_TOUCH_CONTACT	2
 
-#define POINT_READ_BUF	(3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS)
+/* register address*/
+#define FT_REG_DEV_MODE		0x00
+#define FT_DEV_MODE_REG_CAL	0x02
+#define FT_REG_ID		0xA3
+#define FT_REG_PMODE		0xA5
+#define FT_REG_FW_VER		0xA6
+#define FT_REG_FW_VENDOR_ID	0xA8
+#define FT_REG_POINT_RATE	0x88
+#define FT_REG_THGROUP		0x80
+#define FT_REG_ECC		0xCC
+#define FT_REG_RESET_FW		0x07
+#define FT_REG_FW_MIN_VER	0xB2
+#define FT_REG_FW_SUB_MIN_VER	0xB3
 
-/*register address*/
-#define FT5X06_REG_ID		0xA3
-#define FT5X06_REG_PMODE	0xA5
-#define FT5X06_REG_FW_VER	0xA6
-#define FT5X06_REG_POINT_RATE	0x88
-#define FT5X06_REG_THGROUP	0x80
+/* gesture register address*/
+#define FT_REG_GESTURE_ENABLE	0xD0
+#define FT_REG_GESTURE_OUTPUT	0xD3
+
+/* gesture register bits*/
+#define FT_GESTURE_DOUBLECLICK_COORD_X		100
+#define FT_GESTURE_DOUBLECLICK_COORD_Y		100
+#define FT_GESTURE_WAKEUP_TIMEOUT		500
+#define FT_GESTURE_DEFAULT_TRACKING_ID		0x0A
+#define FT_GESTURE_DOUBLECLICK_ID		0x24
+#define FT_GESTURE_POINTER_NUM_MAX		128
+#define FT_GESTURE_POINTER_SIZEOF		4
+#define FT_GESTURE_ID_FLAG_SIZE			1
+#define FT_GESTURE_POINTER_NUM_FLAG_SIZE	1
+/* 6 bytes are taken to mark which gesture is supported in firmware */
+#define FT_GESTURE_SET_FLAG_SIZE		6
+#define I2C_TRANSFER_MAX_BYTE			255
+#define FT_GESTURE_DATA_HEADER	(FT_GESTURE_ID_FLAG_SIZE + \
+				FT_GESTURE_POINTER_NUM_FLAG_SIZE + \
+				FT_GESTURE_SET_FLAG_SIZE)
 
 /* power register bits*/
-#define FT5X06_PMODE_ACTIVE		0x00
-#define FT5X06_PMODE_MONITOR		0x01
-#define FT5X06_PMODE_STANDBY		0x02
-#define FT5X06_PMODE_HIBERNATE		0x03
+#define FT_PMODE_ACTIVE		0x00
+#define FT_PMODE_MONITOR	0x01
+#define FT_PMODE_STANDBY	0x02
+#define FT_PMODE_HIBERNATE	0x03
+#define FT_FACTORYMODE_VALUE	0x40
+#define FT_WORKMODE_VALUE	0x00
+#define FT_RST_CMD_REG1		0xFC
+#define FT_RST_CMD_REG2		0xBC
+#define FT_READ_ID_REG		0x90
+#define FT_ERASE_APP_REG	0x61
+#define FT_ERASE_PANEL_REG	0x63
+#define FT_FW_START_REG		0xBF
 
-#define FT5X06_VTG_MIN_UV	2600000
-#define FT5X06_VTG_MAX_UV	3300000
-#define FT5X06_I2C_VTG_MIN_UV	1800000
-#define FT5X06_I2C_VTG_MAX_UV	1800000
+#define FT_STATUS_NUM_TP_MASK	0x0F
 
-#define FT5X06_COORDS_ARR_SIZE	4
+#define FT_VTG_MIN_UV		2600000
+#define FT_VTG_MAX_UV		3300000
+#define FT_I2C_VTG_MIN_UV	1800000
+#define FT_I2C_VTG_MAX_UV	1800000
+
+#define FT_COORDS_ARR_SIZE	4
 #define MAX_BUTTONS		4
 
-struct ts_event {
-	u16 x[CFG_MAX_TOUCH_POINTS];	/*x coordinate */
-	u16 y[CFG_MAX_TOUCH_POINTS];	/*y coordinate */
-	/* touch event: 0 -- down; 1-- contact; 2 -- contact */
-	u8 touch_event[CFG_MAX_TOUCH_POINTS];
-	u8 finger_id[CFG_MAX_TOUCH_POINTS];	/*touch ID */
-	u16 pressure;
-	u8 touch_point;
+#define FT_8BIT_SHIFT		8
+#define FT_4BIT_SHIFT		4
+#define FT_FW_NAME_MAX_LEN	50
+
+#define FT5316_ID		0x0A
+#define FT5306I_ID		0x55
+#define FT6X06_ID		0x06
+#define FT6X36_ID		0x36
+
+#define FT_UPGRADE_AA		0xAA
+#define FT_UPGRADE_55		0x55
+
+#define FT_FW_MIN_SIZE		8
+#define FT_FW_MAX_SIZE		32768
+
+/* Firmware file is not supporting minor and sub minor so use 0 */
+#define FT_FW_FILE_MAJ_VER(x)	((x)->data[(x)->size - 2])
+#define FT_FW_FILE_MIN_VER(x)	0
+#define FT_FW_FILE_SUB_MIN_VER(x) 0
+#define FT_FW_FILE_VENDOR_ID(x)	((x)->data[(x)->size - 1])
+
+#define FT_FW_FILE_MAJ_VER_FT6X36(x)	((x)->data[0x10a])
+#define FT_FW_FILE_VENDOR_ID_FT6X36(x)	((x)->data[0x108])
+
+/**
+ * Application data verification will be run before upgrade flow.
+ * Firmware image stores some flags with negative and positive value
+ * in corresponding addresses, we need pick them out do some check to
+ * make sure the application data is valid.
+ */
+#define FT_FW_CHECK(x, ts_data) \
+	(ts_data->family_id == FT6X36_ID ? \
+	(((x)->data[0x104] ^ (x)->data[0x105]) == 0xFF \
+	&& ((x)->data[0x106] ^ (x)->data[0x107]) == 0xFF) : \
+	(((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \
+	&& ((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \
+	&& ((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF))
+
+#define FT_MAX_TRIES		5
+#define FT_RETRY_DLY		20
+
+#define FT_MAX_WR_BUF		10
+#define FT_MAX_RD_BUF		2
+#define FT_FW_PKT_LEN		128
+#define FT_FW_PKT_META_LEN	6
+#define FT_FW_PKT_DLY_MS	20
+#define FT_FW_LAST_PKT		0x6ffa
+#define FT_EARSE_DLY_MS		100
+#define FT_55_AA_DLY_NS		5000
+
+#define FT_UPGRADE_LOOP		30
+#define FT_CAL_START		0x04
+#define FT_CAL_FIN		0x00
+#define FT_CAL_STORE		0x05
+#define FT_CAL_RETRY		100
+#define FT_REG_CAL		0x00
+#define FT_CAL_MASK		0x70
+
+#define FT_INFO_MAX_LEN		512
+
+#define FT_BLOADER_SIZE_OFF	12
+#define FT_BLOADER_NEW_SIZE	30
+#define FT_DATA_LEN_OFF_OLD_FW	8
+#define FT_DATA_LEN_OFF_NEW_FW	14
+#define FT_FINISHING_PKT_LEN_OLD_FW	6
+#define FT_FINISHING_PKT_LEN_NEW_FW	12
+#define FT_MAGIC_BLOADER_Z7	0x7bfa
+#define FT_MAGIC_BLOADER_LZ4	0x6ffa
+#define FT_MAGIC_BLOADER_GZF_30	0x7ff4
+#define FT_MAGIC_BLOADER_GZF	0x7bf4
+
+#define PINCTRL_STATE_ACTIVE	"pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND	"pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE	"pmx_ts_release"
+
+static irqreturn_t ft5x06_ts_interrupt(int irq, void *data);
+
+enum {
+	FT_BLOADER_VERSION_LZ4 = 0,
+	FT_BLOADER_VERSION_Z7 = 1,
+	FT_BLOADER_VERSION_GZF = 2,
 };
 
+enum {
+	FT_FT5336_FAMILY_ID_0x11 = 0x11,
+	FT_FT5336_FAMILY_ID_0x12 = 0x12,
+	FT_FT5336_FAMILY_ID_0x13 = 0x13,
+	FT_FT5336_FAMILY_ID_0x14 = 0x14,
+};
+
+#define FT_STORE_TS_INFO(buf, id, fw_maj, fw_min, fw_sub_min) \
+			snprintf(buf, FT_INFO_MAX_LEN, \
+				"vendor name = Focaltech\n" \
+				"model = 0x%x\n" \
+				"fw_version = %d.%d.%d\n", \
+				id, fw_maj, fw_min, fw_sub_min)
+#define FT_TS_INFO_SYSFS_DIR_NAME "ts_info"
+static char *ts_info_buff;
+
+#define FT_STORE_TS_DBG_INFO(buf, id, name, max_tch, group_id, \
+			fw_vkey_support, fw_name, fw_maj, fw_min, fw_sub_min) \
+			snprintf(buf, FT_INFO_MAX_LEN, \
+				"controller\t= focaltech\n" \
+				"model\t\t= 0x%x\n" \
+				"name\t\t= %s\n" \
+				"max_touches\t= %d\n" \
+				"drv_ver\t\t= 0x%x\n" \
+				"group_id\t= 0x%x\n" \
+				"fw_vkey_support\t= %s\n" \
+				"fw_name\t\t= %s\n" \
+				"fw_ver\t\t= %d.%d.%d\n", id, name, \
+				max_tch, FT_DRIVER_VERSION, group_id, \
+				fw_vkey_support, fw_name, fw_maj, fw_min, \
+				fw_sub_min)
+
+#define FT_DEBUG_DIR_NAME	"ts_debug"
+
 struct ft5x06_ts_data {
 	struct i2c_client *client;
 	struct input_dev *input_dev;
-	struct ts_event event;
 	const struct ft5x06_ts_platform_data *pdata;
+	struct ft5x06_gesture_platform_data *gesture_pdata;
 	struct regulator *vdd;
 	struct regulator *vcc_i2c;
-#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct mutex ft_clk_io_ctrl_mutex;
+	char fw_name[FT_FW_NAME_MAX_LEN];
+	bool loading_fw;
+	u8 family_id;
+	struct dentry *dir;
+	u16 addr;
+	bool suspended;
+	char *ts_info;
+	u8 *tch_data;
+	u32 tch_data_len;
+	u8 fw_ver[3];
+	u8 fw_vendor_id;
+	struct kobject *ts_info_kobj;
+#if defined(CONFIG_FB)
+	struct work_struct fb_notify_work;
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
 	struct early_suspend early_suspend;
 #endif
+	struct pinctrl *ts_pinctrl;
+	struct pinctrl_state *pinctrl_state_active;
+	struct pinctrl_state *pinctrl_state_suspend;
+	struct pinctrl_state *pinctrl_state_release;
+#if defined(CONFIG_FT_SECURE_TOUCH)
+	atomic_t st_enabled;
+	atomic_t st_pending_irqs;
+	struct completion st_powerdown;
+	struct completion st_irq_processed;
+	bool st_initialized;
+	struct clk *core_clk;
+	struct clk *iface_clk;
+#endif
 };
 
+static int ft5x06_ts_start(struct device *dev);
+static int ft5x06_ts_stop(struct device *dev);
+
+#if defined(CONFIG_FT_SECURE_TOUCH)
+static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data)
+{
+	data->st_initialized = 0;
+
+	init_completion(&data->st_powerdown);
+	init_completion(&data->st_irq_processed);
+
+	/* Get clocks */
+	data->core_clk = devm_clk_get(&data->client->dev, "core_clk");
+	if (IS_ERR(data->core_clk)) {
+		data->core_clk = NULL;
+		dev_warn(&data->client->dev,
+			"%s: core_clk is not defined\n", __func__);
+	}
+
+	data->iface_clk = devm_clk_get(&data->client->dev, "iface_clk");
+	if (IS_ERR(data->iface_clk)) {
+		data->iface_clk = NULL;
+		dev_warn(&data->client->dev,
+			"%s: iface_clk is not defined", __func__);
+	}
+	data->st_initialized = 1;
+}
+
+static void ft5x06_secure_touch_notify(struct ft5x06_ts_data *data)
+{
+	sysfs_notify(&data->input_dev->dev.kobj, NULL, "secure_touch");
+}
+
+static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data)
+{
+	if (atomic_read(&data->st_enabled)) {
+		if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) {
+			reinit_completion(&data->st_irq_processed);
+			ft5x06_secure_touch_notify(data);
+			wait_for_completion_interruptible(
+						&data->st_irq_processed);
+		}
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+/*
+ * 'blocking' variable will have value 'true' when we want to prevent the driver
+ * from accessing the xPU/SMMU protected HW resources while the session is
+ * active.
+ */
+static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking)
+{
+	if (atomic_read(&data->st_enabled)) {
+		atomic_set(&data->st_pending_irqs, -1);
+		ft5x06_secure_touch_notify(data);
+		if (blocking)
+			wait_for_completion_interruptible(
+						&data->st_powerdown);
+	}
+}
+
+static int ft5x06_clk_prepare_enable(struct ft5x06_ts_data *data)
+{
+	int ret;
+
+	ret = clk_prepare_enable(data->iface_clk);
+	if (ret) {
+		dev_err(&data->client->dev,
+			"error on clk_prepare_enable(iface_clk):%d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(data->core_clk);
+	if (ret) {
+		clk_disable_unprepare(data->iface_clk);
+		dev_err(&data->client->dev,
+			"error clk_prepare_enable(core_clk):%d\n", ret);
+	}
+	return ret;
+}
+
+static void ft5x06_clk_disable_unprepare(struct ft5x06_ts_data *data)
+{
+	clk_disable_unprepare(data->core_clk);
+	clk_disable_unprepare(data->iface_clk);
+}
+
+static int ft5x06_bus_get(struct ft5x06_ts_data *data)
+{
+	int retval;
+
+	mutex_lock(&data->ft_clk_io_ctrl_mutex);
+	retval = pm_runtime_get_sync(data->client->adapter->dev.parent);
+	if (retval >= 0 &&  data->core_clk != NULL && data->iface_clk != NULL) {
+		retval = ft5x06_clk_prepare_enable(data);
+		if (retval)
+			pm_runtime_put_sync(data->client->adapter->dev.parent);
+	}
+	mutex_unlock(&data->ft_clk_io_ctrl_mutex);
+	return retval;
+}
+
+static void ft5x06_bus_put(struct ft5x06_ts_data *data)
+{
+	mutex_lock(&data->ft_clk_io_ctrl_mutex);
+	if (data->core_clk != NULL && data->iface_clk != NULL)
+		ft5x06_clk_disable_unprepare(data);
+	pm_runtime_put_sync(data->client->adapter->dev.parent);
+	mutex_unlock(&data->ft_clk_io_ctrl_mutex);
+}
+
+static ssize_t ft5x06_secure_touch_enable_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled));
+}
+
+/*
+ * Accept only "0" and "1" valid values.
+ * "0" will reset the st_enabled flag, then wake up the reading process and
+ * the interrupt handler.
+ * The bus driver is notified via pm_runtime that it is not required to stay
+ * awake anymore.
+ * It will also make sure the queue of events is emptied in the controller,
+ * in case a touch happened in between the secure touch being disabled and
+ * the local ISR being ungated.
+ * "1" will set the st_enabled flag and clear the st_pending_irqs flag.
+ * The bus driver is requested via pm_runtime to stay awake.
+ */
+static ssize_t ft5x06_secure_touch_enable_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	unsigned long value;
+	int err = 0;
+
+	if (count > 2)
+		return -EINVAL;
+	err = kstrtoul(buf, 10, &value);
+	if (err != 0)
+		return err;
+
+	if (!data->st_initialized)
+		return -EIO;
+
+	err = count;
+	switch (value) {
+	case 0:
+		if (atomic_read(&data->st_enabled) == 0)
+			break;
+		ft5x06_bus_put(data);
+		atomic_set(&data->st_enabled, 0);
+		ft5x06_secure_touch_notify(data);
+		complete(&data->st_irq_processed);
+		ft5x06_ts_interrupt(data->client->irq, data);
+		complete(&data->st_powerdown);
+		break;
+
+	case 1:
+		if (atomic_read(&data->st_enabled)) {
+			err = -EBUSY;
+			break;
+		}
+		synchronize_irq(data->client->irq);
+		if (ft5x06_bus_get(data) < 0) {
+			dev_err(&data->client->dev, "ft5x06_bus_get failed\n");
+			err = -EIO;
+			break;
+		}
+		reinit_completion(&data->st_powerdown);
+		reinit_completion(&data->st_irq_processed);
+		atomic_set(&data->st_enabled, 1);
+		atomic_set(&data->st_pending_irqs,  0);
+		break;
+
+	default:
+		dev_err(&data->client->dev, "unsupported value: %lu\n", value);
+		err = -EINVAL;
+		break;
+	}
+	return err;
+}
+
+/*
+ * This function returns whether there are pending interrupts, or
+ * other error conditions that need to be signaled to the userspace library,
+ * according tot he following logic:
+ * - st_enabled is 0 if secure touch is not enabled, returning -EBADF
+ * - st_pending_irqs is -1 to signal that secure touch is in being stopped,
+ *   returning -EINVAL
+ * - st_pending_irqs is 1 to signal that there is a pending irq, returning
+ *   the value "1" to the sysfs read operation
+ * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt
+ *   has been processed, so the interrupt handler can be allowed to continue.
+ */
+static ssize_t ft5x06_secure_touch_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	int val = 0;
+
+	if (atomic_read(&data->st_enabled) == 0)
+		return -EBADF;
+	if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1)
+		return -EINVAL;
+	if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1)
+		val = 1;
+	else
+		complete(&data->st_irq_processed);
+	return scnprintf(buf, PAGE_SIZE, "%u", val);
+}
+#else
+static void ft5x06_secure_touch_init(struct ft5x06_ts_data *data)
+{
+}
+static irqreturn_t ft5x06_filter_interrupt(struct ft5x06_ts_data *data)
+{
+	return IRQ_NONE;
+}
+static void ft5x06_secure_touch_stop(struct ft5x06_ts_data *data, bool blocking)
+{
+}
+#endif
+
+static struct device_attribute attrs[] = {
+#if defined(CONFIG_FT_SECURE_TOUCH)
+		__ATTR(secure_touch_enable, (0664),
+				ft5x06_secure_touch_enable_show,
+				ft5x06_secure_touch_enable_store),
+		__ATTR(secure_touch, 0444,
+				ft5x06_secure_touch_show, NULL),
+#endif
+};
+
+static inline bool ft5x06_gesture_support_enabled(void)
+{
+	return IS_ENABLED(CONFIG_TOUCHSCREEN_FT5X06_GESTURE);
+}
+
 static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
 			   int writelen, char *readbuf, int readlen)
 {
@@ -154,89 +593,411 @@
 	return ret;
 }
 
-static void ft5x06_report_value(struct ft5x06_ts_data *data)
+static int ft5x0x_write_reg(struct i2c_client *client, u8 addr, const u8 val)
 {
-	struct ts_event *event = &data->event;
-	int i;
-	int fingerdown = 0;
+	u8 buf[2] = {0};
 
-	for (i = 0; i < event->touch_point; i++) {
-		if (event->touch_event[i] == 0 || event->touch_event[i] == 2) {
-			event->pressure = FT_PRESS;
-			fingerdown++;
-		} else {
-			event->pressure = 0;
-		}
+	buf[0] = addr;
+	buf[1] = val;
 
-		input_report_abs(data->input_dev, ABS_MT_POSITION_X,
-				 event->x[i]);
-		input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
-				 event->y[i]);
-		input_report_abs(data->input_dev, ABS_MT_PRESSURE,
-				 event->pressure);
-		input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
-				 event->finger_id[i]);
-		input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
-				 event->pressure);
-		input_mt_sync(data->input_dev);
-	}
-
-	input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown);
-	input_sync(data->input_dev);
+	return ft5x06_i2c_write(client, buf, sizeof(buf));
 }
 
-static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data)
+static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val)
 {
-	struct ts_event *event = &data->event;
-	int ret, i;
-	u8 buf[POINT_READ_BUF] = { 0 };
-	u8 pointid = FT_MAX_ID;
+	return ft5x06_i2c_read(client, &addr, 1, val, 1);
+}
 
-	ret = ft5x06_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
+#ifdef CONFIG_TOUCHSCREEN_FT5X06_GESTURE
+static ssize_t ft5x06_gesture_enable_to_set_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			data->gesture_pdata->gesture_enable_to_set);
+}
+
+static ssize_t ft5x06_gesture_enable_to_set_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	unsigned long value = 0;
+	int ret;
+
+	if (data->suspended)
+		return -EINVAL;
+
+	ret = kstrtoul(buf, 16, &value);
 	if (ret < 0) {
-		dev_err(&data->client->dev, "%s read touchdata failed.\n",
+		dev_err(dev, "%s:kstrtoul failed, ret=0x%x\n",
+			__func__, ret);
+		return ret;
+	}
+
+	if (value == 1)
+		data->gesture_pdata->gesture_enable_to_set = 1;
+	else
+		data->gesture_pdata->gesture_enable_to_set = 0;
+	return size;
+}
+
+static DEVICE_ATTR(enable, 0664,
+		ft5x06_gesture_enable_to_set_show,
+		ft5x06_gesture_enable_to_set_store);
+
+static int ft5x06_entry_pocket(struct device *dev)
+{
+	return ft5x06_ts_stop(dev);
+}
+
+static int ft5x06_leave_pocket(struct device *dev)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	int err;
+
+	ft5x06_ts_start(dev);
+	ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 1);
+	err = enable_irq_wake(data->client->irq);
+	if (err)
+		dev_err(&data->client->dev,
+			"%s: set_irq_wake failed\n", __func__);
+	data->suspended = true;
+
+	return err;
+}
+
+static ssize_t gesture_in_pocket_mode_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			data->gesture_pdata->in_pocket);
+}
+
+static ssize_t gesture_in_pocket_mode_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	unsigned long value = 0;
+	int ret;
+
+	ret = kstrtoul(buf, 16, &value);
+	if (ret < 0) {
+		dev_err(dev, "%s:kstrtoul failed, ret=0x%x\n",
+			__func__, ret);
+		return ret;
+	}
+
+	if (value == 1 && data->gesture_pdata->in_pocket == 0) {
+		data->gesture_pdata->in_pocket = 1;
+		ft5x06_entry_pocket(dev);
+	} else if (value == 0 && data->gesture_pdata->in_pocket == 1) {
+		ft5x06_leave_pocket(dev);
+		data->gesture_pdata->in_pocket = 0;
+	}
+	return size;
+}
+
+static DEVICE_ATTR(pocket, 0664,
+		gesture_in_pocket_mode_show,
+		gesture_in_pocket_mode_store);
+
+static int ft5x06_report_gesture_doubleclick(struct input_dev *ip_dev)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+		input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1);
+		input_report_abs(ip_dev, ABS_MT_POSITION_X,
+					FT_GESTURE_DOUBLECLICK_COORD_X);
+		input_report_abs(ip_dev, ABS_MT_POSITION_Y,
+					FT_GESTURE_DOUBLECLICK_COORD_Y);
+		input_mt_report_pointer_emulation(ip_dev, false);
+		input_sync(ip_dev);
+		input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+		input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0);
+		input_mt_report_pointer_emulation(ip_dev, false);
+		input_sync(ip_dev);
+	}
+	return 0;
+}
+
+static int ft5x06_report_gesture(struct i2c_client *i2c_client,
+		struct input_dev *ip_dev)
+{
+	int i, temp, gesture_data_size;
+	int gesture_coord_x, gesture_coord_y;
+	int ret = -1;
+	short pointnum = 0;
+	unsigned char buf[FT_GESTURE_POINTER_NUM_MAX *
+			FT_GESTURE_POINTER_SIZEOF + FT_GESTURE_DATA_HEADER];
+
+	buf[0] = FT_REG_GESTURE_OUTPUT;
+	ret = ft5x06_i2c_read(i2c_client, buf, 1,
+				buf, FT_GESTURE_DATA_HEADER);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "%s read touchdata failed.\n",
 			__func__);
 		return ret;
 	}
-	memset(event, 0, sizeof(struct ts_event));
 
-	event->touch_point = 0;
-	for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) {
-		pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
-		if (pointid >= FT_MAX_ID)
-			break;
-
-		event->touch_point++;
-
-		event->x[i] =
-		    (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
-		    8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i];
-		event->y[i] =
-		    (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) <<
-		    8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i];
-		event->touch_event[i] =
-		    buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6;
-		event->finger_id[i] =
-		    (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4;
+	/* FW support doubleclick */
+	if (buf[0] == FT_GESTURE_DOUBLECLICK_ID) {
+		ft5x06_report_gesture_doubleclick(ip_dev);
+		return 0;
 	}
 
-	ft5x06_report_value(data);
+	pointnum = (short)(buf[1]) & 0xff;
+	gesture_data_size = pointnum * FT_GESTURE_POINTER_SIZEOF +
+			FT_GESTURE_DATA_HEADER;
+	buf[0] = FT_REG_GESTURE_OUTPUT;
+	temp = gesture_data_size / I2C_TRANSFER_MAX_BYTE;
+	for (i = 0; i < temp; i++)
+		ret = ft5x06_i2c_read(i2c_client, buf, ((i == 0) ? 1 : 0),
+			buf + I2C_TRANSFER_MAX_BYTE * i, I2C_TRANSFER_MAX_BYTE);
+	ret = ft5x06_i2c_read(i2c_client, buf, ((temp == 0) ? 1 : 0),
+			buf + I2C_TRANSFER_MAX_BYTE * temp,
+			gesture_data_size - I2C_TRANSFER_MAX_BYTE * temp);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "%s read touchdata failed.\n",
+			__func__);
+		return ret;
+	}
+
+	for (i = 0; i < pointnum; i++) {
+		gesture_coord_x = (((s16) buf[FT_GESTURE_DATA_HEADER +
+				(FT_GESTURE_POINTER_SIZEOF * i)]) & 0x0F) << 8 |
+				(((s16) buf[FT_GESTURE_DATA_HEADER + 1 +
+				(FT_GESTURE_POINTER_SIZEOF * i)]) & 0xFF);
+		gesture_coord_y = (((s16) buf[FT_GESTURE_DATA_HEADER + 2 +
+				(FT_GESTURE_POINTER_SIZEOF * i)]) & 0x0F) << 8 |
+				(((s16) buf[FT_GESTURE_DATA_HEADER + 3 +
+				(FT_GESTURE_POINTER_SIZEOF * i)]) & 0xFF);
+		input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+		input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1);
+		input_report_abs(ip_dev, ABS_MT_POSITION_X, gesture_coord_x);
+		input_report_abs(ip_dev, ABS_MT_POSITION_Y, gesture_coord_y);
+		input_mt_report_pointer_emulation(ip_dev, false);
+		input_sync(ip_dev);
+	}
+	input_mt_slot(ip_dev, FT_GESTURE_DEFAULT_TRACKING_ID);
+	input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0);
+	input_mt_report_pointer_emulation(ip_dev, false);
+	input_sync(ip_dev);
 
 	return 0;
 }
+#else
+static DEVICE_ATTR(pocket, 0664, NULL, NULL);
+static DEVICE_ATTR(enable, 0664, NULL, NULL);
+
+static int ft5x06_report_gesture(struct i2c_client *i2c_client,
+		struct input_dev *ip_dev)
+{
+	return 0;
+}
+#endif
+
+static void ft5x06_update_fw_vendor_id(struct ft5x06_ts_data *data)
+{
+	struct i2c_client *client = data->client;
+	u8 reg_addr;
+	int err;
+
+	reg_addr = FT_REG_FW_VENDOR_ID;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_vendor_id, 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw vendor id read failed");
+}
+
+static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data)
+{
+	struct i2c_client *client = data->client;
+	u8 reg_addr;
+	int err;
+
+	reg_addr = FT_REG_FW_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_ver[0], 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw major version read failed");
+
+	reg_addr = FT_REG_FW_MIN_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_ver[1], 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw minor version read failed");
+
+	reg_addr = FT_REG_FW_SUB_MIN_VER;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &data->fw_ver[2], 1);
+	if (err < 0)
+		dev_err(&client->dev, "fw sub minor version read failed");
+
+	dev_info(&client->dev, "Firmware version = %d.%d.%d\n",
+		data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]);
+}
 
 static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id)
 {
 	struct ft5x06_ts_data *data = dev_id;
-	int rc;
+	struct input_dev *ip_dev;
+	int rc, i;
+	u32 id, x, y, status, num_touches;
+	u8 reg, *buf, gesture_is_active;
+	bool update_input = false;
 
-	rc = ft5x06_handle_touchdata(data);
-	if (rc)
-		pr_err("%s: handling touchdata failed\n", __func__);
+	if (!data) {
+		pr_err("%s: Invalid data\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	if (ft5x06_filter_interrupt(data) == IRQ_HANDLED)
+		return IRQ_HANDLED;
+
+	ip_dev = data->input_dev;
+	buf = data->tch_data;
+
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
+		ft5x0x_read_reg(data->client, FT_REG_GESTURE_ENABLE,
+					&gesture_is_active);
+		if (gesture_is_active) {
+			pm_wakeup_event(&(data->client->dev),
+					FT_GESTURE_WAKEUP_TIMEOUT);
+			ft5x06_report_gesture(data->client, ip_dev);
+			return IRQ_HANDLED;
+		}
+	}
+
+	/*
+	 * Read touch data start from register FT_REG_DEV_MODE.
+	 * The touch x/y value start from FT_TOUCH_X_H/L_POS and
+	 * FT_TOUCH_Y_H/L_POS in buf.
+	 */
+	reg = FT_REG_DEV_MODE;
+	rc = ft5x06_i2c_read(data->client, &reg, 1, buf, data->tch_data_len);
+	if (rc < 0) {
+		dev_err(&data->client->dev, "%s: read data fail\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	for (i = 0; i < data->pdata->num_max_touches; i++) {
+		/*
+		 * Getting the finger ID of the touch event incase of
+		 * multiple touch events
+		 */
+		id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4;
+		if (id >= FT_MAX_ID)
+			break;
+
+		update_input = true;
+
+		x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 |
+			(buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]);
+		y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 |
+			(buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]);
+
+		status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6;
+
+		num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK;
+
+		/* invalid combination */
+		if (!num_touches && !status && !id)
+			break;
+
+		input_mt_slot(ip_dev, id);
+		if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) {
+			input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1);
+			input_report_abs(ip_dev, ABS_MT_POSITION_X, x);
+			input_report_abs(ip_dev, ABS_MT_POSITION_Y, y);
+		} else {
+			input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0);
+		}
+	}
+
+	if (update_input) {
+		input_mt_report_pointer_emulation(ip_dev, false);
+		input_sync(ip_dev);
+	}
 
 	return IRQ_HANDLED;
 }
 
+static int ft5x06_gpio_configure(struct ft5x06_ts_data *data, bool on)
+{
+	int err = 0;
+
+	if (on) {
+		if (gpio_is_valid(data->pdata->irq_gpio)) {
+			err = gpio_request(data->pdata->irq_gpio,
+						"ft5x06_irq_gpio");
+			if (err) {
+				dev_err(&data->client->dev,
+					"irq gpio request failed");
+				goto err_irq_gpio_req;
+			}
+
+			err = gpio_direction_input(data->pdata->irq_gpio);
+			if (err) {
+				dev_err(&data->client->dev,
+					"set_direction for irq gpio failed\n");
+				goto err_irq_gpio_dir;
+			}
+		}
+
+		if (gpio_is_valid(data->pdata->reset_gpio)) {
+			err = gpio_request(data->pdata->reset_gpio,
+						"ft5x06_reset_gpio");
+			if (err) {
+				dev_err(&data->client->dev,
+					"reset gpio request failed");
+				goto err_irq_gpio_dir;
+			}
+
+			err = gpio_direction_output(data->pdata->reset_gpio, 0);
+			if (err) {
+				dev_err(&data->client->dev,
+				"set_direction for reset gpio failed\n");
+				goto err_reset_gpio_dir;
+			}
+			msleep(data->pdata->hard_rst_dly);
+			gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+		}
+
+		return 0;
+	}
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->irq_gpio);
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		/*
+		 * This is intended to save leakage current
+		 * only. Even if the call(gpio_direction_input)
+		 * fails, only leakage current will be more but
+		 * functionality will not be affected.
+		 */
+		err = gpio_direction_input(data->pdata->reset_gpio);
+		if (err) {
+			dev_err(&data->client->dev,
+				"unable to set direction for gpio [%d]\n",
+				data->pdata->irq_gpio);
+		}
+		gpio_free(data->pdata->reset_gpio);
+	}
+
+		return 0;
+
+err_reset_gpio_dir:
+	if (gpio_is_valid(data->pdata->reset_gpio))
+		gpio_free(data->pdata->reset_gpio);
+err_irq_gpio_dir:
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->irq_gpio);
+err_irq_gpio_req:
+	return err;
+}
+
 static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on)
 {
 	int rc;
@@ -272,7 +1033,11 @@
 	if (rc) {
 		dev_err(&data->client->dev,
 			"Regulator vcc_i2c disable failed rc=%d\n", rc);
-		regulator_enable(data->vdd);
+		rc = regulator_enable(data->vdd);
+		if (rc) {
+			dev_err(&data->client->dev,
+				"Regulator vdd enable failed rc=%d\n", rc);
+		}
 	}
 
 	return rc;
@@ -294,8 +1059,8 @@
 	}
 
 	if (regulator_count_voltages(data->vdd) > 0) {
-		rc = regulator_set_voltage(data->vdd, FT5X06_VTG_MIN_UV,
-					   FT5X06_VTG_MAX_UV);
+		rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV,
+					   FT_VTG_MAX_UV);
 		if (rc) {
 			dev_err(&data->client->dev,
 				"Regulator set_vtg failed vdd rc=%d\n", rc);
@@ -312,8 +1077,8 @@
 	}
 
 	if (regulator_count_voltages(data->vcc_i2c) > 0) {
-		rc = regulator_set_voltage(data->vcc_i2c, FT5X06_I2C_VTG_MIN_UV,
-					   FT5X06_I2C_VTG_MAX_UV);
+		rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV,
+					   FT_I2C_VTG_MAX_UV);
 		if (rc) {
 			dev_err(&data->client->dev,
 			"Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
@@ -327,62 +1092,378 @@
 	regulator_put(data->vcc_i2c);
 reg_vdd_set_vtg:
 	if (regulator_count_voltages(data->vdd) > 0)
-		regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+		regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV);
 reg_vdd_put:
 	regulator_put(data->vdd);
 	return rc;
 
 pwr_deinit:
 	if (regulator_count_voltages(data->vdd) > 0)
-		regulator_set_voltage(data->vdd, 0, FT5X06_VTG_MAX_UV);
+		regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV);
 
 	regulator_put(data->vdd);
 
 	if (regulator_count_voltages(data->vcc_i2c) > 0)
-		regulator_set_voltage(data->vcc_i2c, 0, FT5X06_I2C_VTG_MAX_UV);
+		regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV);
 
 	regulator_put(data->vcc_i2c);
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int ft5x06_ts_suspend(struct device *dev)
+static int ft5x06_ts_pinctrl_init(struct ft5x06_ts_data *ft5x06_data)
 {
-	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
-	char txbuf[2];
+	int retval;
 
-	disable_irq(data->client->irq);
+	/* Get pinctrl if target uses pinctrl */
+	ft5x06_data->ts_pinctrl = devm_pinctrl_get(&(ft5x06_data->client->dev));
+	if (IS_ERR_OR_NULL(ft5x06_data->ts_pinctrl)) {
+		retval = PTR_ERR(ft5x06_data->ts_pinctrl);
+		dev_dbg(&ft5x06_data->client->dev,
+			"Target does not use pinctrl %d\n", retval);
+		goto err_pinctrl_get;
+	}
 
-	if (gpio_is_valid(data->pdata->reset_gpio)) {
-		txbuf[0] = FT5X06_REG_PMODE;
-		txbuf[1] = FT5X06_PMODE_HIBERNATE;
-		ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
+	ft5x06_data->pinctrl_state_active
+		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
+				PINCTRL_STATE_ACTIVE);
+	if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_active)) {
+		retval = PTR_ERR(ft5x06_data->pinctrl_state_active);
+		dev_err(&ft5x06_data->client->dev,
+			"Can not lookup %s pinstate %d\n",
+			PINCTRL_STATE_ACTIVE, retval);
+		goto err_pinctrl_lookup;
+	}
+
+	ft5x06_data->pinctrl_state_suspend
+		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
+			PINCTRL_STATE_SUSPEND);
+	if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_suspend)) {
+		retval = PTR_ERR(ft5x06_data->pinctrl_state_suspend);
+		dev_err(&ft5x06_data->client->dev,
+			"Can not lookup %s pinstate %d\n",
+			PINCTRL_STATE_SUSPEND, retval);
+		goto err_pinctrl_lookup;
+	}
+
+	ft5x06_data->pinctrl_state_release
+		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
+			PINCTRL_STATE_RELEASE);
+	if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_release)) {
+		retval = PTR_ERR(ft5x06_data->pinctrl_state_release);
+		dev_dbg(&ft5x06_data->client->dev,
+			"Can not lookup %s pinstate %d\n",
+			PINCTRL_STATE_RELEASE, retval);
 	}
 
 	return 0;
+
+err_pinctrl_lookup:
+	devm_pinctrl_put(ft5x06_data->ts_pinctrl);
+err_pinctrl_get:
+	ft5x06_data->ts_pinctrl = NULL;
+	return retval;
+}
+
+#ifdef CONFIG_PM
+static int ft5x06_ts_start(struct device *dev)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	int err;
+
+	if (data->pdata->power_on) {
+		err = data->pdata->power_on(true);
+		if (err) {
+			dev_err(dev, "power on failed");
+			return err;
+		}
+	} else {
+		err = ft5x06_power_on(data, true);
+		if (err) {
+			dev_err(dev, "power on failed");
+			return err;
+		}
+	}
+
+	if (data->ts_pinctrl) {
+		err = pinctrl_select_state(data->ts_pinctrl,
+				data->pinctrl_state_active);
+		if (err < 0)
+			dev_err(dev, "Cannot get active pinctrl state\n");
+	}
+
+	err = ft5x06_gpio_configure(data, true);
+	if (err < 0) {
+		dev_err(&data->client->dev,
+			"failed to put gpios in resue state\n");
+		goto err_gpio_configuration;
+	}
+
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+		msleep(data->pdata->hard_rst_dly);
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+	}
+
+	msleep(data->pdata->soft_rst_dly);
+
+	enable_irq(data->client->irq);
+	data->suspended = false;
+
+	return 0;
+
+err_gpio_configuration:
+	if (data->ts_pinctrl) {
+		err = pinctrl_select_state(data->ts_pinctrl,
+					data->pinctrl_state_suspend);
+		if (err < 0)
+			dev_err(dev, "Cannot get suspend pinctrl state\n");
+	}
+	if (data->pdata->power_on) {
+		err = data->pdata->power_on(false);
+		if (err)
+			dev_err(dev, "power off failed");
+	} else {
+		err = ft5x06_power_on(data, false);
+		if (err)
+			dev_err(dev, "power off failed");
+	}
+	return err;
+}
+
+static int ft5x06_ts_stop(struct device *dev)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	char txbuf[2];
+	int i, err;
+
+	disable_irq(data->client->irq);
+
+	/* release all touches */
+	for (i = 0; i < data->pdata->num_max_touches; i++) {
+		input_mt_slot(data->input_dev, i);
+		input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0);
+	}
+	input_mt_report_pointer_emulation(data->input_dev, false);
+	input_sync(data->input_dev);
+
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		txbuf[0] = FT_REG_PMODE;
+		txbuf[1] = FT_PMODE_HIBERNATE;
+		ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf));
+	}
+
+	if (data->pdata->power_on) {
+		err = data->pdata->power_on(false);
+		if (err) {
+			dev_err(dev, "power off failed");
+			goto pwr_off_fail;
+		}
+	} else {
+		err = ft5x06_power_on(data, false);
+		if (err) {
+			dev_err(dev, "power off failed");
+			goto pwr_off_fail;
+		}
+	}
+
+	if (data->ts_pinctrl) {
+		err = pinctrl_select_state(data->ts_pinctrl,
+					data->pinctrl_state_suspend);
+		if (err < 0)
+			dev_err(dev, "Cannot get suspend pinctrl state\n");
+	}
+
+	err = ft5x06_gpio_configure(data, false);
+	if (err < 0) {
+		dev_err(&data->client->dev,
+			"failed to put gpios in suspend state\n");
+		goto gpio_configure_fail;
+	}
+
+	data->suspended = true;
+
+	return 0;
+
+gpio_configure_fail:
+	if (data->ts_pinctrl) {
+		err = pinctrl_select_state(data->ts_pinctrl,
+					data->pinctrl_state_active);
+		if (err < 0)
+			dev_err(dev, "Cannot get active pinctrl state\n");
+	}
+	if (data->pdata->power_on) {
+		err = data->pdata->power_on(true);
+		if (err)
+			dev_err(dev, "power on failed");
+	} else {
+		err = ft5x06_power_on(data, true);
+		if (err)
+			dev_err(dev, "power on failed");
+	}
+pwr_off_fail:
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
+		msleep(data->pdata->hard_rst_dly);
+		gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+	}
+	enable_irq(data->client->irq);
+	return err;
+}
+
+static int ft5x06_ts_suspend(struct device *dev)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	int err;
+
+	if (data->loading_fw) {
+		dev_info(dev, "Firmware loading in process...\n");
+		return 0;
+	}
+
+	if (data->suspended) {
+		dev_info(dev, "Already in suspend state\n");
+		return 0;
+	}
+
+	ft5x06_secure_touch_stop(data, true);
+
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support &&
+		device_may_wakeup(dev) &&
+		data->gesture_pdata->gesture_enable_to_set) {
+
+		ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 1);
+		err = enable_irq_wake(data->client->irq);
+		if (err)
+			dev_err(&data->client->dev,
+				"%s: set_irq_wake failed\n", __func__);
+		data->suspended = true;
+		return err;
+	}
+
+	return ft5x06_ts_stop(dev);
 }
 
 static int ft5x06_ts_resume(struct device *dev)
 {
 	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	int err;
 
-	if (gpio_is_valid(data->pdata->reset_gpio)) {
-		gpio_set_value_cansleep(data->pdata->reset_gpio, 0);
-		msleep(FT_RESET_DLY);
-		gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+	if (!data->suspended) {
+		dev_dbg(dev, "Already in awake state\n");
+		return 0;
 	}
-	enable_irq(data->client->irq);
 
+	ft5x06_secure_touch_stop(data, true);
+
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support &&
+		device_may_wakeup(dev) &&
+		!(data->gesture_pdata->in_pocket) &&
+		data->gesture_pdata->gesture_enable_to_set) {
+
+		ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 0);
+		err = disable_irq_wake(data->client->irq);
+		if (err)
+			dev_err(dev, "%s: disable_irq_wake failed\n",
+				__func__);
+		data->suspended = false;
+		return err;
+	}
+
+	err = ft5x06_ts_start(dev);
+	if (err < 0)
+		return err;
+
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support &&
+		device_may_wakeup(dev) &&
+		data->gesture_pdata->in_pocket &&
+		data->gesture_pdata->gesture_enable_to_set) {
+
+		ft5x0x_write_reg(data->client, FT_REG_GESTURE_ENABLE, 0);
+		err = disable_irq_wake(data->client->irq);
+		if (err)
+			dev_err(dev, "%s: disable_irq_wake failed\n",
+				__func__);
+		data->suspended = false;
+		data->gesture_pdata->in_pocket = 0;
+	}
 	return 0;
 }
 
-#ifdef CONFIG_HAS_EARLYSUSPEND
+static const struct dev_pm_ops ft5x06_ts_pm_ops = {
+#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
+	.suspend = ft5x06_ts_suspend,
+	.resume = ft5x06_ts_resume,
+#endif
+};
+
+#else
+static int ft5x06_ts_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ft5x06_ts_resume(struct device *dev)
+{
+	return 0;
+}
+
+#endif
+
+#if defined(CONFIG_FB)
+static void fb_notify_resume_work(struct work_struct *work)
+{
+	struct ft5x06_ts_data *ft5x06_data =
+		container_of(work, struct ft5x06_ts_data, fb_notify_work);
+	ft5x06_ts_resume(&ft5x06_data->client->dev);
+}
+
+static int fb_notifier_callback(struct notifier_block *self,
+				 unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+	struct ft5x06_ts_data *ft5x06_data =
+		container_of(self, struct ft5x06_ts_data, fb_notif);
+
+	if (evdata && evdata->data && ft5x06_data && ft5x06_data->client) {
+		blank = evdata->data;
+		if (ft5x06_data->pdata->resume_in_workqueue) {
+			if (event == FB_EARLY_EVENT_BLANK &&
+						 *blank == FB_BLANK_UNBLANK)
+				schedule_work(&ft5x06_data->fb_notify_work);
+			else if (event == FB_EVENT_BLANK &&
+						 *blank == FB_BLANK_POWERDOWN) {
+				flush_work(&ft5x06_data->fb_notify_work);
+				ft5x06_ts_suspend(&ft5x06_data->client->dev);
+			}
+		} else {
+			if (event == FB_EVENT_BLANK) {
+				if (*blank == FB_BLANK_UNBLANK)
+					ft5x06_ts_resume(
+						&ft5x06_data->client->dev);
+				else if (*blank == FB_BLANK_POWERDOWN)
+					ft5x06_ts_suspend(
+						&ft5x06_data->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
 static void ft5x06_ts_early_suspend(struct early_suspend *handler)
 {
 	struct ft5x06_ts_data *data = container_of(handler,
 						   struct ft5x06_ts_data,
 						   early_suspend);
 
+	/*
+	 * During early suspend/late resume, the driver doesn't access xPU/SMMU
+	 * protected HW resources. So, there is no compelling need to block,
+	 * but notifying the userspace that a power event has occurred is
+	 * enough. Hence 'blocking' variable can be set to false.
+	 */
+	ft5x06_secure_touch_stop(data, false);
 	ft5x06_ts_suspend(&data->client->dev);
 }
 
@@ -392,23 +1473,577 @@
 						   struct ft5x06_ts_data,
 						   early_suspend);
 
+	ft5x06_secure_touch_stop(data, false);
 	ft5x06_ts_resume(&data->client->dev);
 }
 #endif
 
-static const struct dev_pm_ops ft5x06_ts_pm_ops = {
-#ifndef CONFIG_HAS_EARLYSUSPEND
-	.suspend = ft5x06_ts_suspend,
-	.resume = ft5x06_ts_resume,
-#endif
+static int ft5x06_auto_cal(struct i2c_client *client)
+{
+	struct ft5x06_ts_data *data = i2c_get_clientdata(client);
+	u8 temp = 0, i;
+
+	/* set to factory mode */
+	msleep(2 * data->pdata->soft_rst_dly);
+	ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
+	msleep(data->pdata->soft_rst_dly);
+
+	/* start calibration */
+	ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START);
+	msleep(2 * data->pdata->soft_rst_dly);
+	for (i = 0; i < FT_CAL_RETRY; i++) {
+		ft5x0x_read_reg(client, FT_REG_CAL, &temp);
+		/* return to normal mode, calibration finish */
+		if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN)
+			break;
+	}
+
+	/*calibration OK */
+	msleep(2 * data->pdata->soft_rst_dly);
+	ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE);
+	msleep(data->pdata->soft_rst_dly);
+
+	/* store calibration data */
+	ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE);
+	msleep(2 * data->pdata->soft_rst_dly);
+
+	/* set to normal mode */
+	ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE);
+	msleep(2 * data->pdata->soft_rst_dly);
+
+	return 0;
+}
+
+static int ft5x06_fw_upgrade_start(struct i2c_client *client,
+			const u8 *data, u32 data_len)
+{
+	struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client);
+	struct fw_upgrade_info info = ts_data->pdata->info;
+	u8 reset_reg;
+	u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0};
+	u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN];
+	int i, j, temp;
+	u32 pkt_num, pkt_len;
+	u8 is_5336_new_bootloader = false;
+	u8 is_5336_fwsize_30 = false;
+	u8 fw_ecc;
+
+	/* determine firmware size */
+	if (*(data + data_len - FT_BLOADER_SIZE_OFF) == FT_BLOADER_NEW_SIZE)
+		is_5336_fwsize_30 = true;
+	else
+		is_5336_fwsize_30 = false;
+
+	for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) {
+		msleep(FT_EARSE_DLY_MS);
+		/* reset - write 0xaa and 0x55 to reset register */
+		if (ts_data->family_id == FT6X06_ID
+			|| ts_data->family_id == FT6X36_ID)
+			reset_reg = FT_RST_CMD_REG2;
+		else
+			reset_reg = FT_RST_CMD_REG1;
+
+		ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_AA);
+		msleep(info.delay_aa);
+
+		ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_55);
+		if (i <= (FT_UPGRADE_LOOP / 2))
+			msleep(info.delay_55 + i * 3);
+		else
+			msleep(info.delay_55 - (i - (FT_UPGRADE_LOOP / 2)) * 2);
+
+		/* Enter upgrade mode */
+		w_buf[0] = FT_UPGRADE_55;
+		ft5x06_i2c_write(client, w_buf, 1);
+		usleep_range(FT_55_AA_DLY_NS, FT_55_AA_DLY_NS + 1);
+		w_buf[0] = FT_UPGRADE_AA;
+		ft5x06_i2c_write(client, w_buf, 1);
+
+		/* check READ_ID */
+		msleep(info.delay_readid);
+		w_buf[0] = FT_READ_ID_REG;
+		w_buf[1] = 0x00;
+		w_buf[2] = 0x00;
+		w_buf[3] = 0x00;
+
+		ft5x06_i2c_read(client, w_buf, 4, r_buf, 2);
+
+		if (r_buf[0] != info.upgrade_id_1
+			|| r_buf[1] != info.upgrade_id_2) {
+			dev_err(&client->dev, "Upgrade ID mismatch(%d), IC=0x%x 0x%x, info=0x%x 0x%x\n",
+				i, r_buf[0], r_buf[1],
+				info.upgrade_id_1, info.upgrade_id_2);
+		} else
+			break;
+	}
+
+	if (i >= FT_UPGRADE_LOOP) {
+		dev_err(&client->dev, "Abort upgrade\n");
+		return -EIO;
+	}
+
+	w_buf[0] = 0xcd;
+	ft5x06_i2c_read(client, w_buf, 1, r_buf, 1);
+
+	if (r_buf[0] <= 4)
+		is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4;
+	else if (r_buf[0] == 7)
+		is_5336_new_bootloader = FT_BLOADER_VERSION_Z7;
+	else if (r_buf[0] >= 0x0f &&
+		((ts_data->family_id == FT_FT5336_FAMILY_ID_0x11) ||
+		(ts_data->family_id == FT_FT5336_FAMILY_ID_0x12) ||
+		(ts_data->family_id == FT_FT5336_FAMILY_ID_0x13) ||
+		(ts_data->family_id == FT_FT5336_FAMILY_ID_0x14)))
+		is_5336_new_bootloader = FT_BLOADER_VERSION_GZF;
+	else
+		is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4;
+
+	dev_dbg(&client->dev, "bootloader type=%d, r_buf=0x%x, family_id=0x%x\n",
+		is_5336_new_bootloader, r_buf[0], ts_data->family_id);
+	/* is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; */
+
+	/* erase app and panel paramenter area */
+	w_buf[0] = FT_ERASE_APP_REG;
+	ft5x06_i2c_write(client, w_buf, 1);
+	msleep(info.delay_erase_flash);
+
+	if (is_5336_fwsize_30) {
+		w_buf[0] = FT_ERASE_PANEL_REG;
+		ft5x06_i2c_write(client, w_buf, 1);
+	}
+	msleep(FT_EARSE_DLY_MS);
+
+	/* program firmware */
+	if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4
+		|| is_5336_new_bootloader == FT_BLOADER_VERSION_Z7)
+		data_len = data_len - FT_DATA_LEN_OFF_OLD_FW;
+	else
+		data_len = data_len - FT_DATA_LEN_OFF_NEW_FW;
+
+	pkt_num = (data_len) / FT_FW_PKT_LEN;
+	pkt_len = FT_FW_PKT_LEN;
+	pkt_buf[0] = FT_FW_START_REG;
+	pkt_buf[1] = 0x00;
+	fw_ecc = 0;
+
+	for (i = 0; i < pkt_num; i++) {
+		temp = i * FT_FW_PKT_LEN;
+		pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT);
+		pkt_buf[3] = (u8) temp;
+		pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT);
+		pkt_buf[5] = (u8) pkt_len;
+
+		for (j = 0; j < FT_FW_PKT_LEN; j++) {
+			pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j];
+			fw_ecc ^= pkt_buf[6 + j];
+		}
+
+		ft5x06_i2c_write(client, pkt_buf,
+				FT_FW_PKT_LEN + FT_FW_PKT_META_LEN);
+		msleep(FT_FW_PKT_DLY_MS);
+	}
+
+	/* send remaining bytes */
+	if ((data_len) % FT_FW_PKT_LEN > 0) {
+		temp = pkt_num * FT_FW_PKT_LEN;
+		pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT);
+		pkt_buf[3] = (u8) temp;
+		temp = (data_len) % FT_FW_PKT_LEN;
+		pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT);
+		pkt_buf[5] = (u8) temp;
+
+		for (i = 0; i < temp; i++) {
+			pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i];
+			fw_ecc ^= pkt_buf[6 + i];
+		}
+
+		ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN);
+		msleep(FT_FW_PKT_DLY_MS);
+	}
+
+	/* send the finishing packet */
+	if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 ||
+		is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) {
+		for (i = 0; i < FT_FINISHING_PKT_LEN_OLD_FW; i++) {
+			if (is_5336_new_bootloader  == FT_BLOADER_VERSION_Z7)
+				temp = FT_MAGIC_BLOADER_Z7 + i;
+			else if (is_5336_new_bootloader ==
+						FT_BLOADER_VERSION_LZ4)
+				temp = FT_MAGIC_BLOADER_LZ4 + i;
+			pkt_buf[2] = (u8)(temp >> 8);
+			pkt_buf[3] = (u8)temp;
+			temp = 1;
+			pkt_buf[4] = (u8)(temp >> 8);
+			pkt_buf[5] = (u8)temp;
+			pkt_buf[6] = data[data_len + i];
+			fw_ecc ^= pkt_buf[6];
+
+			ft5x06_i2c_write(client,
+				pkt_buf, temp + FT_FW_PKT_META_LEN);
+			msleep(FT_FW_PKT_DLY_MS);
+		}
+	} else if (is_5336_new_bootloader == FT_BLOADER_VERSION_GZF) {
+		for (i = 0; i < FT_FINISHING_PKT_LEN_NEW_FW; i++) {
+			if (is_5336_fwsize_30)
+				temp = FT_MAGIC_BLOADER_GZF_30 + i;
+			else
+				temp = FT_MAGIC_BLOADER_GZF + i;
+			pkt_buf[2] = (u8)(temp >> 8);
+			pkt_buf[3] = (u8)temp;
+			temp = 1;
+			pkt_buf[4] = (u8)(temp >> 8);
+			pkt_buf[5] = (u8)temp;
+			pkt_buf[6] = data[data_len + i];
+			fw_ecc ^= pkt_buf[6];
+
+			ft5x06_i2c_write(client,
+				pkt_buf, temp + FT_FW_PKT_META_LEN);
+			msleep(FT_FW_PKT_DLY_MS);
+
+		}
+	}
+
+	/* verify checksum */
+	w_buf[0] = FT_REG_ECC;
+	ft5x06_i2c_read(client, w_buf, 1, r_buf, 1);
+	if (r_buf[0] != fw_ecc) {
+		dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n",
+					r_buf[0], fw_ecc);
+		return -EIO;
+	}
+
+	/* reset */
+	w_buf[0] = FT_REG_RESET_FW;
+	ft5x06_i2c_write(client, w_buf, 1);
+	msleep(ts_data->pdata->soft_rst_dly);
+
+	dev_info(&client->dev, "Firmware upgrade successful\n");
+
+	return 0;
+}
+
+static int ft5x06_fw_upgrade(struct device *dev, bool force)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	const struct firmware *fw = NULL;
+	int rc;
+	u8 fw_file_maj, fw_file_min, fw_file_sub_min, fw_file_vendor_id;
+	bool fw_upgrade = false;
+
+	if (data->suspended) {
+		dev_err(dev, "Device is in suspend state: Exit FW upgrade\n");
+		return -EBUSY;
+	}
+
+	rc = request_firmware(&fw, data->fw_name, dev);
+	if (rc < 0) {
+		dev_err(dev, "Request firmware failed - %s (%d)\n",
+						data->fw_name, rc);
+		return rc;
+	}
+
+	if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) {
+		dev_err(dev, "Invalid firmware size (%zu)\n", fw->size);
+		rc = -EIO;
+		goto rel_fw;
+	}
+
+	if (data->family_id == FT6X36_ID) {
+		fw_file_maj = FT_FW_FILE_MAJ_VER_FT6X36(fw);
+		fw_file_vendor_id = FT_FW_FILE_VENDOR_ID_FT6X36(fw);
+	} else {
+		fw_file_maj = FT_FW_FILE_MAJ_VER(fw);
+		fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw);
+	}
+	fw_file_min = FT_FW_FILE_MIN_VER(fw);
+	fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw);
+	fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw);
+
+	dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0],
+				data->fw_ver[1], data->fw_ver[2]);
+	dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj,
+				fw_file_min, fw_file_sub_min);
+
+	if (force)
+		fw_upgrade = true;
+	else if ((data->fw_ver[0] < fw_file_maj) &&
+		data->fw_vendor_id == fw_file_vendor_id)
+		fw_upgrade = true;
+
+	if (!fw_upgrade) {
+		dev_info(dev, "Exiting fw upgrade...\n");
+		rc = -EFAULT;
+		goto rel_fw;
+	}
+
+	/* start firmware upgrade */
+	if (FT_FW_CHECK(fw, data)) {
+		rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size);
+		if (rc < 0)
+			dev_err(dev, "update failed (%d). try later...\n", rc);
+		else if (data->pdata->info.auto_cal)
+			ft5x06_auto_cal(data->client);
+	} else {
+		dev_err(dev, "FW format error\n");
+		rc = -EIO;
+	}
+
+	ft5x06_update_fw_ver(data);
+
+	FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name,
+			data->pdata->num_max_touches, data->pdata->group_id,
+			data->pdata->fw_vkey_support ? "yes" : "no",
+			data->pdata->fw_name, data->fw_ver[0],
+			data->fw_ver[1], data->fw_ver[2]);
+	FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0],
+			 data->fw_ver[1], data->fw_ver[2]);
+rel_fw:
+	release_firmware(fw);
+	return rc;
+}
+
+static ssize_t ft5x06_update_fw_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	return snprintf(buf, 2, "%d\n", data->loading_fw);
+}
+
+static ssize_t ft5x06_update_fw_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	unsigned long val;
+	int rc;
+
+	if (size > 2)
+		return -EINVAL;
+
+	rc = kstrtoul(buf, 10, &val);
+	if (rc != 0)
+		return rc;
+
+	if (data->suspended) {
+		dev_info(dev, "In suspend state, try again later...\n");
+		return size;
+	}
+
+	mutex_lock(&data->input_dev->mutex);
+	if (!data->loading_fw  && val) {
+		data->loading_fw = true;
+		ft5x06_fw_upgrade(dev, false);
+		data->loading_fw = false;
+	}
+	mutex_unlock(&data->input_dev->mutex);
+
+	return size;
+}
+
+static DEVICE_ATTR(update_fw, 0664, ft5x06_update_fw_show,
+				ft5x06_update_fw_store);
+
+static ssize_t ft5x06_force_update_fw_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+	unsigned long val;
+	int rc;
+
+	if (size > 2)
+		return -EINVAL;
+
+	rc = kstrtoul(buf, 10, &val);
+	if (rc != 0)
+		return rc;
+
+	mutex_lock(&data->input_dev->mutex);
+	if (!data->loading_fw  && val) {
+		data->loading_fw = true;
+		ft5x06_fw_upgrade(dev, true);
+		data->loading_fw = false;
+	}
+	mutex_unlock(&data->input_dev->mutex);
+
+	return size;
+}
+
+static DEVICE_ATTR(force_update_fw, 0664, ft5x06_update_fw_show,
+				ft5x06_force_update_fw_store);
+
+static ssize_t ft5x06_fw_name_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	return snprintf(buf, FT_FW_NAME_MAX_LEN - 1, "%s\n", data->fw_name);
+}
+
+static ssize_t ft5x06_fw_name_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct ft5x06_ts_data *data = dev_get_drvdata(dev);
+
+	if (size > FT_FW_NAME_MAX_LEN - 1)
+		return -EINVAL;
+
+	strlcpy(data->fw_name, buf, size);
+	if (data->fw_name[size-1] == '\n')
+		data->fw_name[size-1] = 0;
+
+	return size;
+}
+
+static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store);
+
+static ssize_t ts_info_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	strlcpy(buf, ts_info_buff, FT_INFO_MAX_LEN);
+	return strnlen(buf, FT_INFO_MAX_LEN);
+}
+static struct kobj_attribute ts_info_attr = __ATTR_RO(ts_info);
+
+static bool ft5x06_debug_addr_is_valid(int addr)
+{
+	if (addr < 0 || addr > 0xFF) {
+		pr_err("FT reg address is invalid: 0x%x\n", addr);
+		return false;
+	}
+
+	return true;
+}
+
+static int ft5x06_debug_data_set(void *_data, u64 val)
+{
+	struct ft5x06_ts_data *data = _data;
+
+	mutex_lock(&data->input_dev->mutex);
+
+	if (ft5x06_debug_addr_is_valid(data->addr))
+		dev_info(&data->client->dev,
+			"Writing into FT registers not supported\n");
+
+	mutex_unlock(&data->input_dev->mutex);
+
+	return 0;
+}
+
+static int ft5x06_debug_data_get(void *_data, u64 *val)
+{
+	struct ft5x06_ts_data *data = _data;
+	int rc;
+	u8 reg = 0;
+
+	mutex_lock(&data->input_dev->mutex);
+
+	if (ft5x06_debug_addr_is_valid(data->addr)) {
+		rc = ft5x0x_read_reg(data->client, data->addr, &reg);
+		if (rc < 0)
+			dev_err(&data->client->dev,
+				"FT read register 0x%x failed (%d)\n",
+				data->addr, rc);
+		else
+			*val = reg;
+	}
+
+	mutex_unlock(&data->input_dev->mutex);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft5x06_debug_data_get,
+			ft5x06_debug_data_set, "0x%02llX\n");
+
+static int ft5x06_debug_addr_set(void *_data, u64 val)
+{
+	struct ft5x06_ts_data *data = _data;
+
+	if (ft5x06_debug_addr_is_valid(val)) {
+		mutex_lock(&data->input_dev->mutex);
+		data->addr = val;
+		mutex_unlock(&data->input_dev->mutex);
+	}
+
+	return 0;
+}
+
+static int ft5x06_debug_addr_get(void *_data, u64 *val)
+{
+	struct ft5x06_ts_data *data = _data;
+
+	mutex_lock(&data->input_dev->mutex);
+
+	if (ft5x06_debug_addr_is_valid(data->addr))
+		*val = data->addr;
+
+	mutex_unlock(&data->input_dev->mutex);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft5x06_debug_addr_get,
+			ft5x06_debug_addr_set, "0x%02llX\n");
+
+static int ft5x06_debug_suspend_set(void *_data, u64 val)
+{
+	struct ft5x06_ts_data *data = _data;
+
+	mutex_lock(&data->input_dev->mutex);
+
+	if (val)
+		ft5x06_ts_suspend(&data->client->dev);
+	else
+		ft5x06_ts_resume(&data->client->dev);
+
+	mutex_unlock(&data->input_dev->mutex);
+
+	return 0;
+}
+
+static int ft5x06_debug_suspend_get(void *_data, u64 *val)
+{
+	struct ft5x06_ts_data *data = _data;
+
+	mutex_lock(&data->input_dev->mutex);
+	*val = data->suspended;
+	mutex_unlock(&data->input_dev->mutex);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft5x06_debug_suspend_get,
+			ft5x06_debug_suspend_set, "%lld\n");
+
+static int ft5x06_debug_dump_info(struct seq_file *m, void *v)
+{
+	struct ft5x06_ts_data *data = m->private;
+
+	seq_printf(m, "%s\n", data->ts_info);
+
+	return 0;
+}
+
+static int debugfs_dump_info_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ft5x06_debug_dump_info, inode->i_private);
+}
+
+static const struct file_operations debug_dump_info_fops = {
+	.owner		= THIS_MODULE,
+	.open		= debugfs_dump_info_open,
+	.read		= seq_read,
+	.release	= single_release,
 };
-#endif
 
 #ifdef CONFIG_OF
 static int ft5x06_get_dt_coords(struct device *dev, char *name,
 				struct ft5x06_ts_platform_data *pdata)
 {
-	u32 coords[FT5X06_COORDS_ARR_SIZE];
+	u32 coords[FT_COORDS_ARR_SIZE];
 	struct property *prop;
 	struct device_node *np = dev->of_node;
 	int coords_size, rc;
@@ -420,7 +2055,7 @@
 		return -ENODATA;
 
 	coords_size = prop->length / sizeof(u32);
-	if (coords_size != FT5X06_COORDS_ARR_SIZE) {
+	if (coords_size != FT_COORDS_ARR_SIZE) {
 		dev_err(dev, "invalid %s\n", name);
 		return -EINVAL;
 	}
@@ -458,6 +2093,13 @@
 	u32 temp_val, num_buttons;
 	u32 button_map[MAX_BUTTONS];
 
+	pdata->name = "focaltech";
+	rc = of_property_read_string(np, "focaltech,name", &pdata->name);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read name\n");
+		return rc;
+	}
+
 	rc = ft5x06_get_dt_coords(dev, "focaltech,panel-coords", pdata);
 	if (rc && (rc != -EINVAL))
 		return rc;
@@ -482,6 +2124,98 @@
 	if (pdata->irq_gpio < 0)
 		return pdata->irq_gpio;
 
+	pdata->fw_name = "ft_fw.bin";
+	rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw name\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(np, "focaltech,group-id", &temp_val);
+	if (!rc)
+		pdata->group_id = temp_val;
+	else
+		return rc;
+
+	rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms",
+							&temp_val);
+	if (!rc)
+		pdata->hard_rst_dly = temp_val;
+	else
+		return rc;
+
+	rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms",
+							&temp_val);
+	if (!rc)
+		pdata->soft_rst_dly = temp_val;
+	else
+		return rc;
+
+	rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val);
+	if (!rc)
+		pdata->num_max_touches = temp_val;
+	else
+		return rc;
+
+	rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw delay aa\n");
+		return rc;
+	} else if (rc != -EINVAL)
+		pdata->info.delay_aa =  temp_val;
+
+	rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw delay 55\n");
+		return rc;
+	} else if (rc != -EINVAL)
+		pdata->info.delay_55 =  temp_val;
+
+	rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw upgrade id1\n");
+		return rc;
+	} else if (rc != -EINVAL)
+		pdata->info.upgrade_id_1 =  temp_val;
+
+	rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw upgrade id2\n");
+		return rc;
+	} else if (rc != -EINVAL)
+		pdata->info.upgrade_id_2 =  temp_val;
+
+	rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms",
+							&temp_val);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw delay read id\n");
+		return rc;
+	} else if (rc != -EINVAL)
+		pdata->info.delay_readid =  temp_val;
+
+	rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms",
+							&temp_val);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(dev, "Unable to read fw delay erase flash\n");
+		return rc;
+	} else if (rc != -EINVAL)
+		pdata->info.delay_erase_flash =  temp_val;
+
+	pdata->info.auto_cal = of_property_read_bool(np,
+					"focaltech,fw-auto-cal");
+
+	pdata->fw_vkey_support = of_property_read_bool(np,
+						"focaltech,fw-vkey-support");
+
+	pdata->ignore_id_check = of_property_read_bool(np,
+						"focaltech,ignore-id-check");
+
+	pdata->gesture_support = of_property_read_bool(np,
+						"focaltech,gesture-support");
+
+	pdata->resume_in_workqueue = of_property_read_bool(np,
+					"focaltech,resume-in-workqueue");
+
 	rc = of_property_read_u32(np, "focaltech,family-id", &temp_val);
 	if (!rc)
 		pdata->family_id = temp_val;
@@ -517,11 +2251,13 @@
 			   const struct i2c_device_id *id)
 {
 	struct ft5x06_ts_platform_data *pdata;
+	struct ft5x06_gesture_platform_data *gesture_pdata;
 	struct ft5x06_ts_data *data;
 	struct input_dev *input_dev;
-	u8 reg_value;
+	struct dentry *temp;
+	u8 reg_value = 0;
 	u8 reg_addr;
-	int err;
+	int err, len, retval, attr_count;
 
 	if (client->dev.of_node) {
 		pdata = devm_kzalloc(&client->dev,
@@ -530,8 +2266,10 @@
 			return -ENOMEM;
 
 		err = ft5x06_parse_dt(&client->dev, pdata);
-		if (err)
+		if (err) {
+			dev_err(&client->dev, "DT parsing failed\n");
 			return err;
+		}
 	} else
 		pdata = client->dev.platform_data;
 
@@ -545,15 +2283,31 @@
 		return -ENODEV;
 	}
 
-	data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL);
+	data = devm_kzalloc(&client->dev,
+			sizeof(struct ft5x06_ts_data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
+	if (pdata->fw_name) {
+		len = strlen(pdata->fw_name);
+		if (len > FT_FW_NAME_MAX_LEN - 1) {
+			dev_err(&client->dev, "Invalid firmware name\n");
+			return -EINVAL;
+		}
+
+		strlcpy(data->fw_name, pdata->fw_name, len + 1);
+	}
+
+	data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches);
+	data->tch_data = devm_kzalloc(&client->dev,
+				data->tch_data_len, GFP_KERNEL);
+	if (!data->tch_data)
+		return -ENOMEM;
+
 	input_dev = input_allocate_device();
 	if (!input_dev) {
-		err = -ENOMEM;
 		dev_err(&client->dev, "failed to allocate input device\n");
-		goto free_mem;
+		return -ENOMEM;
 	}
 
 	data->input_dev = input_dev;
@@ -570,20 +2324,19 @@
 	__set_bit(EV_KEY, input_dev->evbit);
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(BTN_TOUCH, input_dev->keybit);
+	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 
+	input_mt_init_slots(input_dev, pdata->num_max_touches, 0);
 	input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min,
 			     pdata->x_max, 0, 0);
 	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
 			     pdata->y_max, 0, 0);
-	input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
-			     CFG_MAX_TOUCH_POINTS, 0, 0);
-	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0);
-	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0);
 
 	err = input_register_device(input_dev);
 	if (err) {
 		dev_err(&client->dev, "Input device registration failed\n");
-		goto free_inputdev;
+		input_free_device(input_dev);
+		return err;
 	}
 
 	if (pdata->power_init) {
@@ -614,98 +2367,294 @@
 		}
 	}
 
-	if (gpio_is_valid(pdata->irq_gpio)) {
-		err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio");
-		if (err) {
-			dev_err(&client->dev, "irq gpio request failed");
-			goto pwr_off;
-		}
-		err = gpio_direction_input(pdata->irq_gpio);
-		if (err) {
+	err = ft5x06_ts_pinctrl_init(data);
+	if (!err && data->ts_pinctrl) {
+		/*
+		 * Pinctrl handle is optional. If pinctrl handle is found
+		 * let pins to be configured in active state. If not
+		 * found continue further without error.
+		 */
+		err = pinctrl_select_state(data->ts_pinctrl,
+					data->pinctrl_state_active);
+		if (err < 0) {
 			dev_err(&client->dev,
-				"set_direction for irq gpio failed\n");
-			goto free_irq_gpio;
+				"failed to select pin to active state");
 		}
 	}
 
-	if (gpio_is_valid(pdata->reset_gpio)) {
-		err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio");
-		if (err) {
-			dev_err(&client->dev, "reset gpio request failed");
-			goto free_irq_gpio;
-		}
-
-		err = gpio_direction_output(pdata->reset_gpio, 0);
-		if (err) {
-			dev_err(&client->dev,
-				"set_direction for reset gpio failed\n");
-			goto free_reset_gpio;
-		}
-		msleep(FT_RESET_DLY);
-		gpio_set_value_cansleep(data->pdata->reset_gpio, 1);
+	err = ft5x06_gpio_configure(data, true);
+	if (err < 0) {
+		dev_err(&client->dev,
+			"Failed to configure the gpios\n");
+		goto err_gpio_req;
 	}
 
 	/* make sure CTP already finish startup process */
-	msleep(FT_STARTUP_DLY);
+	msleep(data->pdata->soft_rst_dly);
 
 	/* check the controller id */
-	reg_addr = FT5X06_REG_ID;
+	reg_addr = FT_REG_ID;
 	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
 	if (err < 0) {
 		dev_err(&client->dev, "version read failed");
-		return err;
+		goto free_gpio;
 	}
 
-	if (pdata->family_id != reg_value) {
+	dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
+
+	if ((pdata->family_id != reg_value) && (!pdata->ignore_id_check)) {
 		dev_err(&client->dev, "%s:Unsupported controller\n", __func__);
-		goto free_reset_gpio;
+		goto free_gpio;
 	}
 
-	/*get some register information */
-	reg_addr = FT5X06_REG_FW_VER;
-	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-	if (err)
-		dev_err(&client->dev, "version read failed");
-
-	dev_info(&client->dev, "[FTS] Firmware version = 0x%x\n", reg_value);
-
-	reg_addr = FT5X06_REG_POINT_RATE;
-	ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-	if (err)
-		dev_err(&client->dev, "report rate read failed");
-	dev_info(&client->dev, "[FTS] report rate is %dHz.\n", reg_value * 10);
-
-	reg_addr = FT5X06_REG_THGROUP;
-	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
-	if (err)
-		dev_err(&client->dev, "threshold read failed");
-	dev_dbg(&client->dev, "[FTS] touch threshold is %d.\n", reg_value * 4);
+	data->family_id = pdata->family_id;
 
 	err = request_threaded_irq(client->irq, NULL,
-				   ft5x06_ts_interrupt, pdata->irqflags,
-				   client->dev.driver->name, data);
+				ft5x06_ts_interrupt,
+	/*
+	 * the interrupt trigger mode will be set in Device Tree with property
+	 * "interrupts", so here we just need to set the flag IRQF_ONESHOT
+	 */
+				IRQF_ONESHOT,
+				client->dev.driver->name, data);
 	if (err) {
 		dev_err(&client->dev, "request irq failed\n");
-		goto free_reset_gpio;
+		goto free_gpio;
 	}
 
-#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
+		device_init_wakeup(&client->dev, 1);
+		gesture_pdata = devm_kzalloc(&client->dev,
+				sizeof(struct ft5x06_gesture_platform_data),
+				GFP_KERNEL);
+		if (!gesture_pdata) {
+			dev_err(&client->dev, "Failed to allocate memory\n");
+			goto free_gesture_dev;
+		}
+		data->gesture_pdata = gesture_pdata;
+		gesture_pdata->data = data;
+
+		gesture_pdata->gesture_class =
+					class_create(THIS_MODULE, "gesture");
+		if (IS_ERR(gesture_pdata->gesture_class)) {
+			err = PTR_ERR(gesture_pdata->gesture_class);
+			dev_err(&client->dev, "Failed to create class.\n");
+			goto free_gesture_pdata;
+		}
+
+		gesture_pdata->dev = device_create(gesture_pdata->gesture_class,
+				NULL, 0, NULL, "gesture_ft5x06");
+		if (IS_ERR(gesture_pdata->dev)) {
+			err = PTR_ERR(gesture_pdata->dev);
+			dev_err(&client->dev, "Failed to create device.\n");
+			goto free_gesture_class;
+		}
+
+		dev_set_drvdata(gesture_pdata->dev, data);
+		err = device_create_file(gesture_pdata->dev,
+					&dev_attr_enable);
+		if (err) {
+			dev_err(gesture_pdata->dev,
+					"sys file creation failed\n");
+			goto free_gesture_dev;
+		}
+		err = device_create_file(gesture_pdata->dev,
+					&dev_attr_pocket);
+		if (err) {
+			dev_err(gesture_pdata->dev,
+					"sys file creation failed\n");
+			goto free_enable_sys;
+		}
+	}
+
+	err = device_create_file(&client->dev, &dev_attr_fw_name);
+	if (err) {
+		dev_err(&client->dev, "sys file creation failed\n");
+		goto free_pocket_sys;
+	}
+
+	err = device_create_file(&client->dev, &dev_attr_update_fw);
+	if (err) {
+		dev_err(&client->dev, "sys file creation failed\n");
+		goto free_fw_name_sys;
+	}
+
+	err = device_create_file(&client->dev, &dev_attr_force_update_fw);
+	if (err) {
+		dev_err(&client->dev, "sys file creation failed\n");
+		goto free_update_fw_sys;
+	}
+
+	data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL);
+	if (data->dir == NULL || IS_ERR(data->dir)) {
+		pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir));
+		err = PTR_ERR(data->dir);
+		goto free_force_update_fw_sys;
+	}
+
+	temp = debugfs_create_file("addr", 0600, data->dir, data,
+				   &debug_addr_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		err = PTR_ERR(temp);
+		goto free_debug_dir;
+	}
+
+	temp = debugfs_create_file("data", 0600, data->dir, data,
+				   &debug_data_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		err = PTR_ERR(temp);
+		goto free_debug_dir;
+	}
+
+	temp = debugfs_create_file("suspend", 0600, data->dir,
+					data, &debug_suspend_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		err = PTR_ERR(temp);
+		goto free_debug_dir;
+	}
+
+	temp = debugfs_create_file("dump_info", 0600, data->dir,
+					data, &debug_dump_info_fops);
+	if (temp == NULL || IS_ERR(temp)) {
+		pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
+		err = PTR_ERR(temp);
+		goto free_debug_dir;
+	}
+
+	data->ts_info = devm_kzalloc(&client->dev,
+				FT_INFO_MAX_LEN, GFP_KERNEL);
+	if (!data->ts_info)
+		goto free_debug_dir;
+
+	/*get some register information */
+	reg_addr = FT_REG_POINT_RATE;
+	ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+	if (err < 0)
+		dev_err(&client->dev, "report rate read failed");
+
+	dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10);
+
+	reg_addr = FT_REG_THGROUP;
+	err = ft5x06_i2c_read(client, &reg_addr, 1, &reg_value, 1);
+	if (err < 0)
+		dev_err(&client->dev, "threshold read failed");
+
+	dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4);
+
+	/*creation touch panel info kobj*/
+	data->ts_info_kobj = kobject_create_and_add(FT_TS_INFO_SYSFS_DIR_NAME,
+					kernel_kobj);
+	if (!data->ts_info_kobj) {
+		dev_err(&client->dev, "kobject creation failed.\n");
+	} else {
+		err = sysfs_create_file(data->ts_info_kobj, &ts_info_attr.attr);
+		if (err) {
+			kobject_put(data->ts_info_kobj);
+			dev_err(&client->dev, "sysfs creation failed.\n");
+		} else {
+			ts_info_buff = devm_kzalloc(&client->dev,
+						 FT_INFO_MAX_LEN, GFP_KERNEL);
+			if (!ts_info_buff)
+				goto free_debug_dir;
+		}
+	}
+
+	/*Initialize secure touch */
+	ft5x06_secure_touch_init(data);
+	ft5x06_secure_touch_stop(data, true);
+	mutex_init(&(data->ft_clk_io_ctrl_mutex));
+
+	/* Creation of secure touch sysfs files */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		retval = sysfs_create_file(&data->input_dev->dev.kobj,
+						&attrs[attr_count].attr);
+		if (retval < 0) {
+			dev_err(&client->dev,
+				"%s: Failed to create sysfs attributes\n",
+							__func__);
+			goto free_secure_touch_sysfs;
+		}
+	}
+
+	ft5x06_update_fw_ver(data);
+	ft5x06_update_fw_vendor_id(data);
+
+	FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name,
+			data->pdata->num_max_touches, data->pdata->group_id,
+			data->pdata->fw_vkey_support ? "yes" : "no",
+			data->pdata->fw_name, data->fw_ver[0],
+			data->fw_ver[1], data->fw_ver[2]);
+	FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0],
+			 data->fw_ver[1], data->fw_ver[2]);
+#if defined(CONFIG_FB)
+	INIT_WORK(&data->fb_notify_work, fb_notify_resume_work);
+	data->fb_notif.notifier_call = fb_notifier_callback;
+
+	err = fb_register_client(&data->fb_notif);
+
+	if (err)
+		dev_err(&client->dev, "Unable to register fb_notifier: %d\n",
+			err);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
 	data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
-	    FT5X06_SUSPEND_LEVEL;
+						    FT_SUSPEND_LEVEL;
 	data->early_suspend.suspend = ft5x06_ts_early_suspend;
 	data->early_suspend.resume = ft5x06_ts_late_resume;
 	register_early_suspend(&data->early_suspend);
 #endif
-
 	return 0;
 
-free_reset_gpio:
+free_secure_touch_sysfs:
+	for (attr_count--; attr_count >= 0; attr_count--) {
+		sysfs_remove_file(&data->input_dev->dev.kobj,
+					&attrs[attr_count].attr);
+	}
+free_debug_dir:
+	debugfs_remove_recursive(data->dir);
+free_force_update_fw_sys:
+	device_remove_file(&client->dev, &dev_attr_force_update_fw);
+free_update_fw_sys:
+	device_remove_file(&client->dev, &dev_attr_update_fw);
+free_fw_name_sys:
+	device_remove_file(&client->dev, &dev_attr_fw_name);
+free_pocket_sys:
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+		device_remove_file(&client->dev, &dev_attr_pocket);
+free_enable_sys:
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+		device_remove_file(&client->dev, &dev_attr_enable);
+free_gesture_dev:
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+		device_destroy(gesture_pdata->gesture_class, 0);
+free_gesture_class:
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support)
+		class_destroy(gesture_pdata->gesture_class);
+free_gesture_pdata:
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
+		devm_kfree(&client->dev, gesture_pdata);
+		data->gesture_pdata = NULL;
+	}
+
+free_gpio:
 	if (gpio_is_valid(pdata->reset_gpio))
 		gpio_free(pdata->reset_gpio);
-free_irq_gpio:
 	if (gpio_is_valid(pdata->irq_gpio))
-		gpio_free(pdata->reset_gpio);
-pwr_off:
+		gpio_free(pdata->irq_gpio);
+err_gpio_req:
+	if (data->ts_pinctrl) {
+		if (IS_ERR_OR_NULL(data->pinctrl_state_release)) {
+			devm_pinctrl_put(data->ts_pinctrl);
+			data->ts_pinctrl = NULL;
+		} else {
+			err = pinctrl_select_state(data->ts_pinctrl,
+					data->pinctrl_state_release);
+			if (err)
+				pr_err("failed to select relase pinctrl state\n");
+		}
+	}
 	if (pdata->power_on)
 		pdata->power_on(false);
 	else
@@ -717,19 +2666,33 @@
 		ft5x06_power_init(data, false);
 unreg_inputdev:
 	input_unregister_device(input_dev);
-	input_dev = NULL;
-free_inputdev:
-	input_free_device(input_dev);
-free_mem:
-	kfree(data);
 	return err;
 }
 
 static int ft5x06_ts_remove(struct i2c_client *client)
 {
 	struct ft5x06_ts_data *data = i2c_get_clientdata(client);
+	int retval, attr_count;
 
-#ifdef CONFIG_HAS_EARLYSUSPEND
+	if (ft5x06_gesture_support_enabled() && data->pdata->gesture_support) {
+		device_init_wakeup(&client->dev, 0);
+		device_remove_file(&client->dev, &dev_attr_pocket);
+		device_remove_file(&client->dev, &dev_attr_enable);
+		device_destroy(data->gesture_pdata->gesture_class, 0);
+		class_destroy(data->gesture_pdata->gesture_class);
+		devm_kfree(&client->dev, data->gesture_pdata);
+		data->gesture_pdata = NULL;
+	}
+
+	debugfs_remove_recursive(data->dir);
+	device_remove_file(&client->dev, &dev_attr_force_update_fw);
+	device_remove_file(&client->dev, &dev_attr_update_fw);
+	device_remove_file(&client->dev, &dev_attr_fw_name);
+
+#if defined(CONFIG_FB)
+	if (fb_unregister_client(&data->fb_notif))
+		dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
 	unregister_early_suspend(&data->early_suspend);
 #endif
 	free_irq(client->irq, data);
@@ -738,7 +2701,24 @@
 		gpio_free(data->pdata->reset_gpio);
 
 	if (gpio_is_valid(data->pdata->irq_gpio))
-		gpio_free(data->pdata->reset_gpio);
+		gpio_free(data->pdata->irq_gpio);
+
+	if (data->ts_pinctrl) {
+		if (IS_ERR_OR_NULL(data->pinctrl_state_release)) {
+			devm_pinctrl_put(data->ts_pinctrl);
+			data->ts_pinctrl = NULL;
+		} else {
+			retval = pinctrl_select_state(data->ts_pinctrl,
+					data->pinctrl_state_release);
+			if (retval < 0)
+				pr_err("failed to select release pinctrl state\n");
+		}
+	}
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		sysfs_remove_file(&data->input_dev->dev.kobj,
+					&attrs[attr_count].attr);
+	}
 
 	if (data->pdata->power_on)
 		data->pdata->power_on(false);
@@ -751,8 +2731,7 @@
 		ft5x06_power_init(data, false);
 
 	input_unregister_device(data->input_dev);
-	kfree(data);
-
+	kobject_put(data->ts_info_kobj);
 	return 0;
 }
 
@@ -774,7 +2753,7 @@
 
 static struct i2c_driver ft5x06_ts_driver = {
 	.probe = ft5x06_ts_probe,
-	.remove = __devexit_p(ft5x06_ts_remove),
+	.remove = ft5x06_ts_remove,
 	.driver = {
 		   .name = "ft5x06_ts",
 		   .owner = THIS_MODULE,
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 6c9446e..bd189a4 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -48,6 +48,7 @@
 #include <asm/smp_plat.h>
 #include <asm/virt.h>
 
+#include <linux/syscore_ops.h>
 #include "irq-gic-common.h"
 
 #ifdef CONFIG_ARM64
@@ -1232,6 +1233,70 @@
 	return ret;
 }
 
+#ifdef CONFIG_PM
+static int gic_suspend(void)
+{
+	return 0;
+}
+
+static void gic_show_resume_irq(struct gic_chip_data *gic)
+{
+	unsigned int i;
+	u32 enabled;
+	u32 pending[32];
+	void __iomem *base = gic_data_dist_base(gic);
+
+	if (!msm_show_resume_irq_mask)
+		return;
+
+	for (i = 0; i * 32 < gic->gic_irqs; i++) {
+		enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4);
+		pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4);
+		pending[i] &= enabled;
+	}
+
+	for (i = find_first_bit((unsigned long *)pending, gic->gic_irqs);
+	i < gic->gic_irqs;
+	i = find_next_bit((unsigned long *)pending, gic->gic_irqs, i+1)) {
+		unsigned int irq = irq_find_mapping(gic->domain, i);
+		struct irq_desc *desc = irq_to_desc(irq);
+		const char *name = "null";
+
+		if (desc == NULL)
+			name = "stray irq";
+		else if (desc->action && desc->action->name)
+			name = desc->action->name;
+
+		pr_warn("%s: %d triggered %s\n", __func__, i, name);
+	}
+}
+
+static void gic_resume_one(struct gic_chip_data *gic)
+{
+	gic_show_resume_irq(gic);
+}
+
+static void gic_resume(void)
+{
+	int i;
+
+	for (i = 0; i < CONFIG_ARM_GIC_MAX_NR; i++)
+		gic_resume_one(&gic_data[i]);
+}
+
+static struct syscore_ops gic_syscore_ops = {
+	.suspend = gic_suspend,
+	.resume = gic_resume,
+};
+
+static int __init gic_init_sys(void)
+{
+	register_syscore_ops(&gic_syscore_ops);
+	return 0;
+}
+arch_initcall(gic_init_sys);
+#endif
+
 void __init gic_init(unsigned int gic_nr, int irq_start,
 		     void __iomem *dist_base, void __iomem *cpu_base)
 {
diff --git a/drivers/leds/leds-qti-tri-led.c b/drivers/leds/leds-qti-tri-led.c
index f638bc9..c303893 100644
--- a/drivers/leds/leds-qti-tri-led.c
+++ b/drivers/leds/leds-qti-tri-led.c
@@ -53,6 +53,7 @@
 	u32			off_ms;
 	enum led_brightness	brightness;
 	bool			blink;
+	bool			breath;
 };
 
 struct qpnp_led_dev {
@@ -66,6 +67,7 @@
 	const char		*default_trigger;
 	u8			id;
 	bool			blinking;
+	bool			breathing;
 };
 
 struct qpnp_tri_led_chip {
@@ -119,6 +121,10 @@
 	pstate.enabled = !!(pwm->duty_ns != 0);
 	pstate.period = pwm->period_ns;
 	pstate.duty_cycle = pwm->duty_ns;
+	pstate.output_type = led->led_setting.breath ?
+		PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED;
+	/* Use default pattern in PWM device */
+	pstate.output_pattern = NULL;
 	rc = pwm_apply_state(led->pwm_dev, &pstate);
 
 	if (rc < 0)
@@ -183,7 +189,9 @@
 		/* Use initial period if no blinking is required */
 		period_ns = led->pwm_setting.pre_period_ns;
 
-		if (period_ns > INT_MAX / brightness)
+		if (brightness == LED_OFF)
+			duty_ns = 0;
+		else if (period_ns > INT_MAX / brightness)
 			duty_ns = (period_ns / LED_FULL) * brightness;
 		else
 			duty_ns = (period_ns * brightness) / LED_FULL;
@@ -207,9 +215,15 @@
 	if (led->led_setting.blink) {
 		led->cdev.brightness = LED_FULL;
 		led->blinking = true;
+		led->breathing = false;
+	} else if (led->led_setting.breath) {
+		led->cdev.brightness = LED_FULL;
+		led->blinking = false;
+		led->breathing = true;
 	} else {
 		led->cdev.brightness = led->led_setting.brightness;
 		led->blinking = false;
+		led->breathing = false;
 	}
 
 	return rc;
@@ -227,7 +241,7 @@
 		brightness = LED_FULL;
 
 	if (brightness == led->led_setting.brightness &&
-				!led->blinking) {
+			!led->blinking && !led->breathing) {
 		mutex_unlock(&led->lock);
 		return 0;
 	}
@@ -238,6 +252,7 @@
 	else
 		led->led_setting.on_ms = 0;
 	led->led_setting.blink = false;
+	led->led_setting.breath = false;
 
 	rc = qpnp_tri_led_set(led);
 	if (rc)
@@ -273,14 +288,17 @@
 
 	if (*on_ms == 0) {
 		led->led_setting.blink = false;
+		led->led_setting.breath = false;
 		led->led_setting.brightness = LED_OFF;
 	} else if (*off_ms == 0) {
 		led->led_setting.blink = false;
+		led->led_setting.breath = false;
 		led->led_setting.brightness = led->cdev.brightness;
 	} else {
 		led->led_setting.on_ms = *on_ms;
 		led->led_setting.off_ms = *off_ms;
 		led->led_setting.blink = true;
+		led->led_setting.breath = false;
 	}
 
 	rc = qpnp_tri_led_set(led);
@@ -292,6 +310,52 @@
 	return rc;
 }
 
+static ssize_t breath_show(struct device *dev, struct device_attribute *attr,
+							char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct qpnp_led_dev *led =
+		container_of(led_cdev, struct qpnp_led_dev, cdev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath);
+}
+
+static ssize_t breath_store(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	int rc;
+	bool breath;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct qpnp_led_dev *led =
+		container_of(led_cdev, struct qpnp_led_dev, cdev);
+
+	rc = kstrtobool(buf, &breath);
+	if (rc < 0)
+		return rc;
+
+	mutex_lock(&led->lock);
+	if (led->breathing == breath)
+		goto unlock;
+
+	led->led_setting.blink = false;
+	led->led_setting.breath = breath;
+	led->led_setting.brightness = breath ? LED_FULL : LED_OFF;
+	rc = qpnp_tri_led_set(led);
+	if (rc < 0)
+		dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+				led->label, rc);
+
+unlock:
+	mutex_unlock(&led->lock);
+	return (rc < 0) ? rc : count;
+}
+
+static DEVICE_ATTR(breath, 0644, breath_show, breath_store);
+static const struct attribute *breath_attrs[] = {
+	&dev_attr_breath.attr,
+	NULL
+};
+
 static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip)
 {
 	struct qpnp_led_dev *led;
@@ -313,15 +377,30 @@
 		if (rc < 0) {
 			dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
 							led->label, rc);
-			goto destroy;
+			goto err_out;
+		}
+
+		if (pwm_get_output_type_supported(led->pwm_dev)
+				& PWM_OUTPUT_MODULATED) {
+			rc = sysfs_create_files(&led->cdev.dev->kobj,
+					breath_attrs);
+			if (rc < 0) {
+				dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n",
+						led->label, rc);
+				goto err_out;
+			}
 		}
 	}
 
 	return 0;
-destroy:
-	for (j = 0; j <= i; j++)
-		mutex_destroy(&chip->leds[i].lock);
 
+err_out:
+	for (j = 0; j <= i; j++) {
+		if (j < i)
+			sysfs_remove_files(&chip->leds[j].cdev.dev->kobj,
+					breath_attrs);
+		mutex_destroy(&chip->leds[j].lock);
+	}
 	return rc;
 }
 
@@ -483,8 +562,10 @@
 	struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev);
 
 	mutex_destroy(&chip->bus_lock);
-	for (i = 0; i < chip->num_leds; i++)
+	for (i = 0; i < chip->num_leds; i++) {
+		sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs);
 		mutex_destroy(&chip->leds[i].lock);
+	}
 	dev_set_drvdata(chip->dev, NULL);
 	return 0;
 }
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.h b/drivers/media/platform/msm/camera/cam_core/cam_context.h
index 8324e78..ffceea2 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -70,7 +70,7 @@
 	uint32_t                      num_in_map_entries;
 	struct cam_hw_fence_map_entry out_map_entries[CAM_CTX_CFG_MAX];
 	uint32_t                      num_out_map_entries;
-	uint32_t                      num_in_acked;
+	atomic_t                      num_in_acked;
 	uint32_t                      num_out_acked;
 	int                           flushed;
 	struct cam_context           *ctx;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index b4f83f7..f167ef7 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -189,8 +189,7 @@
 		return;
 	}
 
-	req->num_in_acked++;
-	if (req->num_in_acked == req->num_in_map_entries) {
+	if (atomic_inc_return(&req->num_in_acked) == req->num_in_map_entries) {
 		apply.request_id = req->request_id;
 		/*
 		 * take mutex to ensure that another thread does
@@ -342,6 +341,7 @@
 	req->num_hw_update_entries = cfg.num_hw_update_entries;
 	req->num_out_map_entries = cfg.num_out_map_entries;
 	req->num_in_map_entries = cfg.num_in_map_entries;
+	atomic_set(&req->num_in_acked, 0);
 	req->request_id = packet->header.request_id;
 	req->status = 1;
 	req->req_priv = cfg.priv;
@@ -492,6 +492,7 @@
 	struct cam_ctx_request *req;
 	uint32_t i;
 	int rc = 0;
+	bool free_req;
 
 	CAM_DBG(CAM_CTXT, "[%s] E: NRT flush ctx", ctx->dev_name);
 
@@ -528,6 +529,21 @@
 
 		flush_args.flush_req_pending[flush_args.num_req_pending++] =
 			req->req_priv;
+
+		free_req = false;
+		for (i = 0; i < req->num_in_map_entries; i++) {
+			rc = cam_sync_deregister_callback(
+				cam_context_sync_callback,
+				(void *)req,
+				req->in_map_entries[i].sync_id);
+			if (!rc) {
+				cam_context_putref(ctx);
+				if (atomic_inc_return(&req->num_in_acked) ==
+					req->num_in_map_entries)
+					free_req = true;
+			}
+		}
+
 		for (i = 0; i < req->num_out_map_entries; i++) {
 			if (req->out_map_entries[i].sync_id != -1) {
 				rc = cam_sync_signal(
@@ -543,6 +559,17 @@
 			}
 		}
 
+		/*
+		 * If we have deregistered the last sync callback, req will
+		 * not be put on the free list. So put it on the free list here
+		 */
+		if (free_req) {
+			req->ctx = NULL;
+			spin_lock(&ctx->lock);
+			list_add_tail(&req->list, &ctx->free_req_list);
+			spin_unlock(&ctx->lock);
+		}
+
 		if (cam_debug_ctx_req_list & ctx->dev_id)
 			CAM_INFO(CAM_CTXT,
 				"[%s][%d] : Deleting req[%llu] from temp_list",
@@ -630,6 +657,7 @@
 	uint32_t i;
 	int32_t sync_id = 0;
 	int rc = 0;
+	bool free_req = false;
 
 	CAM_DBG(CAM_CTXT, "[%s] E: NRT flush req", ctx->dev_name);
 
@@ -682,6 +710,22 @@
 	}
 
 	if (req) {
+		if (flush_args.num_req_pending) {
+			for (i = 0; i < req->num_in_map_entries; i++) {
+				rc = cam_sync_deregister_callback(
+					cam_context_sync_callback,
+					(void *)req,
+					req->in_map_entries[i].sync_id);
+				if (rc)
+					continue;
+
+				cam_context_putref(ctx);
+				if (atomic_inc_return(&req->num_in_acked) ==
+					req->num_in_map_entries)
+					free_req = true;
+			}
+		}
+
 		if (flush_args.num_req_pending || flush_args.num_req_active) {
 			for (i = 0; i < req->num_out_map_entries; i++) {
 				sync_id =
@@ -697,17 +741,20 @@
 					}
 				}
 			}
-			if (flush_args.num_req_active) {
+			if (flush_args.num_req_active || free_req) {
+				req->ctx = NULL;
 				spin_lock(&ctx->lock);
 				list_add_tail(&req->list, &ctx->free_req_list);
 				spin_unlock(&ctx->lock);
-				req->ctx = NULL;
 
 				if (cam_debug_ctx_req_list & ctx->dev_id)
 					CAM_INFO(CAM_CTXT,
-						"[%s][%d] : Moving req[%llu] from active_list to free_list",
+						"[%s][%d] : Moving req[%llu] from %s to free_list",
 						ctx->dev_name, ctx->ctx_id,
-						req->request_id);
+						req->request_id,
+						flush_args.num_req_active ?
+							"active_list" :
+							"pending_list");
 			}
 		}
 	}
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 9ed71d2..ba4385f5 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
@@ -57,6 +57,8 @@
 #define ICP_DEV_TYPE_TO_CLK_TYPE(dev_type) \
 	((dev_type == CAM_ICP_RES_TYPE_BPS) ? ICP_CLK_HW_BPS : ICP_CLK_HW_IPE)
 
+#define ICP_DEVICE_IDLE_TIMEOUT 400
+
 static struct cam_icp_hw_mgr icp_hw_mgr;
 
 static int cam_icp_send_ubwc_cfg(struct cam_icp_hw_mgr *hw_mgr)
@@ -549,7 +551,7 @@
 	for (i = 0; i < ICP_CLK_HW_MAX; i++)  {
 		if (!hw_mgr->clk_info[i].watch_dog) {
 			rc = crm_timer_init(&hw_mgr->clk_info[i].watch_dog,
-				3000, &hw_mgr->clk_info[i],
+				ICP_DEVICE_IDLE_TIMEOUT, &hw_mgr->clk_info[i],
 				&cam_icp_device_timer_cb);
 
 			if (rc)
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index 1106453..97e977d 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
@@ -447,6 +447,33 @@
 	return rc;
 }
 
+static void __cam_isp_ctx_send_sof_boot_timestamp(
+	struct cam_isp_context *ctx_isp, uint64_t request_id,
+	uint32_t sof_event_status)
+{
+	struct cam_req_mgr_message   req_msg;
+
+	req_msg.session_hdl = ctx_isp->base->session_hdl;
+	req_msg.u.frame_msg.frame_id = ctx_isp->frame_id;
+	req_msg.u.frame_msg.request_id = request_id;
+	req_msg.u.frame_msg.timestamp = ctx_isp->boot_timestamp;
+	req_msg.u.frame_msg.link_hdl = ctx_isp->base->link_hdl;
+	req_msg.u.frame_msg.sof_status = sof_event_status;
+
+	CAM_DBG(CAM_ISP,
+		"request id:%lld frame number:%lld boot time stamp:0x%llx",
+		 request_id, ctx_isp->frame_id,
+		 ctx_isp->boot_timestamp);
+
+	if (cam_req_mgr_notify_message(&req_msg,
+		V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS,
+		V4L_EVENT_CAM_REQ_MGR_EVENT))
+		CAM_ERR(CAM_ISP,
+			"Error in notifying the boot time for req id:%lld",
+			request_id);
+}
+
+
 static void __cam_isp_ctx_send_sof_timestamp(
 	struct cam_isp_context *ctx_isp, uint64_t request_id,
 	uint32_t sof_event_status)
@@ -464,13 +491,17 @@
 		"request id:%lld frame number:%lld SOF time stamp:0x%llx",
 		 request_id, ctx_isp->frame_id,
 		ctx_isp->sof_timestamp_val);
-	CAM_DBG(CAM_ISP, " sof status:%d", sof_event_status);
+	CAM_DBG(CAM_ISP, "sof status:%d", sof_event_status);
 
 	if (cam_req_mgr_notify_message(&req_msg,
 		V4L_EVENT_CAM_REQ_MGR_SOF, V4L_EVENT_CAM_REQ_MGR_EVENT))
 		CAM_ERR(CAM_ISP,
 			"Error in notifying the sof time for req id:%lld",
 			request_id);
+
+	__cam_isp_ctx_send_sof_boot_timestamp(ctx_isp,
+		request_id, sof_event_status);
+
 }
 
 static int __cam_isp_ctx_reg_upd_in_activated_state(
@@ -617,6 +648,7 @@
 
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
 	__cam_isp_ctx_update_state_monitor_array(ctx_isp,
 		CAM_ISP_STATE_CHANGE_TRIGGER_SOF, req->request_id);
 	CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
@@ -773,6 +805,7 @@
 
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
 
 	if (list_empty(&ctx->active_req_list))
 		ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
@@ -1045,6 +1078,7 @@
 	}
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
 	CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
 		ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
 
@@ -1470,6 +1504,8 @@
 
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
+
 	CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
 		ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
 
@@ -1523,6 +1559,7 @@
 
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
 	CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
 		ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
 
@@ -1554,6 +1591,7 @@
 
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
 	CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
 		ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
 
@@ -1639,6 +1677,7 @@
 
 	ctx_isp->frame_id++;
 	ctx_isp->sof_timestamp_val = sof_event_data->timestamp;
+	ctx_isp->boot_timestamp = sof_event_data->boot_time;
 	CAM_DBG(CAM_ISP, "frame id: %lld time stamp:0x%llx",
 		ctx_isp->frame_id, ctx_isp->sof_timestamp_val);
 	/*
@@ -1701,6 +1740,7 @@
 	struct cam_req_mgr_trigger_notify  notify;
 	uint64_t  request_id  = 0;
 
+	ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH;
 	/* notify reqmgr with sof signal*/
 	if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_trigger) {
 		if (list_empty(&ctx->pending_req_list)) {
@@ -1712,13 +1752,19 @@
 		list_del_init(&req->list);
 
 		req_isp = (struct cam_isp_ctx_req *) req->req_priv;
-		request_id = req->request_id;
+		request_id =
+			(req_isp->hw_update_data.packet_opcode_type ==
+				CAM_ISP_PACKET_INIT_DEV) ?
+			0 : req->request_id;
+
 		if (req_isp->num_fence_map_out != 0) {
 			list_add_tail(&req->list, &ctx->active_req_list);
 			ctx_isp->active_req_cnt++;
 			CAM_DBG(CAM_ISP,
 				"move request %lld to active list(cnt = %d)",
 				req->request_id, ctx_isp->active_req_cnt);
+			/* if packet has buffers, set correct request id */
+			request_id = req->request_id;
 		} else {
 			/* no io config, so the request is completed. */
 			list_add_tail(&req->list, &ctx->free_req_list);
@@ -1738,10 +1784,11 @@
 	} else {
 		CAM_ERR(CAM_ISP, "Can not notify SOF to CRM");
 	}
+	if (request_id)
+		ctx_isp->reported_req_id = request_id;
+
 	__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 		CAM_REQ_MGR_SOF_EVENT_SUCCESS);
-
-	ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_EPOCH;
 	CAM_DBG(CAM_ISP, "next substate %d", ctx_isp->substate_activated);
 
 	return 0;
@@ -2042,7 +2089,7 @@
 	req->request_id = packet->header.request_id;
 	req->status = 1;
 
-	CAM_DBG(CAM_ISP, "Packet request id 0x%llx packet opcode:%d",
+	CAM_DBG(CAM_ISP, "Packet request id %lld packet opcode:%d",
 		packet->header.request_id,
 		req_isp->hw_update_data.packet_opcode_type);
 
@@ -2185,6 +2232,7 @@
 			cam_isp_ctx_activated_state_machine;
 	}
 
+	ctx_isp->rdi_only_context = hw_cmd_args.u.is_rdi_only_context;
 	ctx_isp->hw_ctx = param.ctxt_to_hw_map;
 
 	req_hdl_param.session_hdl = cmd->session_handle;
@@ -2330,7 +2378,8 @@
 	ctx_isp->frame_id = 0;
 	ctx_isp->active_req_cnt = 0;
 	ctx_isp->reported_req_id = 0;
-	ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
+	ctx_isp->substate_activated = ctx_isp->rdi_only_context ?
+		CAM_ISP_CTX_ACTIVATED_APPLIED : CAM_ISP_CTX_ACTIVATED_SOF;
 
 	/*
 	 * Only place to change state before calling the hw due to
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
index 1eae89f..a939f2d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
@@ -149,6 +149,7 @@
  * @req_isp:                   ISP private request object storage
  * @hw_ctx:                    HW object returned by the acquire device command
  * @sof_timestamp_val:         Captured time stamp value at sof hw event
+ * @boot_timestamp:            Boot time stamp for a given req_id
  * @active_req_cnt:            Counter for the active request
  * @reported_req_id:           Last reported request id
  * @subscribe_event:           The irq event mask that CRM subscribes to, IFE
@@ -157,6 +158,8 @@
  * @frame_skip_count:          Number of frame to skip before change state
  * @state_monitor_head:        Write index to the state monitoring array
  * @cam_isp_ctx_state_monitor: State monitoring array
+ * @rdi_only_context:          Get context type information.
+ *                             true, if context is rdi only context
  *
  */
 struct cam_isp_context {
@@ -172,6 +175,7 @@
 
 	void                            *hw_ctx;
 	uint64_t                         sof_timestamp_val;
+	uint64_t                         boot_timestamp;
 	int32_t                          active_req_cnt;
 	int64_t                          reported_req_id;
 	uint32_t                         subscribe_event;
@@ -180,6 +184,7 @@
 	atomic64_t                       state_monitor_head;
 	struct cam_isp_context_state_monitor cam_isp_ctx_state_monitor[
 		CAM_ISP_CTX_STATE_MONITOR_MAX_ENTRIES];
+	bool                             rdi_only_context;
 };
 
 /**
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 6e140e2..0a127ef 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
@@ -1418,7 +1418,7 @@
 
 		in_port = memdup_user((void __user *)isp_resource[i].res_hdl,
 			isp_resource[i].length);
-		if (in_port > 0) {
+		if (!IS_ERR(in_port)) {
 			rc = cam_ife_mgr_acquire_hw_for_ctx(ife_ctx, in_port,
 				&num_pix_port_per_in, &num_rdi_port_per_in);
 			total_pix_port += num_pix_port_per_in;
@@ -1611,6 +1611,9 @@
 			cdm_cmd->cmd[i].len = cmd->len;
 		}
 
+		if (cfg->request_id == 1)
+			init_completion(&ctx->config_done_complete);
+
 		CAM_DBG(CAM_ISP, "Submit to CDM");
 		rc = cam_cdm_submit_bls(ctx->cdm_handle, cdm_cmd);
 		if (rc) {
@@ -1619,7 +1622,6 @@
 		}
 
 		if (cfg->request_id == 1) {
-			init_completion(&ctx->config_done_complete);
 			rc = wait_for_completion_timeout(
 				&ctx->config_done_complete,
 				msecs_to_jiffies(30));
@@ -2675,7 +2677,8 @@
 
 static int cam_ife_mgr_cmd_get_sof_timestamp(
 	struct cam_ife_hw_mgr_ctx      *ife_ctx,
-	uint64_t                       *time_stamp)
+	uint64_t                       *time_stamp,
+	uint64_t                       *boot_time_stamp)
 {
 	int rc = -EINVAL;
 	uint32_t i;
@@ -2704,9 +2707,12 @@
 					&csid_get_time,
 					sizeof(
 					struct cam_csid_get_time_stamp_args));
-				if (!rc)
+				if (!rc) {
 					*time_stamp =
 						csid_get_time.time_stamp_val;
+					*boot_time_stamp =
+						csid_get_time.boot_timestamp;
+				}
 			/*
 			 * Single VFE case, Get the time stamp from available
 			 * one csid hw in the context
@@ -3537,7 +3543,8 @@
 				if (!sof_status && !sof_sent) {
 					cam_ife_mgr_cmd_get_sof_timestamp(
 						ife_hw_mgr_ctx,
-						&sof_done_event_data.timestamp);
+						&sof_done_event_data.timestamp,
+						&sof_done_event_data.boot_time);
 
 					ife_hw_irq_sof_cb(
 						ife_hw_mgr_ctx->common.cb_priv,
@@ -3558,7 +3565,8 @@
 			if (!sof_status && !sof_sent) {
 				cam_ife_mgr_cmd_get_sof_timestamp(
 					ife_hw_mgr_ctx,
-					&sof_done_event_data.timestamp);
+					&sof_done_event_data.timestamp,
+					&sof_done_event_data.boot_time);
 
 				ife_hw_irq_sof_cb(
 					ife_hw_mgr_ctx->common.cb_priv,
@@ -4172,6 +4180,8 @@
 		g_ife_hw_mgr.ctx_pool[i].common.tasklet_info =
 			g_ife_hw_mgr.mgr_common.tasklet_pool[i];
 
+
+		init_completion(&g_ife_hw_mgr.ctx_pool[i].config_done_complete);
 		list_add_tail(&g_ife_hw_mgr.ctx_pool[i].list,
 			&g_ife_hw_mgr.free_ctx_list);
 	}
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
index 2601190..5410858 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include/cam_isp_hw_mgr_intf.h
@@ -108,11 +108,13 @@
 /**
  * struct cam_isp_hw_sof_event_data - Event payload for CAM_HW_EVENT_SOF
  *
- * @timestamp:     Time stamp for the sof event
+ * @timestamp:   Time stamp for the sof event
+ * @boot_time:   Boot time stamp for the sof event
  *
  */
 struct cam_isp_hw_sof_event_data {
 	uint64_t       timestamp;
+	uint64_t       boot_time;
 };
 
 /**
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 34f8c41..053eb00 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
@@ -856,7 +856,27 @@
 	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;
-	path_data->crop_enable = true;
+
+	/* Enable RDI crop for single ife use case only */
+	switch (reserve->res_id) {
+	case CAM_IFE_PIX_PATH_RES_RDI_0:
+	case CAM_IFE_PIX_PATH_RES_RDI_1:
+	case CAM_IFE_PIX_PATH_RES_RDI_2:
+	case CAM_IFE_PIX_PATH_RES_RDI_3:
+		if (reserve->in_port->usage_type)
+			path_data->crop_enable = false;
+		else
+			path_data->crop_enable = true;
+
+		break;
+	case CAM_IFE_PIX_PATH_RES_IPP:
+		path_data->crop_enable = true;
+		break;
+	default:
+		rc = -EINVAL;
+		goto end;
+	}
+
 	CAM_DBG(CAM_ISP, "Res id: %d height:%d line_start %d line_stop %d",
 		reserve->res_id, reserve->in_port->height,
 		reserve->in_port->line_start, reserve->in_port->line_stop);
@@ -1884,6 +1904,7 @@
 	struct cam_ife_csid_reg_offset       *csid_reg;
 	struct cam_hw_soc_info               *soc_info;
 	struct cam_ife_csid_rdi_reg_offset   *rdi_reg;
+	struct timespec64 ts;
 	uint32_t  time_32, id;
 
 	time_stamp = (struct cam_csid_get_time_stamp_args  *)cmd_args;
@@ -1932,6 +1953,10 @@
 		CAM_IFE_CSID_QTIMER_MUL_FACTOR,
 		CAM_IFE_CSID_QTIMER_DIV_FACTOR);
 
+	get_monotonic_boottime64(&ts);
+	time_stamp->boot_timestamp = (uint64_t)((ts.tv_sec * 1000000000) +
+		ts.tv_nsec);
+
 	return 0;
 }
 
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 ceeacbe..8911f99 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
@@ -138,12 +138,13 @@
 /**
  * struct cam_csid_get_time_stamp_args-  time stamp capture arguments
  * @res_node :   resource to get the time stamp
- * @ time_stamp_val : captured time stamp
- *
+ * @time_stamp_val : captured time stamp
+ * @boot_timestamp : boot time stamp
  */
 struct cam_csid_get_time_stamp_args {
 	struct cam_isp_resource_node      *node_res;
 	uint64_t                           time_stamp_val;
+	uint64_t                           boot_timestamp;
 };
 
 /**
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 969b75a..be4db8a 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
@@ -863,34 +863,16 @@
 			rsrc_data->stride = rsrc_data->width;
 			break;
 		case CAM_FORMAT_PLAIN16_10:
+		case CAM_FORMAT_PLAIN16_12:
+		case CAM_FORMAT_PLAIN16_14:
+		case CAM_FORMAT_PLAIN16_16:
+		case CAM_FORMAT_PLAIN32_20:
 			rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH;
 			rsrc_data->height = 0;
 			rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE;
 			rsrc_data->pack_fmt = 0x0;
 			rsrc_data->en_cfg = 0x3;
 			break;
-		case CAM_FORMAT_PLAIN16_12:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x3;
-			rsrc_data->width = rsrc_data->width * 2;
-			rsrc_data->stride = rsrc_data->width;
-			break;
-		case CAM_FORMAT_PLAIN16_14:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x4;
-			rsrc_data->width = rsrc_data->width * 2;
-			rsrc_data->stride = rsrc_data->width;
-			break;
-		case CAM_FORMAT_PLAIN16_16:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x5;
-			rsrc_data->width = rsrc_data->width * 2;
-			rsrc_data->stride = rsrc_data->width;
-			break;
-		case CAM_FORMAT_PLAIN32_20:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x9;
-			break;
 		case CAM_FORMAT_PLAIN64:
 			rsrc_data->en_cfg = 0x1;
 			rsrc_data->pack_fmt = 0xA;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
index 643b0afc..93e4249 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
@@ -646,6 +646,10 @@
 			CAM_SMMU_REGION_IO);
 		if (rc)
 			goto map_fail;
+	} else {
+		rc = ion_handle_get_size(tbl.client, ion_hdl, &len);
+		if (rc)
+			return rc;
 	}
 
 	idx = cam_mem_get_slot();
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
index d625a20..517b7df 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -123,6 +123,7 @@
 {
 	struct sync_table_row *row = NULL;
 	struct sync_callback_info *sync_cb, *temp;
+	bool found = false;
 
 	if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0)
 		return -EINVAL;
@@ -145,11 +146,12 @@
 			sync_cb->cb_data == userdata) {
 			list_del_init(&sync_cb->list);
 			kfree(sync_cb);
+			found = true;
 		}
 	}
 
 	spin_unlock_bh(&sync_dev->row_spinlocks[sync_obj]);
-	return 0;
+	return found ? 0 : -ENOENT;
 }
 
 int cam_sync_signal(int32_t sync_obj, uint32_t status)
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 690b28b..eec0875 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014,2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,2016-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
 #include <linux/regmap.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/notifier.h>
 #include <linux/qpnp/qpnp-misc.h>
 
 #define QPNP_MISC_DEV_NAME "qcom,qpnp-misc"
@@ -29,8 +30,20 @@
 #define PWM_SEL_MAX		0x03
 #define GP_DRIVER_EN_BIT	BIT(0)
 
+enum twm {
+	TWM_MODE_1 = 1,
+	TWM_MODE_2,
+	TWM_MODE_3,
+};
+
+enum twm_attrib {
+	TWM_ENABLE,
+	TWM_EXIT,
+};
+
 static DEFINE_MUTEX(qpnp_misc_dev_list_mutex);
 static LIST_HEAD(qpnp_misc_dev_list);
+static RAW_NOTIFIER_HEAD(twm_notifier);
 
 struct qpnp_misc_version {
 	u8	subtype;
@@ -55,10 +68,14 @@
 	struct device			*dev;
 	struct regmap			*regmap;
 	struct qpnp_misc_version	version;
+	struct class			twm_class;
 
+	u8				twm_mode;
 	u32				base;
 	u8				pwm_sel;
 	bool				enable_gp_driver;
+	bool				support_twm_config;
+	bool				twm_enable;
 };
 
 static const struct of_device_id qpnp_misc_match_table[] = {
@@ -211,12 +228,100 @@
 	return __misc_irqs_available(mdev_found);
 }
 
+#define MISC_SPARE_1		0x50
+#define MISC_SPARE_2		0x51
+#define ENABLE_TWM_MODE		0x80
+#define DISABLE_TWM_MODE	0x0
+#define TWM_EXIT_BIT		BIT(0)
+static ssize_t twm_enable_store(struct class *c,
+			struct class_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct qpnp_misc_dev *mdev = container_of(c,
+			struct qpnp_misc_dev, twm_class);
+	u8 val = 0;
+	ssize_t rc = 0;
+
+	rc = kstrtou8(buf, 10, &val);
+	if (rc < 0)
+		return rc;
+
+	mdev->twm_enable = val ? true : false;
+
+	/* Notify the TWM state */
+	raw_notifier_call_chain(&twm_notifier,
+		mdev->twm_enable ? PMIC_TWM_ENABLE : PMIC_TWM_CLEAR, NULL);
+
+	return count;
+}
+
+static ssize_t twm_enable_show(struct class *c,
+			struct class_attribute *attr, char *buf)
+{
+	struct qpnp_misc_dev *mdev = container_of(c,
+			struct qpnp_misc_dev, twm_class);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", mdev->twm_enable);
+}
+
+static ssize_t twm_exit_show(struct class *c,
+			struct class_attribute *attr, char *buf)
+{
+	struct qpnp_misc_dev *mdev = container_of(c,
+			struct qpnp_misc_dev, twm_class);
+	int rc = 0;
+	u8 val = 0;
+
+	rc = qpnp_read_byte(mdev, MISC_SPARE_1, &val);
+	if (rc < 0) {
+		pr_err("Failed to read TWM enable (misc_spare_1) rc=%d\n", rc);
+		return rc;
+	}
+
+	pr_debug("TWM_EXIT (misc_spare_1) register = 0x%02x\n", val);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", !!(val & TWM_EXIT_BIT));
+}
+
+static struct class_attribute twm_attributes[] = {
+	[TWM_ENABLE]		= __ATTR(twm_enable, 0644,
+					twm_enable_show, twm_enable_store),
+	[TWM_EXIT]		= __ATTR(twm_exit, 0644,
+					twm_exit_show, NULL),
+	__ATTR_NULL,
+};
+
+int qpnp_misc_twm_notifier_register(struct notifier_block *nb)
+{
+	return raw_notifier_chain_register(&twm_notifier, nb);
+}
+EXPORT_SYMBOL(qpnp_misc_twm_notifier_register);
+
+int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb)
+{
+	return raw_notifier_chain_unregister(&twm_notifier, nb);
+}
+EXPORT_SYMBOL(qpnp_misc_twm_notifier_unregister);
+
 static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
 {
 	struct device_node *node = mdev->dev->of_node;
 	u32 val;
 	int rc;
 
+	if (of_property_read_bool(mdev->dev->of_node,
+				"qcom,support-twm-config")) {
+		mdev->support_twm_config = true;
+		mdev->twm_mode = TWM_MODE_3;
+		rc = of_property_read_u8(mdev->dev->of_node, "qcom,twm-mode",
+							&mdev->twm_mode);
+		if (!rc && (mdev->twm_mode < TWM_MODE_1 ||
+				mdev->twm_mode > TWM_MODE_3)) {
+			pr_err("Invalid TWM mode %d\n", mdev->twm_mode);
+			return -EINVAL;
+		}
+	}
+
 	rc = of_property_read_u32(node, "reg", &mdev->base);
 	if (rc < 0 || !mdev->base) {
 		dev_err(mdev->dev, "Base address not defined or invalid\n");
@@ -270,6 +375,18 @@
 		break;
 	}
 
+	if (mdev->support_twm_config) {
+		mdev->twm_class.name = "pmic_twm",
+		mdev->twm_class.owner = THIS_MODULE,
+		mdev->twm_class.class_attrs = twm_attributes;
+
+		rc = class_register(&mdev->twm_class);
+		if (rc < 0) {
+			pr_err("Failed to register pmic_twm class rc=%d\n", rc);
+			return rc;
+		}
+	}
+
 	return 0;
 }
 
@@ -283,6 +400,7 @@
 		return -ENOMEM;
 
 	mdev->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, mdev);
 	mdev->regmap = dev_get_regmap(mdev->dev->parent, NULL);
 	if (!mdev->regmap) {
 		dev_err(mdev->dev, "Parent regmap is unavailable\n");
@@ -325,8 +443,33 @@
 	return 0;
 }
 
+static void qpnp_misc_shutdown(struct platform_device *pdev)
+{
+	struct qpnp_misc_dev *mdev = dev_get_drvdata(&pdev->dev);
+	int rc;
+
+	if (mdev->support_twm_config) {
+		rc = qpnp_write_byte(mdev, MISC_SPARE_2,
+				mdev->twm_enable ? mdev->twm_mode : 0x0);
+		if (rc < 0)
+			pr_err("Failed to write MISC_SPARE_2 (twm_mode) val=%d rc=%d\n",
+				mdev->twm_enable ? mdev->twm_mode : 0x0, rc);
+
+		rc = qpnp_write_byte(mdev, MISC_SPARE_1,
+				mdev->twm_enable ? ENABLE_TWM_MODE : 0x0);
+		if (rc < 0)
+			pr_err("Failed to write MISC_SPARE_1 (twm_state) val=%d rc=%d\n",
+				mdev->twm_enable ? ENABLE_TWM_MODE : 0x0, rc);
+
+		pr_debug("PMIC configured for TWM-%s MODE=%d\n",
+				mdev->twm_enable ? "enabled" : "disabled",
+				mdev->twm_mode);
+	}
+}
+
 static struct platform_driver qpnp_misc_driver = {
 	.probe	= qpnp_misc_probe,
+	.shutdown = qpnp_misc_shutdown,
 	.driver	= {
 		.name		= QPNP_MISC_DEV_NAME,
 		.owner		= THIS_MODULE,
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index a3cf6c2..b1fbbd8 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -137,11 +137,17 @@
 {
 	struct cnss_plat_data *plat_priv =
 		((struct seq_file *)fp->private_data)->private;
+	struct cnss_pci_data *pci_priv;
 	char buf[64];
 	char *cmd;
 	unsigned int len = 0;
 	int ret = 0;
 
+	if (!plat_priv)
+		return -ENODEV;
+
+	pci_priv = plat_priv->bus_priv;
+
 	len = min(count, sizeof(buf) - 1);
 	if (copy_from_user(buf, user_buf, len))
 		return -EFAULT;
@@ -157,11 +163,11 @@
 		ret = cnss_pci_init(plat_priv);
 	} else if (sysfs_streq(cmd, "download")) {
 		set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
-		ret = cnss_pci_start_mhi(plat_priv->bus_priv);
+		ret = cnss_pci_start_mhi(pci_priv);
 	} else if (sysfs_streq(cmd, "linkup")) {
-		ret = cnss_resume_pci_link(plat_priv->bus_priv);
+		ret = cnss_resume_pci_link(pci_priv);
 	} else if (sysfs_streq(cmd, "linkdown")) {
-		ret = cnss_suspend_pci_link(plat_priv->bus_priv);
+		ret = cnss_suspend_pci_link(pci_priv);
 	} else if (sysfs_streq(cmd, "powerup")) {
 		set_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
 		ret = cnss_driver_event_post(plat_priv,
@@ -172,6 +178,8 @@
 					     CNSS_DRIVER_EVENT_POWER_DOWN,
 					     0, NULL);
 		clear_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state);
+	} else if (sysfs_streq(cmd, "assert")) {
+		ret = cnss_force_fw_assert(&pci_priv->pci_dev->dev);
 	} else {
 		cnss_pr_err("Device boot debugfs command is invalid\n");
 		ret = -EINVAL;
@@ -195,6 +203,7 @@
 	seq_puts(s, "linkdown: bring down PCIe link\n");
 	seq_puts(s, "powerup: full power on sequence to boot device, download FW and do QMI handshake with FW\n");
 	seq_puts(s, "shutdown: full power off sequence to shutdown device\n");
+	seq_puts(s, "assert: trigger firmware assert\n");
 
 	return 0;
 }
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index 098631e..9d1fbf9 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -1458,8 +1458,7 @@
 		ret = mhi_pm_resume(pci_priv->mhi_ctrl);
 		break;
 	case CNSS_MHI_TRIGGER_RDDM:
-		cnss_pr_dbg("Bypass MHI state: %s(%d)\n",
-			    cnss_mhi_state_to_str(mhi_state), mhi_state);
+		ret = mhi_force_rddm_mode(pci_priv->mhi_ctrl);
 		break;
 	default:
 		cnss_pr_err("Unhandled MHI state (%d)\n", mhi_state);
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
index db6f689..4558530 100644
--- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
@@ -526,12 +526,14 @@
 		ep_pcie_write_reg(dev->parf, PCIE20_PARF_DEVICE_TYPE, 0x0);
 
 		/* adjust DBI base address */
-		if (dev->dbi_base_reg)
-			writel_relaxed(0x3FFFE000,
-				dev->parf + dev->dbi_base_reg);
-		else
-			writel_relaxed(0x3FFFE000,
-				dev->parf + PCIE20_PARF_DBI_BASE_ADDR);
+		if (dev->phy_rev < 6) {
+			if (dev->dbi_base_reg)
+				writel_relaxed(0x3FFFE000,
+					dev->parf + dev->dbi_base_reg);
+			else
+				writel_relaxed(0x3FFFE000,
+					dev->parf + PCIE20_PARF_DBI_BASE_ADDR);
+		}
 
 		/* Configure PCIe core to support 1GB aperture */
 		if (dev->slv_space_reg)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index da60ff5..065f97f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -373,7 +373,7 @@
 {
 	ipa3_active_clients_log_print_table(active_clients_table_buf,
 			IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE);
-	IPAERR("%s", active_clients_table_buf);
+	IPAERR("%s\n", active_clients_table_buf);
 
 	return NOTIFY_DONE;
 }
@@ -4280,6 +4280,9 @@
 	if (res)
 		IPAERR("uC panic handler failed %d\n", res);
 
+	if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) != 0)
+		ipahal_print_all_regs();
+
 	return NOTIFY_DONE;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 07773eb..ee9c49c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -1894,6 +1894,16 @@
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
+static ssize_t ipa3_read_ipahal_regs(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+	ipahal_print_all_regs();
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+	return 0;
+}
+
 static void ipa_dump_status(struct ipahal_pkt_status *status)
 {
 	IPA_DUMP_STATUS_FIELD(status_opcode);
@@ -2160,6 +2170,10 @@
 		"enable_low_prio_print", IPA_WRITE_ONLY_MODE, NULL, {
 			.write = ipa3_enable_ipc_low,
 		}
+	}, {
+		"ipa_dump_regs", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_ipahal_regs,
+		}
 	}
 };
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 386ad51..6f87ece 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2116,6 +2116,7 @@
 int __ipa3_release_hdr_proc_ctx(u32 proc_ctx_hdl);
 int _ipa_read_ep_reg_v3_0(char *buf, int max_len, int pipe);
 int _ipa_read_ep_reg_v4_0(char *buf, int max_len, int pipe);
+int _ipa_read_ipahal_regs(void);
 void _ipa_enable_clks_v3_0(void);
 void _ipa_disable_clks_v3_0(void);
 struct device *ipa3_get_dma_dev(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 8f78d56..26b7f0f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -46,6 +46,15 @@
 			IPAHAL_DRV_NAME " %s:%d " fmt, ## args); \
 	} while (0)
 
+#define IPAHAL_DBG_REG(fmt, args...) \
+	do { \
+		pr_err(fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			" %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			" %s:%d " fmt, ## args); \
+	} while (0)
+
 #define IPAHAL_ERR_RL(fmt, args...) \
 		do { \
 			pr_err_ratelimited_ipa(IPAHAL_DRV_NAME " %s:%d " fmt, \
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index 66837d0..ce59488 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -18,6 +18,8 @@
 #include "ipahal_reg.h"
 #include "ipahal_reg_i.h"
 
+#define IPA_MAX_MSG_LEN 4096
+
 static const char *ipareg_name_to_str[IPA_REG_MAX] = {
 	__stringify(IPA_ROUTE),
 	__stringify(IPA_IRQ_STTS_EE_n),
@@ -26,6 +28,9 @@
 	__stringify(IPA_IRQ_SUSPEND_INFO_EE_n),
 	__stringify(IPA_SUSPEND_IRQ_EN_EE_n),
 	__stringify(IPA_SUSPEND_IRQ_CLR_EE_n),
+	__stringify(IPA_HOLB_DROP_IRQ_INFO_EE_n),
+	__stringify(IPA_HOLB_DROP_IRQ_EN_EE_n),
+	__stringify(IPA_HOLB_DROP_IRQ_CLR_EE_n),
 	__stringify(IPA_BCR),
 	__stringify(IPA_ENABLED_PIPES),
 	__stringify(IPA_COMP_SW_RESET),
@@ -35,7 +40,20 @@
 	__stringify(IPA_SPARE_REG_1),
 	__stringify(IPA_SPARE_REG_2),
 	__stringify(IPA_COMP_CFG),
+	__stringify(IPA_STATE_TX_WRAPPER),
+	__stringify(IPA_STATE_TX1),
+	__stringify(IPA_STATE_FETCHER),
+	__stringify(IPA_STATE_FETCHER_MASK),
+	__stringify(IPA_STATE_DFETCHER),
+	__stringify(IPA_STATE_ACL),
+	__stringify(IPA_STATE),
+	__stringify(IPA_STATE_RX_ACTIVE),
+	__stringify(IPA_STATE_TX0),
 	__stringify(IPA_STATE_AGGR_ACTIVE),
+	__stringify(IPA_STATE_GSI_TLV),
+	__stringify(IPA_STATE_GSI_AOS),
+	__stringify(IPA_STATE_GSI_IF),
+	__stringify(IPA_STATE_GSI_SKIP),
 	__stringify(IPA_ENDP_INIT_HDR_n),
 	__stringify(IPA_ENDP_INIT_HDR_EXT_n),
 	__stringify(IPA_ENDP_INIT_AGGR_n),
@@ -46,6 +64,7 @@
 	__stringify(IPA_ENDP_INIT_CONN_TRACK_n),
 	__stringify(IPA_ENDP_INIT_CTRL_n),
 	__stringify(IPA_ENDP_INIT_CTRL_SCND_n),
+	__stringify(IPA_ENDP_INIT_CTRL_STATUS_n),
 	__stringify(IPA_ENDP_INIT_HOL_BLOCK_EN_n),
 	__stringify(IPA_ENDP_INIT_HOL_BLOCK_TIMER_n),
 	__stringify(IPA_ENDP_INIT_DEAGGR_n),
@@ -55,6 +74,7 @@
 	__stringify(IPA_IRQ_EE_UC_n),
 	__stringify(IPA_ENDP_INIT_HDR_METADATA_MASK_n),
 	__stringify(IPA_ENDP_INIT_HDR_METADATA_n),
+	__stringify(IPA_ENDP_INIT_PROD_CFG_n),
 	__stringify(IPA_ENDP_INIT_RSRC_GRP_n),
 	__stringify(IPA_SHARED_MEM_SIZE),
 	__stringify(IPA_SRAM_DIRECT_ACCESS_n),
@@ -66,6 +86,8 @@
 	__stringify(IPA_SYS_PKT_PROC_CNTXT_BASE),
 	__stringify(IPA_LOCAL_PKT_PROC_CNTXT_BASE),
 	__stringify(IPA_ENDP_STATUS_n),
+	__stringify(IPA_ENDP_WEIGHTS_n),
+	__stringify(IPA_ENDP_YELLOW_RED_MARKER),
 	__stringify(IPA_ENDP_FILTER_ROUTER_HSH_CFG_n),
 	__stringify(IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n),
 	__stringify(IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n),
@@ -105,6 +127,12 @@
 	__stringify(IPA_STAT_ROUTER_IPV6_END_ID),
 	__stringify(IPA_STAT_DROP_CNT_BASE_n),
 	__stringify(IPA_STAT_DROP_CNT_MASK_n),
+	__stringify(IPA_SNOC_FEC_EE_n),
+	__stringify(IPA_FEC_ADDR_EE_n),
+	__stringify(IPA_FEC_ADDR_MSB_EE_n),
+	__stringify(IPA_FEC_ATTR_EE_n),
+	__stringify(IPA_MBIM_DEAGGR_FEC_ATTR_EE_n),
+	__stringify(IPA_GEN_DEAGGR_FEC_ATTR_EE_n),
 };
 
 static void ipareg_construct_dummy(enum ipahal_reg_name reg,
@@ -1651,6 +1679,9 @@
  * @parse - CB to parse register value to abstracted structure
  * @offset - register offset relative to base address
  * @n_ofst - N parameterized register sub-offset
+ * @n_start - starting n for n_registers
+ * @n_end - ending n for n_registers
+ * @en_print - enable this register to be printed when the device crashes
  */
 struct ipahal_reg_obj {
 	void (*construct)(enum ipahal_reg_name reg, const void *fields,
@@ -1659,6 +1690,9 @@
 		u32 val);
 	u32 offset;
 	u32 n_ofst;
+	int n_start;
+	int n_end;
+	bool en_print;
 };
 
 /*
@@ -1676,365 +1710,543 @@
 	/* IPAv3 */
 	[IPA_HW_v3_0][IPA_ROUTE] = {
 		ipareg_construct_route, ipareg_parse_dummy,
-		0x00000048, 0},
+		0x00000048, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_IRQ_STTS_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00003008, 0x1000},
+		0x00003008, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_IRQ_EN_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000300c, 0x1000},
+		0x0000300c, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_IRQ_CLR_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00003010, 0x1000},
+		0x00003010, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_IRQ_SUSPEND_INFO_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00003098, 0x1000},
+		0x00003098, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_BCR] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x000001D0, 0},
+		0x000001D0, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENABLED_PIPES] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000038, 0},
+		0x00000038, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_COMP_SW_RESET] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000040, 0},
+		0x00000040, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_VERSION] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000034, 0},
+		0x00000034, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_TAG_TIMER] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000060, 0 },
+		0x00000060, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_COMP_HW_VERSION] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000030, 0},
+		0x00000030, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SPARE_REG_1] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00005090, 0},
+		0x00005090, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SPARE_REG_2] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00005094, 0},
+		0x00005094, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_COMP_CFG] = {
 		ipareg_construct_comp_cfg, ipareg_parse_comp_cfg,
-		0x0000003C, 0},
+		0x0000003C, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_STATE_AGGR_ACTIVE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000010C, 0},
+		0x0000010C, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_n] = {
 		ipareg_construct_endp_init_hdr_n, ipareg_parse_dummy,
-		0x00000810, 0x70},
+		0x00000810, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_EXT_n] = {
 		ipareg_construct_endp_init_hdr_ext_n, ipareg_parse_dummy,
-		0x00000814, 0x70},
+		0x00000814, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_AGGR_n] = {
 		ipareg_construct_endp_init_aggr_n,
 		ipareg_parse_endp_init_aggr_n,
-		0x00000824, 0x70},
+		0x00000824, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_AGGR_FORCE_CLOSE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x000001EC, 0},
+		0x000001EC, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_ROUTE_n] = {
 		ipareg_construct_endp_init_route_n, ipareg_parse_dummy,
-		0x00000828, 0x70},
+		0x00000828, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_MODE_n] = {
 		ipareg_construct_endp_init_mode_n, ipareg_parse_dummy,
-		0x00000820, 0x70},
+		0x00000820, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_NAT_n] = {
 		ipareg_construct_endp_init_nat_n, ipareg_parse_dummy,
-		0x0000080C, 0x70},
+		0x0000080C, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_n] = {
 		ipareg_construct_endp_init_ctrl_n,
 		ipareg_parse_endp_init_ctrl_n,
-		0x00000800, 0x70},
+		0x00000800, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_CTRL_SCND_n] = {
 		ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy,
-		0x00000804, 0x70 },
+		0x00000804, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_HOL_BLOCK_EN_n] = {
 		ipareg_construct_endp_init_hol_block_en_n,
 		ipareg_parse_dummy,
-		0x0000082c, 0x70},
+		0x0000082c, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_HOL_BLOCK_TIMER_n] = {
 		ipareg_construct_endp_init_hol_block_timer_n,
 		ipareg_parse_dummy,
-		0x00000830, 0x70},
+		0x00000830, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_DEAGGR_n] = {
 		ipareg_construct_endp_init_deaggr_n,
 		ipareg_parse_dummy,
-		0x00000834, 0x70},
+		0x00000834, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_SEQ_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000083C, 0x70},
+		0x0000083C, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DEBUG_CNT_REG_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000600, 0x4},
+		0x00000600, 0x4, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_CFG_n] = {
 		ipareg_construct_endp_init_cfg_n, ipareg_parse_dummy,
-		0x00000808, 0x70},
+		0x00000808, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_IRQ_EE_UC_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000301c, 0x1000},
+		0x0000301c, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_METADATA_MASK_n] = {
 		ipareg_construct_endp_init_hdr_metadata_mask_n,
 		ipareg_parse_dummy,
-		0x00000818, 0x70},
+		0x00000818, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_HDR_METADATA_n] = {
 		ipareg_construct_endp_init_hdr_metadata_n,
 		ipareg_parse_dummy,
-		0x0000081c, 0x70},
+		0x0000081c, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_INIT_RSRC_GRP_n] = {
 		ipareg_construct_endp_init_rsrc_grp_n,
 		ipareg_parse_dummy,
-		0x00000838, 0x70},
+		0x00000838, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SHARED_MEM_SIZE] = {
 		ipareg_construct_dummy, ipareg_parse_shared_mem_size,
-		0x00000054, 0},
+		0x00000054, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SRAM_DIRECT_ACCESS_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00007000, 0x4},
+		0x00007000, 0x4, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DEBUG_CNT_CTRL_n] = {
 		ipareg_construct_debug_cnt_ctrl_n, ipareg_parse_dummy,
-		0x00000640, 0x4},
+		0x00000640, 0x4, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_UC_MAILBOX_m_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00032000, 0x4},
+		0x00032000, 0x4, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_FILT_ROUT_HASH_FLUSH] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000090, 0},
+		0x00000090, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SINGLE_NDP_MODE] = {
 		ipareg_construct_single_ndp_mode, ipareg_parse_single_ndp_mode,
-		0x00000068, 0},
+		0x00000068, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_QCNCM] = {
 		ipareg_construct_qcncm, ipareg_parse_qcncm,
-		0x00000064, 0},
+		0x00000064, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SYS_PKT_PROC_CNTXT_BASE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x000001e0, 0},
+		0x000001e0, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_LOCAL_PKT_PROC_CNTXT_BASE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x000001e8, 0},
+		0x000001e8, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_STATUS_n] = {
 		ipareg_construct_endp_status_n, ipareg_parse_dummy,
-		0x00000840, 0x70},
+		0x00000840, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_ENDP_FILTER_ROUTER_HSH_CFG_n] = {
 		ipareg_construct_hash_cfg_n, ipareg_parse_hash_cfg_n,
-		0x0000085C, 0x70},
+		0x0000085C, 0x70, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x00000400, 0x20},
+		0x00000400, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x00000404, 0x20},
+		0x00000404, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_45_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x00000408, 0x20},
+		0x00000408, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_SRC_RSRC_GRP_67_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x0000040C, 0x20},
+		0x0000040C, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DST_RSRC_GRP_01_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x00000500, 0x20},
+		0x00000500, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DST_RSRC_GRP_23_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x00000504, 0x20},
+		0x00000504, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DST_RSRC_GRP_45_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x00000508, 0x20},
+		0x00000508, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DST_RSRC_GRP_67_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy, ipareg_parse_dummy,
-		0x0000050c, 0x20},
+		0x0000050c, 0x20, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MIN_DEPTH_0] = {
 		ipareg_construct_rx_hps_clients_depth0, ipareg_parse_dummy,
-		0x000023C4, 0},
+		0x000023C4, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MIN_DEPTH_1] = {
 		ipareg_construct_rx_hps_clients_depth1, ipareg_parse_dummy,
-		0x000023C8, 0},
+		0x000023C8, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MAX_DEPTH_0] = {
 		ipareg_construct_rx_hps_clients_depth0, ipareg_parse_dummy,
-		0x000023CC, 0},
+		0x000023CC, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_RX_HPS_CLIENTS_MAX_DEPTH_1] = {
 		ipareg_construct_rx_hps_clients_depth1, ipareg_parse_dummy,
-		0x000023D0, 0},
+		0x000023D0, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_QSB_MAX_WRITES] = {
 		ipareg_construct_qsb_max_writes, ipareg_parse_dummy,
-		0x00000074, 0},
+		0x00000074, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_QSB_MAX_READS] = {
 		ipareg_construct_qsb_max_reads, ipareg_parse_dummy,
-		0x00000078, 0},
+		0x00000078, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_DPS_SEQUENCER_FIRST] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0001e000, 0},
+		0x0001e000, 0, 0, 0, 0},
 	[IPA_HW_v3_0][IPA_HPS_SEQUENCER_FIRST] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0001e080, 0},
+		0x0001e080, 0, 0, 0, 0},
 
 
 	/* IPAv3.1 */
 	[IPA_HW_v3_1][IPA_IRQ_SUSPEND_INFO_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00003030, 0x1000},
+		0x00003030, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_1][IPA_SUSPEND_IRQ_EN_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00003034, 0x1000},
+		0x00003034, 0x1000, 0, 0, 0},
 	[IPA_HW_v3_1][IPA_SUSPEND_IRQ_CLR_EE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00003038, 0x1000},
+		0x00003038, 0x1000, 0, 0, 0},
 
 
 	/* IPAv3.5 */
 	[IPA_HW_v3_5][IPA_TX_CFG] = {
 		ipareg_construct_tx_cfg, ipareg_parse_tx_cfg,
-		0x000001FC, 0},
+		0x000001FC, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
-		0x00000400, 0x20},
+		0x00000400, 0x20, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
-		0x00000404, 0x20},
+		0x00000404, 0x20, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_45_RSRC_TYPE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_SRC_RSRC_GRP_67_RSRC_TYPE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_DST_RSRC_GRP_01_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
-		0x00000500, 0x20},
+		0x00000500, 0x20, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_DST_RSRC_GRP_23_RSRC_TYPE_n] = {
 		ipareg_construct_rsrg_grp_xy_v3_5, ipareg_parse_dummy,
-		0x00000504, 0x20},
+		0x00000504, 0x20, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_DST_RSRC_GRP_45_RSRC_TYPE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_DST_RSRC_GRP_67_RSRC_TYPE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_ENDP_INIT_RSRC_GRP_n] = {
 		ipareg_construct_endp_init_rsrc_grp_n_v3_5,
 		ipareg_parse_dummy,
-		0x00000838, 0x70},
+		0x00000838, 0x70, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MIN_DEPTH_0] = {
 		ipareg_construct_rx_hps_clients_depth0_v3_5,
 		ipareg_parse_dummy,
-		0x000023C4, 0},
+		0x000023C4, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MIN_DEPTH_1] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MAX_DEPTH_0] = {
 		ipareg_construct_rx_hps_clients_depth0_v3_5,
 		ipareg_parse_dummy,
-		0x000023CC, 0},
+		0x000023CC, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_RX_HPS_CLIENTS_MAX_DEPTH_1] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_SPARE_REG_1] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00002780, 0},
+		0x00002780, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_SPARE_REG_2] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00002784, 0},
+		0x00002784, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_IDLE_INDICATION_CFG] = {
 		ipareg_construct_idle_indication_cfg, ipareg_parse_dummy,
-		0x00000220, 0},
+		0x00000220, 0, 0, 0, 0},
 	[IPA_HW_v3_5][IPA_HPS_FTCH_ARB_QUEUE_WEIGHT] = {
 		ipareg_construct_hps_queue_weights,
-		ipareg_parse_hps_queue_weights, 0x000005a4, 0},
+		ipareg_parse_hps_queue_weights, 0x000005a4, 0, 0, 0, 0},
 
 	/* IPAv4.0 */
+	[IPA_HW_v4_0][IPA_IRQ_SUSPEND_INFO_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003030, 0x1000, 0, 1, 1},
+	[IPA_HW_v4_0][IPA_SUSPEND_IRQ_EN_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003034, 0x1000, 0, 1, 1},
+	[IPA_HW_v4_0][IPA_SUSPEND_IRQ_CLR_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003038, 0x1000, 0, 1, 1},
+	[IPA_HW_v4_0][IPA_IRQ_EN_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0000300c, 0x1000, 0, 1, 1},
+	[IPA_HW_v4_0][IPA_TAG_TIMER] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000060, 0, 0, 0, 1},
 	[IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_n] = {
 		ipareg_construct_endp_init_ctrl_n_v4_0, ipareg_parse_dummy,
-		0x00000800, 0x70 },
+		0x00000800, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_HDR_EXT_n] = {
+		ipareg_construct_endp_init_hdr_ext_n, ipareg_parse_dummy,
+		0x00000814, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_AGGR_n] = {
+		ipareg_construct_endp_init_aggr_n,
+		ipareg_parse_endp_init_aggr_n,
+		0x00000824, 0x70, 0, 23, 1},
 	[IPA_HW_v4_0][IPA_TX_CFG] = {
 		ipareg_construct_tx_cfg_v4_0, ipareg_parse_tx_cfg_v4_0,
-		0x000001FC, 0},
+		0x000001FC, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_DEBUG_CNT_REG_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_DEBUG_CNT_CTRL_n] = {
 		ipareg_construct_debug_cnt_ctrl_n, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_QCNCM] = {
 		ipareg_construct_qcncm, ipareg_parse_qcncm,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_SINGLE_NDP_MODE] = {
 		ipareg_construct_single_ndp_mode, ipareg_parse_single_ndp_mode,
-		-1, 0},
+		-1, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_QSB_MAX_READS] = {
 		ipareg_construct_qsb_max_reads_v4_0, ipareg_parse_dummy,
-		0x00000078, 0},
+		0x00000078, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_FILT_ROUT_HASH_FLUSH] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000014c, 0},
-	[IPA_HW_v4_0][IPA_STATE_AGGR_ACTIVE] = {
-		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x000000b4, 0},
+		0x0000014c, 0, 0, 0, 0},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_HDR_n] = {
+		ipareg_construct_endp_init_hdr_n, ipareg_parse_dummy,
+		0x00000810, 0x70, 0, 23, 1},
 	[IPA_HW_v4_0][IPA_ENDP_INIT_ROUTE_n] = {
 		ipareg_construct_endp_init_route_n, ipareg_parse_dummy,
-		-1, 0},
+		-1, 0, 0, 0, 0},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_MODE_n] = {
+		ipareg_construct_endp_init_mode_n, ipareg_parse_dummy,
+		0x00000820, 0x70, 0, 10, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_NAT_n] = {
+		ipareg_construct_endp_init_nat_n, ipareg_parse_dummy,
+		0x0000080C, 0x70, 0, 10, 1},
 	[IPA_HW_v4_0][IPA_ENDP_STATUS_n] = {
 		ipareg_construct_endp_status_n_v4_0, ipareg_parse_dummy,
-		0x00000840, 0x70},
-	[IPA_HW_v4_0][IPA_CLKON_CFG] = {
-		ipareg_construct_clkon_cfg, ipareg_parse_clkon_cfg,
-		0x00000044, 0},
+		0x00000840, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_FILTER_ROUTER_HSH_CFG_n] = {
+		ipareg_construct_hash_cfg_n, ipareg_parse_hash_cfg_n,
+		0x0000085C, 0x70, 0, 32, 1},
 	[IPA_HW_v4_0][IPA_ENDP_INIT_CONN_TRACK_n] = {
 		ipareg_construct_endp_init_conn_track_n,
 		ipareg_parse_dummy,
-		0x00000850, 0x70},
+		0x00000850, 0x70, 0, 10, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_SCND_n] = {
+		ipareg_construct_endp_init_ctrl_scnd_n, ipareg_parse_dummy,
+		0x00000804, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_HOL_BLOCK_EN_n] = {
+		ipareg_construct_endp_init_hol_block_en_n,
+		ipareg_parse_dummy,
+		0x0000082c, 0x70, 10, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_HOL_BLOCK_TIMER_n] = {
+		ipareg_construct_endp_init_hol_block_timer_n,
+		ipareg_parse_dummy,
+		0x00000830, 0x70, 10, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_DEAGGR_n] = {
+		ipareg_construct_endp_init_deaggr_n,
+		ipareg_parse_dummy,
+		0x00000834, 0x70, 0, 10, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_SEQ_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0000083C, 0x70, 0, 10, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_CFG_n] = {
+		ipareg_construct_endp_init_cfg_n, ipareg_parse_dummy,
+		0x00000808, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_IRQ_EE_UC_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0000301c, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_HDR_METADATA_MASK_n] = {
+		ipareg_construct_endp_init_hdr_metadata_mask_n,
+		ipareg_parse_dummy,
+		0x00000818, 0x70, 10, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_HDR_METADATA_n] = {
+		ipareg_construct_endp_init_hdr_metadata_n,
+		ipareg_parse_dummy,
+		0x0000081c, 0x70, 0, 10, 1},
+	[IPA_HW_v4_0][IPA_CLKON_CFG] = {
+		ipareg_construct_clkon_cfg, ipareg_parse_clkon_cfg,
+		0x00000044, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_QUOTA_BASE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000700, 0x4 },
+		0x00000700, 0x4, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_QUOTA_MASK_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000708, 0x4 },
+		0x00000708, 0x4, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_TETHERING_BASE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000710, 0x4 },
+		0x00000710, 0x4, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_TETHERING_MASK_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000718, 0x4 },
+		0x00000718, 0x4, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_BASE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000720, 0x0 },
+		0x00000720, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_BASE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000724, 0x0 },
+		0x00000724, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_BASE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000728, 0x0 },
+		0x00000728, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_BASE] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000072C, 0x0 },
+		0x0000072C, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_START_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000730, 0x0 },
+		0x00000730, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_START_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000734, 0x0 },
+		0x00000734, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_START_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000738, 0x0 },
+		0x00000738, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_START_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000073C, 0x0 },
+		0x0000073C, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_FILTER_IPV4_END_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000740, 0x0 },
+		0x00000740, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_FILTER_IPV6_END_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000744, 0x0 },
+		0x00000744, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV4_END_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000748, 0x0 },
+		0x00000748, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_ROUTER_IPV6_END_ID] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x0000074C, 0x0 },
+		0x0000074C, 0, 0, 0, 0},
 	[IPA_HW_v4_0][IPA_STAT_DROP_CNT_BASE_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000750, 0x4 },
+		0x00000750, 0x4, 3, 1},
 	[IPA_HW_v4_0][IPA_STAT_DROP_CNT_MASK_n] = {
 		ipareg_construct_dummy, ipareg_parse_dummy,
-		0x00000758, 0x4 },
+		0x00000758, 0x4, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_TX_WRAPPER] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000090, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_TX1] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000094, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_FETCHER] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000098, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_FETCHER_MASK] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0000009C, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_DFETCHER] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000A0, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_ACL] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000A4, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000A8, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_RX_ACTIVE] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000AC, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_TX0] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000B0, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_AGGR_ACTIVE] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000B4, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_GSI_TLV] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000B8, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_GSI_AOS] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000B8, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_GSI_IF] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000C0, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_STATE_GSI_SKIP] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x000000C4, 0, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_SNOC_FEC_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003018, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_FEC_ADDR_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003020, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_FEC_ADDR_MSB_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003024, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_FEC_ATTR_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003028, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_MBIM_DEAGGR_FEC_ATTR_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003028, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_GEN_DEAGGR_FEC_ATTR_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003028, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_INFO_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0000303C, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_EN_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003040, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_HOLB_DROP_IRQ_CLR_EE_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00003044, 0x1000, 0, 0, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_CTRL_STATUS_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000864, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_PROD_CFG_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000CC8, 0x70, 10, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_INIT_RSRC_GRP_n] = {
+		ipareg_construct_endp_init_rsrc_grp_n_v3_5,
+		ipareg_parse_dummy,
+		0x00000838, 0x70, 0, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_WEIGHTS_n] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000CA4, 0x70, 10, 23, 1},
+	[IPA_HW_v4_0][IPA_ENDP_YELLOW_RED_MARKER] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x00000CC0, 0x70, 10, 23, 1},
 };
 
+int ipahal_print_all_regs(void)
+{
+	int i, j;
+
+	IPAHAL_DBG("Printing all registers for ipa_hw_type %d\n",
+		ipahal_ctx->hw_type);
+
+	if ((ipahal_ctx->hw_type < IPA_HW_v4_0) ||
+		(ipahal_ctx->hw_type >= IPA_HW_MAX)) {
+		IPAHAL_ERR("invalid IPA HW type (%d)\n", ipahal_ctx->hw_type);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < IPA_REG_MAX ; i++) {
+		if (!ipahal_reg_objs[IPA_HW_v4_0][i].en_print)
+			continue;
+
+		j = ipahal_reg_objs[ipahal_ctx->hw_type][i].n_start;
+
+		if (j == ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end)
+			IPAHAL_DBG_REG("%s=0x%x\n", ipahal_reg_name_str(i),
+				ipahal_read_reg_n(i, j));
+
+		for (; j < ipahal_reg_objs[ipahal_ctx->hw_type][i].n_end; j++)
+			IPAHAL_DBG_REG("%s_%u=0x%x\n", ipahal_reg_name_str(i),
+				j, ipahal_read_reg_n(i, j));
+	}
+	return 0;
+}
+
 /*
  * ipahal_reg_init() - Build the registers information table
  *  See ipahal_reg_objs[][] comments
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 2675771..7e8e8ba 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -29,6 +29,9 @@
 	IPA_IRQ_SUSPEND_INFO_EE_n,
 	IPA_SUSPEND_IRQ_EN_EE_n,
 	IPA_SUSPEND_IRQ_CLR_EE_n,
+	IPA_HOLB_DROP_IRQ_INFO_EE_n,
+	IPA_HOLB_DROP_IRQ_EN_EE_n,
+	IPA_HOLB_DROP_IRQ_CLR_EE_n,
 	IPA_BCR,
 	IPA_ENABLED_PIPES,
 	IPA_COMP_SW_RESET,
@@ -38,7 +41,20 @@
 	IPA_SPARE_REG_1,
 	IPA_SPARE_REG_2,
 	IPA_COMP_CFG,
+	IPA_STATE_TX_WRAPPER,
+	IPA_STATE_TX1,
+	IPA_STATE_FETCHER,
+	IPA_STATE_FETCHER_MASK,
+	IPA_STATE_DFETCHER,
+	IPA_STATE_ACL,
+	IPA_STATE,
+	IPA_STATE_RX_ACTIVE,
+	IPA_STATE_TX0,
 	IPA_STATE_AGGR_ACTIVE,
+	IPA_STATE_GSI_TLV,
+	IPA_STATE_GSI_AOS,
+	IPA_STATE_GSI_IF,
+	IPA_STATE_GSI_SKIP,
 	IPA_ENDP_INIT_HDR_n,
 	IPA_ENDP_INIT_HDR_EXT_n,
 	IPA_ENDP_INIT_AGGR_n,
@@ -49,6 +65,7 @@
 	IPA_ENDP_INIT_CONN_TRACK_n,
 	IPA_ENDP_INIT_CTRL_n,
 	IPA_ENDP_INIT_CTRL_SCND_n,
+	IPA_ENDP_INIT_CTRL_STATUS_n,
 	IPA_ENDP_INIT_HOL_BLOCK_EN_n,
 	IPA_ENDP_INIT_HOL_BLOCK_TIMER_n,
 	IPA_ENDP_INIT_DEAGGR_n,
@@ -58,6 +75,7 @@
 	IPA_IRQ_EE_UC_n,
 	IPA_ENDP_INIT_HDR_METADATA_MASK_n,
 	IPA_ENDP_INIT_HDR_METADATA_n,
+	IPA_ENDP_INIT_PROD_CFG_n,
 	IPA_ENDP_INIT_RSRC_GRP_n,
 	IPA_SHARED_MEM_SIZE,
 	IPA_SRAM_DIRECT_ACCESS_n,
@@ -69,6 +87,8 @@
 	IPA_SYS_PKT_PROC_CNTXT_BASE,
 	IPA_LOCAL_PKT_PROC_CNTXT_BASE,
 	IPA_ENDP_STATUS_n,
+	IPA_ENDP_WEIGHTS_n,
+	IPA_ENDP_YELLOW_RED_MARKER,
 	IPA_ENDP_FILTER_ROUTER_HSH_CFG_n,
 	IPA_SRC_RSRC_GRP_01_RSRC_TYPE_n,
 	IPA_SRC_RSRC_GRP_23_RSRC_TYPE_n,
@@ -108,6 +128,12 @@
 	IPA_STAT_ROUTER_IPV6_END_ID,
 	IPA_STAT_DROP_CNT_BASE_n,
 	IPA_STAT_DROP_CNT_MASK_n,
+	IPA_SNOC_FEC_EE_n,
+	IPA_FEC_ADDR_EE_n,
+	IPA_FEC_ADDR_MSB_EE_n,
+	IPA_FEC_ATTR_EE_n,
+	IPA_MBIM_DEAGGR_FEC_ATTR_EE_n,
+	IPA_GEN_DEAGGR_FEC_ATTR_EE_n,
 	IPA_REG_MAX,
 };
 
@@ -492,6 +518,9 @@
 	bool endp_delay;
 };
 
+
+int ipahal_print_all_regs(void);
+
 /*
  * ipahal_reg_name_str() - returns string that represent the register
  * @reg_name: [in] register name
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 348b287..6ce2161 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -165,6 +165,38 @@
 }
 EXPORT_SYMBOL(get_se_proto);
 
+/**
+ * get_se_m_fw() - Read the Firmware ver for the Main seqeuncer engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * Return:	Firmware version for the Main seqeuncer engine
+ */
+int get_se_m_fw(void __iomem *base)
+{
+	int fw_ver_m;
+
+	fw_ver_m = ((geni_read_reg(base, GENI_FW_REVISION_RO)
+			& FW_REV_VERSION_MSK));
+	return fw_ver_m;
+}
+EXPORT_SYMBOL(get_se_m_fw);
+
+/**
+ * get_se_s_fw() - Read the Firmware ver for the Secondry seqeuncer engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * Return:	Firmware version for the Secondry seqeuncer engine
+ */
+int get_se_s_fw(void __iomem *base)
+{
+	int fw_ver_s;
+
+	fw_ver_s = ((geni_read_reg(base, GENI_FW_S_REVISION_RO)
+			& FW_REV_VERSION_MSK));
+	return fw_ver_s;
+}
+EXPORT_SYMBOL(get_se_s_fw);
+
 static int se_geni_irq_en(void __iomem *base)
 {
 	unsigned int common_geni_m_irq_en;
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index d675e46..4d4b4cf 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -337,6 +337,7 @@
 	POWER_SUPPLY_ATTR(manufacturer),
 	POWER_SUPPLY_ATTR(serial_number),
 	POWER_SUPPLY_ATTR(battery_type),
+	POWER_SUPPLY_ATTR(cycle_counts),
 };
 
 static struct attribute *
diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c
new file mode 100644
index 0000000..3d74f7e
--- /dev/null
+++ b/drivers/power/supply/qcom/fg-alg.c
@@ -0,0 +1,606 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"ALG: %s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include "fg-alg.h"
+
+#define FULL_SOC_RAW		255
+#define CAPACITY_DELTA_DECIPCT	500
+
+/* Cycle counter APIs */
+
+/**
+ * restore_cycle_count -
+ * @counter: Cycle counter object
+ *
+ * Restores all the counters back from FG/QG during boot
+ *
+ */
+int restore_cycle_count(struct cycle_counter *counter)
+{
+	int rc = 0;
+
+	if (!counter)
+		return -ENODEV;
+
+	mutex_lock(&counter->lock);
+	rc = counter->restore_count(counter->data, counter->count,
+			BUCKET_COUNT);
+	if (rc < 0)
+		pr_err("failed to restore cycle counter rc=%d\n", rc);
+	mutex_unlock(&counter->lock);
+
+	return rc;
+}
+
+/**
+ * clear_cycle_count -
+ * @counter: Cycle counter object
+ *
+ * Clears all the counters stored by FG/QG when a battery is inserted
+ * or the profile is re-loaded.
+ *
+ */
+void clear_cycle_count(struct cycle_counter *counter)
+{
+	int rc = 0, i;
+
+	if (!counter)
+		return;
+
+	mutex_lock(&counter->lock);
+	memset(counter->count, 0, sizeof(counter->count));
+	for (i = 0; i < BUCKET_COUNT; i++) {
+		counter->started[i] = false;
+		counter->last_soc[i] = 0;
+	}
+
+	rc = counter->store_count(counter->data, counter->count, 0,
+			BUCKET_COUNT * 2);
+	if (rc < 0)
+		pr_err("failed to clear cycle counter rc=%d\n", rc);
+
+	mutex_unlock(&counter->lock);
+}
+
+/**
+ * store_cycle_count -
+ * @counter: Cycle counter object
+ * @id: Cycle counter bucket id
+ *
+ * Stores the cycle counter for a bucket in FG/QG.
+ *
+ */
+static int store_cycle_count(struct cycle_counter *counter, int id)
+{
+	int rc = 0;
+	u16 cyc_count;
+
+	if (!counter)
+		return -ENODEV;
+
+	if (id < 0 || (id > BUCKET_COUNT - 1)) {
+		pr_err("Invalid id %d\n", id);
+		return -EINVAL;
+	}
+
+	cyc_count = counter->count[id];
+	cyc_count++;
+
+	rc = counter->store_count(counter->data, &cyc_count, id, 2);
+	if (rc < 0) {
+		pr_err("failed to write cycle_count[%d] rc=%d\n",
+			id, rc);
+		return rc;
+	}
+
+	counter->count[id] = cyc_count;
+	pr_debug("Stored count %d in id %d\n", cyc_count, id);
+
+	return rc;
+}
+
+/**
+ * cycle_count_update -
+ * @counter: Cycle counter object
+ * @batt_soc: Battery State of Charge (SOC)
+ * @charge_status: Charging status from power supply
+ * @charge_done: Indicator for charge termination
+ * @input_present: Indicator for input presence
+ *
+ * Called by FG/QG whenever there is a state change (Charging status, SOC)
+ *
+ */
+void cycle_count_update(struct cycle_counter *counter, int batt_soc,
+			int charge_status, bool charge_done, bool input_present)
+{
+	int rc = 0, id, i, soc_thresh;
+
+	if (!counter)
+		return;
+
+	mutex_lock(&counter->lock);
+
+	/* Find out which id the SOC falls in */
+	id = batt_soc / BUCKET_SOC_PCT;
+
+	if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+		if (!counter->started[id] && id != counter->last_bucket) {
+			counter->started[id] = true;
+			counter->last_soc[id] = batt_soc;
+		}
+	} else if (charge_done || !input_present) {
+		for (i = 0; i < BUCKET_COUNT; i++) {
+			soc_thresh = counter->last_soc[i] + BUCKET_SOC_PCT / 2;
+			if (counter->started[i] && batt_soc > soc_thresh) {
+				rc = store_cycle_count(counter, i);
+				if (rc < 0)
+					pr_err("Error in storing cycle_ctr rc: %d\n",
+						rc);
+				counter->last_soc[i] = 0;
+				counter->started[i] = false;
+				counter->last_bucket = i;
+			}
+		}
+	}
+
+	pr_debug("batt_soc: %d id: %d chg_status: %d\n", batt_soc, id,
+		charge_status);
+	mutex_unlock(&counter->lock);
+}
+
+/**
+ * get_cycle_count -
+ * @counter: Cycle counter object
+ *
+ * Returns the cycle counter for a SOC bucket.
+ *
+ */
+int get_cycle_count(struct cycle_counter *counter)
+{
+	int count;
+
+	if (!counter)
+		return 0;
+
+	if ((counter->id <= 0) || (counter->id > BUCKET_COUNT))
+		return -EINVAL;
+
+	mutex_lock(&counter->lock);
+	count = counter->count[counter->id - 1];
+	mutex_unlock(&counter->lock);
+	return count;
+}
+
+/**
+ * cycle_count_init -
+ * @counter: Cycle counter object
+ *
+ * FG/QG have to call this during driver probe to validate the required
+ * parameters after allocating cycle_counter object.
+ *
+ */
+int cycle_count_init(struct cycle_counter *counter)
+{
+	if (!counter)
+		return -ENODEV;
+
+	if (!counter->data || !counter->restore_count ||
+		!counter->store_count) {
+		pr_err("Invalid parameters for using cycle counter\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&counter->lock);
+	counter->last_bucket = -1;
+	return 0;
+}
+
+/* Capacity learning algorithm APIs */
+
+/**
+ * cap_learning_post_process -
+ * @cl: Capacity learning object
+ *
+ * Does post processing on the learnt capacity based on the user specified
+ * or default parameters for the capacity learning algorithm.
+ *
+ */
+static void cap_learning_post_process(struct cap_learning *cl)
+{
+	int64_t max_inc_val, min_dec_val, old_cap;
+	int rc;
+
+	if (cl->dt.skew_decipct) {
+		pr_debug("applying skew %d on current learnt capacity %lld\n",
+			cl->dt.skew_decipct, cl->final_cap_uah);
+		cl->final_cap_uah = cl->final_cap_uah *
+					(1000 + cl->dt.skew_decipct);
+		cl->final_cap_uah = div64_u64(cl->final_cap_uah, 1000);
+	}
+
+	max_inc_val = cl->learned_cap_uah * (1000 + cl->dt.max_cap_inc);
+	max_inc_val = div64_u64(max_inc_val, 1000);
+
+	min_dec_val = cl->learned_cap_uah * (1000 - cl->dt.max_cap_dec);
+	min_dec_val = div64_u64(min_dec_val, 1000);
+
+	old_cap = cl->learned_cap_uah;
+	if (cl->final_cap_uah > max_inc_val)
+		cl->learned_cap_uah = max_inc_val;
+	else if (cl->final_cap_uah < min_dec_val)
+		cl->learned_cap_uah = min_dec_val;
+	else
+		cl->learned_cap_uah = cl->final_cap_uah;
+
+	if (cl->dt.max_cap_limit) {
+		max_inc_val = (int64_t)cl->nom_cap_uah * (1000 +
+				cl->dt.max_cap_limit);
+		max_inc_val = div64_u64(max_inc_val, 1000);
+		if (cl->final_cap_uah > max_inc_val) {
+			pr_debug("learning capacity %lld goes above max limit %lld\n",
+				cl->final_cap_uah, max_inc_val);
+			cl->learned_cap_uah = max_inc_val;
+		}
+	}
+
+	if (cl->dt.min_cap_limit) {
+		min_dec_val = (int64_t)cl->nom_cap_uah * (1000 -
+				cl->dt.min_cap_limit);
+		min_dec_val = div64_u64(min_dec_val, 1000);
+		if (cl->final_cap_uah < min_dec_val) {
+			pr_debug("learning capacity %lld goes below min limit %lld\n",
+				cl->final_cap_uah, min_dec_val);
+			cl->learned_cap_uah = min_dec_val;
+		}
+	}
+
+	if (cl->store_learned_capacity) {
+		rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah);
+		if (rc < 0)
+			pr_err("Error in storing learned_cap_uah, rc=%d\n", rc);
+	}
+
+	pr_debug("final cap_uah = %lld, learned capacity %lld -> %lld uah\n",
+		cl->final_cap_uah, old_cap, cl->learned_cap_uah);
+}
+
+/**
+ * cap_learning_process_full_data -
+ * @cl: Capacity learning object
+ *
+ * Processes the coulomb counter during charge termination and calculates the
+ * delta w.r.to the coulomb counter obtained earlier when the learning begun.
+ *
+ */
+static int cap_learning_process_full_data(struct cap_learning *cl)
+{
+	int rc, cc_soc_sw, cc_soc_delta_pct;
+	int64_t delta_cap_uah;
+
+	rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
+	if (rc < 0) {
+		pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
+		return rc;
+	}
+
+	cc_soc_delta_pct =
+		div64_s64((int64_t)(cc_soc_sw - cl->init_cc_soc_sw) * 100,
+			cl->cc_soc_max);
+
+	/* If the delta is < 50%, then skip processing full data */
+	if (cc_soc_delta_pct < 50) {
+		pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct);
+		return -ERANGE;
+	}
+
+	delta_cap_uah = div64_s64(cl->learned_cap_uah * cc_soc_delta_pct, 100);
+	cl->final_cap_uah = cl->init_cap_uah + delta_cap_uah;
+	pr_debug("Current cc_soc=%d cc_soc_delta_pct=%d total_cap_uah=%lld\n",
+		cc_soc_sw, cc_soc_delta_pct, cl->final_cap_uah);
+	return 0;
+}
+
+/**
+ * cap_learning_begin -
+ * @cl: Capacity learning object
+ * @batt_soc: Battery State of Charge (SOC)
+ *
+ * Gets the coulomb counter from FG/QG when the conditions are suitable for
+ * beginning capacity learning. Also, primes the coulomb counter based on
+ * battery SOC if required.
+ *
+ */
+static int cap_learning_begin(struct cap_learning *cl, u32 batt_soc)
+{
+	int rc, cc_soc_sw, batt_soc_msb;
+
+	batt_soc_msb = batt_soc >> 24;
+	if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
+		cl->dt.start_soc) {
+		pr_debug("Battery SOC %d is high!, not starting\n",
+			batt_soc_msb);
+		return -EINVAL;
+	}
+
+	cl->init_cap_uah = div64_s64(cl->learned_cap_uah * batt_soc_msb,
+					FULL_SOC_RAW);
+
+	if (cl->prime_cc_soc) {
+		/*
+		 * Prime cc_soc_sw with battery SOC when capacity learning
+		 * begins.
+		 */
+		rc = cl->prime_cc_soc(cl->data, batt_soc);
+		if (rc < 0) {
+			pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+			goto out;
+		}
+	}
+
+	rc = cl->get_cc_soc(cl->data, &cc_soc_sw);
+	if (rc < 0) {
+		pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
+		goto out;
+	}
+
+	cl->init_cc_soc_sw = cc_soc_sw;
+	pr_debug("Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
+		batt_soc_msb, cl->init_cc_soc_sw);
+out:
+	return rc;
+}
+
+/**
+ * cap_learning_done -
+ * @cl: Capacity learning object
+ *
+ * Top level function for getting coulomb counter and post processing the
+ * data once the capacity learning is complete after charge termination.
+ *
+ */
+static int cap_learning_done(struct cap_learning *cl)
+{
+	int rc;
+
+	rc = cap_learning_process_full_data(cl);
+	if (rc < 0) {
+		pr_err("Error in processing cap learning full data, rc=%d\n",
+			rc);
+		goto out;
+	}
+
+	if (cl->prime_cc_soc) {
+		/* Write a FULL value to cc_soc_sw */
+		rc = cl->prime_cc_soc(cl->data, cl->cc_soc_max);
+		if (rc < 0) {
+			pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+			goto out;
+		}
+	}
+
+	cap_learning_post_process(cl);
+out:
+	return rc;
+}
+
+/**
+ * cap_learning_update -
+ * @cl: Capacity learning object
+ * @batt_temp - Battery temperature
+ * @batt_soc: Battery State of Charge (SOC)
+ * @charge_status: Charging status from power supply
+ * @charge_done: Indicator for charge termination
+ * @input_present: Indicator for input presence
+ * @qnovo_en: Indicator for Qnovo enable status
+ *
+ * Called by FG/QG driver when there is a state change (Charging status, SOC)
+ *
+ */
+void cap_learning_update(struct cap_learning *cl, int batt_temp,
+			int batt_soc, int charge_status, bool charge_done,
+			bool input_present, bool qnovo_en)
+{
+	int rc, batt_soc_msb, batt_soc_prime;
+	bool prime_cc = false;
+
+	if (!cl)
+		return;
+
+	mutex_lock(&cl->lock);
+
+	if (batt_temp > cl->dt.max_temp || batt_temp < cl->dt.min_temp ||
+		!cl->learned_cap_uah) {
+		cl->active = false;
+		cl->init_cap_uah = 0;
+		goto out;
+	}
+
+	batt_soc_msb = (u32)batt_soc >> 24;
+	pr_debug("Charge_status: %d active: %d batt_soc: %d\n",
+		charge_status, cl->active, batt_soc_msb);
+
+	/* Initialize the starting point of learning capacity */
+	if (!cl->active) {
+		if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
+			rc = cap_learning_begin(cl, batt_soc);
+			cl->active = (rc == 0);
+		} else {
+			if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING ||
+				charge_done)
+				prime_cc = true;
+		}
+	} else {
+		if (charge_done) {
+			rc = cap_learning_done(cl);
+			if (rc < 0)
+				pr_err("Error in completing capacity learning, rc=%d\n",
+					rc);
+
+			cl->active = false;
+			cl->init_cap_uah = 0;
+		}
+
+		if (charge_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+			if (!input_present) {
+				pr_debug("Capacity learning aborted @ battery SOC %d\n",
+					 batt_soc_msb);
+				cl->active = false;
+				cl->init_cap_uah = 0;
+				prime_cc = true;
+			}
+		}
+
+		if (charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
+			if (qnovo_en && input_present) {
+				/*
+				 * Don't abort the capacity learning when qnovo
+				 * is enabled and input is present where the
+				 * charging status can go to "not charging"
+				 * intermittently.
+				 */
+			} else {
+				pr_debug("Capacity learning aborted @ battery SOC %d\n",
+					batt_soc_msb);
+				cl->active = false;
+				cl->init_cap_uah = 0;
+				prime_cc = true;
+			}
+		}
+	}
+
+	/*
+	 * Prime CC_SOC_SW when the device is not charging or during charge
+	 * termination when the capacity learning is not active.
+	 */
+
+	if (prime_cc && cl->prime_cc_soc) {
+		if (charge_done)
+			batt_soc_prime = cl->cc_soc_max;
+		else
+			batt_soc_prime = batt_soc;
+
+		rc = cl->prime_cc_soc(cl->data, batt_soc_prime);
+		if (rc < 0)
+			pr_err("Error in writing cc_soc_sw, rc=%d\n",
+				rc);
+	}
+
+out:
+	mutex_unlock(&cl->lock);
+}
+
+/**
+ * cap_learning_abort -
+ * @cl: Capacity learning object
+ *
+ * Aborts the capacity learning and initializes variables
+ *
+ */
+void cap_learning_abort(struct cap_learning *cl)
+{
+	if (!cl)
+		return;
+
+	mutex_lock(&cl->lock);
+	pr_debug("Aborting cap_learning\n");
+	cl->active = false;
+	cl->init_cap_uah = 0;
+	mutex_lock(&cl->lock);
+}
+
+/**
+ * cap_learning_post_profile_init -
+ * @cl: Capacity learning object
+ * @nom_cap_uah: Nominal capacity of battery in uAh
+ *
+ * Called by FG/QG once the profile load is complete and nominal capacity
+ * of battery is known. This also gets the last learned capacity back from
+ * FG/QG to feed back to the algorithm.
+ *
+ */
+int cap_learning_post_profile_init(struct cap_learning *cl, int64_t nom_cap_uah)
+{
+	int64_t delta_cap_uah, pct_nom_cap_uah;
+	int rc;
+
+	if (!cl || !cl->data)
+		return -EINVAL;
+
+	mutex_lock(&cl->lock);
+	cl->nom_cap_uah = nom_cap_uah;
+	rc = cl->get_learned_capacity(cl->data, &cl->learned_cap_uah);
+	if (rc < 0) {
+		pr_err("Couldn't get learned capacity, rc=%d\n", rc);
+		goto out;
+	}
+
+	if (cl->learned_cap_uah != cl->nom_cap_uah) {
+		if (cl->learned_cap_uah == 0)
+			cl->learned_cap_uah = cl->nom_cap_uah;
+
+		delta_cap_uah = abs(cl->learned_cap_uah - cl->nom_cap_uah);
+		pct_nom_cap_uah = div64_s64((int64_t)cl->nom_cap_uah *
+				CAPACITY_DELTA_DECIPCT, 1000);
+		/*
+		 * If the learned capacity is out of range by 50% from the
+		 * nominal capacity, then overwrite the learned capacity with
+		 * the nominal capacity.
+		 */
+		if (cl->nom_cap_uah && delta_cap_uah > pct_nom_cap_uah) {
+			pr_debug("learned_cap_uah: %lld is higher than expected, capping it to nominal: %lld\n",
+				cl->learned_cap_uah, cl->nom_cap_uah);
+			cl->learned_cap_uah = cl->nom_cap_uah;
+		}
+
+		rc = cl->store_learned_capacity(cl->data, cl->learned_cap_uah);
+		if (rc < 0)
+			pr_err("Error in storing learned_cap_uah, rc=%d\n", rc);
+	}
+
+out:
+	mutex_unlock(&cl->lock);
+	return rc;
+}
+
+/**
+ * cap_learning_init -
+ * @cl: Capacity learning object
+ *
+ * FG/QG have to call this during driver probe to validate the required
+ * parameters after allocating cap_learning object.
+ *
+ */
+int cap_learning_init(struct cap_learning *cl)
+{
+	if (!cl)
+		return -ENODEV;
+
+	if (!cl->get_learned_capacity || !cl->store_learned_capacity ||
+		!cl->get_cc_soc) {
+		pr_err("Insufficient functions for supporting capacity learning\n");
+		return -EINVAL;
+	}
+
+	if (!cl->cc_soc_max) {
+		pr_err("Insufficient parameters for supporting capacity learning\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&cl->lock);
+	return 0;
+}
diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h
new file mode 100644
index 0000000..ff5bece
--- /dev/null
+++ b/drivers/power/supply/qcom/fg-alg.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __FG_ALG_H__
+#define __FG_ALG_H__
+
+#define BUCKET_COUNT		8
+#define BUCKET_SOC_PCT		(256 / BUCKET_COUNT)
+
+struct cycle_counter {
+	void		*data;
+	bool		started[BUCKET_COUNT];
+	u16		count[BUCKET_COUNT];
+	u8		last_soc[BUCKET_COUNT];
+	int		id;
+	int		last_bucket;
+	struct mutex	lock;
+	int (*restore_count)(void *data, u16 *buf, int num_bytes);
+	int (*store_count)(void *data, u16 *buf, int id, int num_bytes);
+};
+
+struct cl_params {
+	int	start_soc;
+	int	max_temp;
+	int	min_temp;
+	int	max_cap_inc;
+	int	max_cap_dec;
+	int	max_cap_limit;
+	int	min_cap_limit;
+	int	skew_decipct;
+};
+
+struct cap_learning {
+	void			*data;
+	int			init_cc_soc_sw;
+	int			cc_soc_max;
+	int64_t			nom_cap_uah;
+	int64_t			init_cap_uah;
+	int64_t			final_cap_uah;
+	int64_t			learned_cap_uah;
+	bool			active;
+	struct mutex		lock;
+	struct cl_params	dt;
+	int (*get_learned_capacity)(void *data, int64_t *learned_cap_uah);
+	int (*store_learned_capacity)(void *data, int64_t learned_cap_uah);
+	int (*get_cc_soc)(void *data, int *cc_soc_sw);
+	int (*prime_cc_soc)(void *data, u32 cc_soc_sw);
+};
+
+int restore_cycle_count(struct cycle_counter *counter);
+void clear_cycle_count(struct cycle_counter *counter);
+void cycle_count_update(struct cycle_counter *counter, int batt_soc,
+		int charge_status, bool charge_done, bool input_present);
+int get_cycle_count(struct cycle_counter *counter);
+int cycle_count_init(struct cycle_counter *counter);
+void cap_learning_abort(struct cap_learning *cl);
+void cap_learning_update(struct cap_learning *cl, int batt_temp,
+		int batt_soc, int charge_status, bool charge_done,
+		bool input_present, bool qnovo_en);
+int cap_learning_init(struct cap_learning *cl);
+int cap_learning_post_profile_init(struct cap_learning *cl,
+		int64_t nom_cap_uah);
+
+#endif
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 4bd6c4f..f73e647 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -412,6 +412,7 @@
 };
 
 struct fg_chip {
+	struct thermal_zone_device	*tz_dev;
 	struct device		*dev;
 	struct pmic_revid_data	*pmic_rev_id;
 	struct regmap		*regmap;
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 55dba4e..6e44f33 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -29,7 +29,9 @@
 
 struct qg_dt {
 	int			vbatt_empty_mv;
+	int			vbatt_empty_cold_mv;
 	int			vbatt_low_mv;
+	int			vbatt_low_cold_mv;
 	int			vbatt_cutoff_mv;
 	int			iterm_ma;
 	int			s2_fifo_length;
@@ -44,6 +46,7 @@
 	int			delta_soc;
 	int			rbat_conn_mohm;
 	int			ignore_shutdown_soc_secs;
+	int			cold_temp_threshold;
 	bool			hold_soc_while_full;
 	bool			linearize_soc;
 };
@@ -147,6 +150,7 @@
 
 enum qg_wa_flags {
 	QG_VBAT_LOW_WA = BIT(0),
+	QG_RECHARGE_SOC_WA = BIT(1),
 };
 
 
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index 660f6f1..c4d5043 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -24,11 +24,15 @@
 
 #define DEFAULT_UPDATE_TIME_MS			64000
 #define SOC_SCALE_HYST_MS			2000
-#define SOC_SCALE_LOW_TEMP_THRESHOLD		100
 
 static int qg_delta_soc_interval_ms = 20000;
 module_param_named(
-	delta_soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+	soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+);
+
+static int qg_delta_soc_cold_interval_ms = 4000;
+module_param_named(
+	soc_cold_interval_ms, qg_delta_soc_cold_interval_ms, int, 0600
 );
 
 static void get_next_update_time(struct qpnp_qg *chip)
@@ -52,9 +56,11 @@
 	rc = qg_get_battery_temp(chip, &batt_temp);
 	if (rc < 0)
 		pr_err("Failed to read battery temperature rc=%d\n", rc);
+	else if (batt_temp < chip->dt.cold_temp_threshold)
+		min_delta_soc_interval_ms = qg_delta_soc_cold_interval_ms;
 
-	if (batt_temp < SOC_SCALE_LOW_TEMP_THRESHOLD)
-		min_delta_soc_interval_ms = min_delta_soc_interval_ms / 2;
+	if (!min_delta_soc_interval_ms)
+		min_delta_soc_interval_ms = 1000;	/* 1 second */
 
 	chip->next_wakeup_ms = (full_time_ms / (soc_points + 1))
 					- SOC_SCALE_HYST_MS;
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 5a9b8f6..440bf74 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -18,6 +18,7 @@
 #include <linux/of_platform.h>
 #include <linux/of_batterydata.h>
 #include <linux/platform_device.h>
+#include <linux/thermal.h>
 #include <linux/iio/consumer.h>
 #include <linux/qpnp/qpnp-revid.h>
 #include "fg-core.h"
@@ -5355,6 +5356,29 @@
 	dev_set_drvdata(chip->dev, NULL);
 }
 
+static int fg_tz_get_temp(void *data, int *temperature)
+{
+	struct fg_chip *chip = (struct fg_chip *)data;
+	int rc, batt_temp = 0;
+
+	if (!temperature)
+		return -EINVAL;
+
+	rc = fg_get_battery_temp(chip, &batt_temp);
+	if (rc < 0) {
+		pr_err("Error in getting batt_temp\n");
+		return rc;
+	}
+
+	/* Convert deciDegC to milliDegC */
+	*temperature = batt_temp * 100;
+	return 0;
+}
+
+static struct thermal_zone_of_device_ops fg_gen3_tz_ops = {
+	.get_temp = fg_tz_get_temp,
+};
+
 static int fg_gen3_probe(struct platform_device *pdev)
 {
 	struct fg_chip *chip;
@@ -5537,6 +5561,15 @@
 			pr_err("Error in configuring ESR filter rc:%d\n", rc);
 	}
 
+	chip->tz_dev = thermal_zone_of_sensor_register(chip->dev, 0, chip,
+							&fg_gen3_tz_ops);
+	if (IS_ERR_OR_NULL(chip->tz_dev)) {
+		rc = PTR_ERR(chip->tz_dev);
+		chip->tz_dev = NULL;
+		dev_err(chip->dev, "thermal_zone_of_sensor_register() failed rc:%d\n",
+			rc);
+	}
+
 	device_init_wakeup(chip->dev, true);
 	schedule_delayed_work(&chip->profile_load_work, 0);
 
@@ -5601,6 +5634,8 @@
 {
 	struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
 
+	if (chip->tz_dev)
+		thermal_zone_of_sensor_unregister(chip->dev, chip->tz_dev);
 	fg_cleanup(chip);
 	return 0;
 }
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index effb093..f7a6b7a 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -114,9 +114,11 @@
 	return rc;
 }
 
+#define DEFAULT_S3_FIFO_LENGTH		3
 static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length)
 {
 	int rc;
+	u8 s3_entry_fifo_length = 0;
 
 	if (!length || length > 8) {
 		pr_err("Invalid FIFO length %d\n", length);
@@ -128,6 +130,21 @@
 	if (rc < 0)
 		pr_err("Failed to write S2 FIFO length, rc=%d\n", rc);
 
+	/* update the S3 FIFO length, when S2 length is updated */
+	if (length > 3)
+		s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ?
+			chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH;
+	else	/* Use S3 length as 1 for any S2 length <= 3 */
+		s3_entry_fifo_length = 1;
+
+	rc = qg_masked_write(chip,
+			chip->qg_base + QG_S3_SLEEP_OCV_IBAT_CTL1_REG,
+			SLEEP_IBAT_QUALIFIED_LENGTH_MASK,
+			s3_entry_fifo_length - 1);
+	if (rc < 0)
+		pr_err("Failed to write S3-entry fifo-length, rc=%d\n",
+						rc);
+
 	return rc;
 }
 
@@ -412,8 +429,19 @@
 #define VBAT_LOW_HYST_UV		50000 /* 50mV */
 static int qg_vbat_low_wa(struct qpnp_qg *chip)
 {
-	int rc, i;
-	u32 vbat_low_uv = chip->dt.vbatt_low_mv * 1000 + VBAT_LOW_HYST_UV;
+	int rc, i, temp = 0;
+	u32 vbat_low_uv = 0;
+
+	rc = qg_get_battery_temp(chip, &temp);
+	if (rc < 0) {
+		pr_err("Failed to read batt_temp rc=%d\n", rc);
+		temp = 250;
+	}
+
+	vbat_low_uv = 1000 * ((temp < chip->dt.cold_temp_threshold) ?
+				chip->dt.vbatt_low_cold_mv :
+				chip->dt.vbatt_low_mv);
+	vbat_low_uv += VBAT_LOW_HYST_UV;
 
 	if (!(chip->wa_flags & QG_VBAT_LOW_WA) || !chip->vbat_low)
 		return 0;
@@ -459,6 +487,73 @@
 	return rc;
 }
 
+static int qg_vbat_thresholds_config(struct qpnp_qg *chip)
+{
+	int rc, temp = 0, vbat_mv;
+	u8 reg;
+
+	rc = qg_get_battery_temp(chip, &temp);
+	if (rc < 0) {
+		pr_err("Failed to read batt_temp rc=%d\n", rc);
+		return rc;
+	}
+
+	vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+			chip->dt.vbatt_empty_cold_mv :
+			chip->dt.vbatt_empty_mv;
+
+	rc = qg_read(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to read vbat-empty, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (vbat_mv == (reg * 50))	/* No change */
+		goto config_vbat_low;
+
+	reg = vbat_mv / 50;
+	rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+		return rc;
+	}
+
+	qg_dbg(chip, QG_DEBUG_STATUS,
+		"VBAT EMPTY threshold updated to %dmV temp=%d\n",
+						vbat_mv, temp);
+
+config_vbat_low:
+	vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+			chip->dt.vbatt_low_cold_mv :
+			chip->dt.vbatt_low_mv;
+
+	rc = qg_read(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to read vbat-low, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (vbat_mv == (reg * 50))	/* No change */
+		return 0;
+
+	reg = vbat_mv / 50;
+	rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to write vbat-low, rc=%d\n", rc);
+		return rc;
+	}
+
+	qg_dbg(chip, QG_DEBUG_STATUS,
+		"VBAT LOW threshold updated to %dmV temp=%d\n",
+						vbat_mv, temp);
+
+	return rc;
+}
+
 #define MIN_FIFO_FULL_TIME_MS			12000
 static int process_rt_fifo_data(struct qpnp_qg *chip,
 				bool vbat_low, bool update_smb)
@@ -609,6 +704,10 @@
 		goto done;
 	}
 
+	rc = qg_vbat_thresholds_config(chip);
+	if (rc < 0)
+		pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc);
+
 	rc = qg_vbat_low_wa(chip);
 	if (rc < 0) {
 		pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc);
@@ -1081,12 +1180,26 @@
 			chip->charge_full = true;
 			qg_dbg(chip, QG_DEBUG_STATUS, "Setting charge_full (0->1) @ msoc=%d\n",
 					chip->msoc);
-		} else {
+		} else if (health != POWER_SUPPLY_HEALTH_GOOD) {
+			/* terminated in JEITA */
 			qg_dbg(chip, QG_DEBUG_STATUS, "Terminated charging @ msoc=%d\n",
 					chip->msoc);
 		}
 	} else if ((!chip->charge_done || chip->msoc < recharge_soc)
 				&& chip->charge_full) {
+
+		if (chip->wa_flags & QG_RECHARGE_SOC_WA) {
+			/* Force recharge */
+			prop.intval = 0;
+			rc = power_supply_set_property(chip->batt_psy,
+				POWER_SUPPLY_PROP_RECHARGE_SOC, &prop);
+			if (rc < 0)
+				pr_err("Failed to force recharge rc=%d\n", rc);
+			else
+				qg_dbg(chip, QG_DEBUG_STATUS,
+					"Forced recharge\n");
+		}
+
 		/*
 		 * If recharge or discharge has started and
 		 * if linearize soc dtsi property defined
@@ -1668,6 +1781,7 @@
 {
 	switch (chip->pmic_rev_id->pmic_subtype) {
 	case PMI632_SUBTYPE:
+		chip->wa_flags |= QG_RECHARGE_SOC_WA;
 		if (chip->pmic_rev_id->rev4 == PMI632_V1P0_REV4)
 			chip->wa_flags |= QG_VBAT_LOW_WA;
 		break;
@@ -1840,31 +1954,25 @@
 		}
 	}
 
-	/* vbat low */
+	/* vbat based configs */
 	if (chip->dt.vbatt_low_mv < 0)
 		chip->dt.vbatt_low_mv = 0;
 	else if (chip->dt.vbatt_low_mv > 12750)
 		chip->dt.vbatt_low_mv = 12750;
 
-	reg = chip->dt.vbatt_low_mv / 50;
-	rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
-					&reg, 1);
-	if (rc < 0) {
-		pr_err("Failed to write vbat-low, rc=%d\n", rc);
-		return rc;
-	}
-
-	/* vbat empty */
 	if (chip->dt.vbatt_empty_mv < 0)
 		chip->dt.vbatt_empty_mv = 0;
 	else if (chip->dt.vbatt_empty_mv > 12750)
 		chip->dt.vbatt_empty_mv = 12750;
 
-	reg = chip->dt.vbatt_empty_mv / 50;
-	rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
-					&reg, 1);
+	if (chip->dt.vbatt_empty_cold_mv < 0)
+		chip->dt.vbatt_empty_cold_mv = 0;
+	else if (chip->dt.vbatt_empty_cold_mv > 12750)
+		chip->dt.vbatt_empty_cold_mv = 12750;
+
+	rc = qg_vbat_thresholds_config(chip);
 	if (rc < 0) {
-		pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+		pr_err("Failed to configure VBAT empty/low rc=%d\n", rc);
 		return rc;
 	}
 
@@ -1962,8 +2070,10 @@
 }
 
 #define DEFAULT_VBATT_EMPTY_MV		3200
+#define DEFAULT_VBATT_EMPTY_COLD_MV	3000
 #define DEFAULT_VBATT_CUTOFF_MV		3400
 #define DEFAULT_VBATT_LOW_MV		3500
+#define DEFAULT_VBATT_LOW_COLD_MV	3800
 #define DEFAULT_ITERM_MA		100
 #define DEFAULT_S2_FIFO_LENGTH		5
 #define DEFAULT_S2_VBAT_LOW_LENGTH	2
@@ -1971,6 +2081,7 @@
 #define DEFAULT_S2_ACC_INTVL_MS		100
 #define DEFAULT_DELTA_SOC		1
 #define DEFAULT_SHUTDOWN_SOC_SECS	360
+#define DEFAULT_COLD_TEMP_THRESHOLD	0
 static int qg_parse_dt(struct qpnp_qg *chip)
 {
 	int rc = 0;
@@ -2103,12 +2214,30 @@
 	else
 		chip->dt.vbatt_empty_mv = temp;
 
+	rc = of_property_read_u32(node, "qcom,vbatt-empty-cold-mv", &temp);
+	if (rc < 0)
+		chip->dt.vbatt_empty_cold_mv = DEFAULT_VBATT_EMPTY_COLD_MV;
+	else
+		chip->dt.vbatt_empty_cold_mv = temp;
+
+	rc = of_property_read_u32(node, "qcom,cold-temp-threshold", &temp);
+	if (rc < 0)
+		chip->dt.cold_temp_threshold = DEFAULT_COLD_TEMP_THRESHOLD;
+	else
+		chip->dt.cold_temp_threshold = temp;
+
 	rc = of_property_read_u32(node, "qcom,vbatt-low-mv", &temp);
 	if (rc < 0)
 		chip->dt.vbatt_low_mv = DEFAULT_VBATT_LOW_MV;
 	else
 		chip->dt.vbatt_low_mv = temp;
 
+	rc = of_property_read_u32(node, "qcom,vbatt-low-cold-mv", &temp);
+	if (rc < 0)
+		chip->dt.vbatt_low_cold_mv = DEFAULT_VBATT_LOW_COLD_MV;
+	else
+		chip->dt.vbatt_low_cold_mv = temp;
+
 	rc = of_property_read_u32(node, "qcom,vbatt-cutoff-mv", &temp);
 	if (rc < 0)
 		chip->dt.vbatt_cutoff_mv = DEFAULT_VBATT_CUTOFF_MV;
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 20dd78e..7a3a4dc 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -949,6 +949,7 @@
  *************************/
 static enum power_supply_property smb5_batt_props[] = {
 	POWER_SUPPLY_PROP_INPUT_SUSPEND,
+	POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_HEALTH,
 	POWER_SUPPLY_PROP_PRESENT,
@@ -995,6 +996,10 @@
 	case POWER_SUPPLY_PROP_INPUT_SUSPEND:
 		rc = smblib_get_prop_input_suspend(chg, val);
 		break;
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+		val->intval = !get_client_vote(chg->chg_disable_votable,
+					      USER_VOTER);
+		break;
 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
 		rc = smblib_get_prop_batt_charge_type(chg, val);
 		break;
@@ -1093,6 +1098,9 @@
 	case POWER_SUPPLY_PROP_INPUT_SUSPEND:
 		rc = smblib_set_prop_input_suspend(chg, val);
 		break;
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+		vote(chg->chg_disable_votable, USER_VOTER, !val->intval, 0);
+		break;
 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 		rc = smblib_set_prop_system_temp_level(chg, val);
 		break;
@@ -1147,6 +1155,17 @@
 		chg->die_health = val->intval;
 		power_supply_changed(chg->batt_psy);
 		break;
+	case POWER_SUPPLY_PROP_RECHARGE_SOC:
+		if (chg->smb_version == PMI632_SUBTYPE) {
+			/* toggle charging to force recharge */
+			vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+					true, 0);
+			/* charge disable delay */
+			msleep(50);
+			vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+					false, 0);
+		}
+		break;
 	default:
 		rc = -EINVAL;
 	}
@@ -1169,6 +1188,7 @@
 	case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
 	case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
 	case POWER_SUPPLY_PROP_DIE_HEALTH:
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
 		return 1;
 	default:
 		break;
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
index 403b670..59478a9 100644
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -352,6 +352,7 @@
 #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"
+#define SHUTDOWN_WORKAROUND_ICL_VOTER "SHUTDOWN_WORKAROUND_ICL_VOTER"
 
 /* USB SUSPEND VOTERS */
 /* userspace has suspended charging altogether */
@@ -1061,6 +1062,33 @@
 	return ua;
 }
 
+#define DEFAULT_BATT_RESISTANCE_ID	0
+static int get_prop_batt_resistance_id(struct smbchg_chip *chip)
+{
+	int rbatt, rc;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE_ID,
+			&rbatt);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get resistance id rc = %d\n", rc);
+		rbatt = DEFAULT_BATT_RESISTANCE_ID;
+	}
+	return rbatt;
+}
+
+#define DEFAULT_BATT_FULL_CHG_CAPACITY	0
+static int get_prop_batt_full_charge(struct smbchg_chip *chip)
+{
+	int bfc, rc;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CHARGE_FULL, &bfc);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get charge_full rc = %d\n", rc);
+		bfc = DEFAULT_BATT_FULL_CHG_CAPACITY;
+	}
+	return bfc;
+}
+
 #define DEFAULT_BATT_VOLTAGE_NOW	0
 static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
 {
@@ -1876,6 +1904,7 @@
 		return 0;
 	}
 
+	fg_current_now = abs(fg_current_now) / 1000;
 	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);
@@ -4013,11 +4042,11 @@
 		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
+		else if (blinking == 2)
 			reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
+		else
+			reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT;
 	}
 
 	rc = smbchg_sec_masked_write(chip,
@@ -4410,6 +4439,20 @@
 	if (type == POWER_SUPPLY_TYPE_UNKNOWN)
 		chip->usb_supply_type = type;
 
+	/*
+	 * Update TYPE property to DCP for HVDCP/HVDCP3 charger types
+	 * so that they can be recongized as AC chargers by healthd.
+	 * Don't report UNKNOWN charger type to prevent healthd missing
+	 * detecting this power_supply status change.
+	 */
+	if (chip->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP_3
+			|| chip->usb_supply_type == POWER_SUPPLY_TYPE_USB_HVDCP)
+		chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB_DCP;
+	else if (chip->usb_supply_type == POWER_SUPPLY_TYPE_UNKNOWN)
+		chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+	else
+		chip->usb_psy_d.type = chip->usb_supply_type;
+
 	if (!chip->skip_usb_notification)
 		power_supply_changed(chip->usb_psy);
 
@@ -4602,6 +4645,8 @@
 	/* Clear typec current status */
 	if (chip->typec_psy)
 		chip->typec_current_ma = 0;
+	/* cancel/wait for hvdcp pending work if any */
+	cancel_delayed_work_sync(&chip->hvdcp_det_work);
 	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)
@@ -5226,6 +5271,15 @@
 	 */
 	chip->parallel.enabled_once = 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, AICL_EN_BIT);
+	if (rc < 0) {
+		pr_err("Couldn't enable AICL rc=%d\n", rc);
+		goto out;
+	}
+
 	/* fake an insertion */
 	pr_smb(PR_MISC, "Faking Insertion\n");
 	rc = fake_insertion_removal(chip, true);
@@ -5235,15 +5289,6 @@
 	}
 	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
@@ -5276,49 +5321,63 @@
 #define APSD_RERUN		BIT(0)
 static int rerun_apsd(struct smbchg_chip *chip)
 {
-	int rc;
+	int rc = 0;
 
-	reinit_completion(&chip->src_det_raised);
-	reinit_completion(&chip->usbin_uv_lowered);
-	reinit_completion(&chip->src_det_lowered);
-	reinit_completion(&chip->usbin_uv_raised);
+	chip->hvdcp_3_det_ignore_uv = true;
 
-	/* 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;
+	if (chip->schg_version == QPNP_SCHG_LITE) {
+		pr_smb(PR_STATUS, "Re-running APSD\n");
+		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);
+			goto out;
+		}
+
+		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);
+			goto out;
+		}
+
+		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);
+			goto out;
+		}
+
+		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);
+			goto out;
+		}
+
+		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);
+			goto out;
+		}
+	} else {
+		pr_smb(PR_STATUS, "Faking Removal\n");
+		rc = fake_insertion_removal(chip, false);
+		msleep(500);
+		pr_smb(PR_STATUS, "Faking Insertion\n");
+		rc = fake_insertion_removal(chip, true);
 	}
 
-	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;
-	}
-
+out:
+	chip->hvdcp_3_det_ignore_uv = false;
 	return rc;
 }
 
@@ -5395,8 +5454,6 @@
 	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) {
@@ -5404,8 +5461,6 @@
 		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);
@@ -5434,6 +5489,10 @@
 out:
 	chip->hvdcp_3_det_ignore_uv = false;
 	restore_from_hvdcp_detection(chip);
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "HVDCP removed - force removal\n");
+		update_usb_status(chip, 0, true);
+	}
 	return rc;
 }
 
@@ -5453,6 +5512,10 @@
 	if (rc < 0)
 		pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
 
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "HVDCP removed\n");
+		update_usb_status(chip, 0, 0);
+	}
 	smbchg_handle_hvdcp3_disable(chip);
 
 	return rc;
@@ -5631,6 +5694,9 @@
 		val->intval = chip->usb_online;
 		break;
 	case POWER_SUPPLY_PROP_TYPE:
+		val->intval = chip->usb_psy_d.type;
+		break;
+	case POWER_SUPPLY_PROP_REAL_TYPE:
 		val->intval = chip->usb_supply_type;
 		break;
 	case POWER_SUPPLY_PROP_HEALTH:
@@ -5688,6 +5754,7 @@
 	POWER_SUPPLY_PROP_ONLINE,
 	POWER_SUPPLY_PROP_CURRENT_MAX,
 	POWER_SUPPLY_PROP_TYPE,
+	POWER_SUPPLY_PROP_REAL_TYPE,
 	POWER_SUPPLY_PROP_HEALTH,
 };
 
@@ -5733,6 +5800,8 @@
 	POWER_SUPPLY_PROP_CURRENT_NOW,
 	POWER_SUPPLY_PROP_TEMP,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_RESISTANCE_ID,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
 	POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
 	POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
 	POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
@@ -5924,6 +5993,12 @@
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 		val->intval = get_prop_batt_voltage_now(chip);
 		break;
+	case POWER_SUPPLY_PROP_RESISTANCE_ID:
+		val->intval = get_prop_batt_resistance_id(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		val->intval = get_prop_batt_full_charge(chip);
+		break;
 	case POWER_SUPPLY_PROP_TEMP:
 		val->intval = get_prop_batt_temp(chip);
 		break;
@@ -6609,7 +6684,6 @@
 	} 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);
@@ -7968,20 +8042,18 @@
 			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);
+		rc = rerun_apsd(chip);
+		if (rc)
+			pr_err("APSD rerun failed rc=%d\n", rc);
 
 		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);
+			pr_smb(PR_STATUS, "Rerun APSD as type !=DCP\n");
+
+			rc = rerun_apsd(chip);
+			if (rc)
+				pr_err("APSD rerun failed rc=%d\n", rc);
 		}
 
 		rc = vote(chip->usb_icl_votable,
@@ -7989,6 +8061,14 @@
 		if (rc < 0)
 			pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
 					rc);
+
+		/* Schedule work for HVDCP detection */
+		if (!chip->hvdcp_not_supported) {
+			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));
+		}
 	}
 }
 
@@ -8385,6 +8465,12 @@
 	if (!is_hvdcp_present(chip))
 		return;
 
+	pr_smb(PR_MISC, "Reducing to 500mA\n");
+	rc = vote(chip->usb_icl_votable, SHUTDOWN_WORKAROUND_ICL_VOTER, true,
+			500);
+	if (rc < 0)
+		pr_err("Couldn't vote 500mA ICL\n");
+
 	pr_smb(PR_MISC, "Disable Parallel\n");
 	mutex_lock(&chip->parallel.lock);
 	smbchg_parallel_en = 0;
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 7ee4a3a..668ce5f 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -67,6 +67,7 @@
 #define HW_LIMIT_VOTER			"HW_LIMIT_VOTER"
 #define DYNAMIC_RP_VOTER		"DYNAMIC_RP_VOTER"
 #define DEFAULT_100MA_VOTER		"DEFAULT_100MA_VOTER"
+#define FORCE_RECHARGE_VOTER		"FORCE_RECHARGE_VOTER"
 
 #define BOOST_BACK_STORM_COUNT	3
 #define WEAK_CHG_STORM_COUNT	8
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 172ef82..17b808c 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -285,6 +285,7 @@
 		pwm->pwm = chip->base + i;
 		pwm->hwpwm = i;
 		pwm->state.polarity = polarity;
+		pwm->state.output_type = PWM_OUTPUT_FIXED;
 
 		if (chip->ops->get_state)
 			chip->ops->get_state(chip, pwm, &pwm->state);
@@ -498,6 +499,31 @@
 			pwm->state.polarity = state->polarity;
 		}
 
+		if (state->output_type != pwm->state.output_type) {
+			if (!pwm->chip->ops->set_output_type)
+				return -ENOTSUPP;
+
+			err = pwm->chip->ops->set_output_type(pwm->chip, pwm,
+						state->output_type);
+			if (err)
+				return err;
+
+			pwm->state.output_type = state->output_type;
+		}
+
+		if (state->output_pattern != pwm->state.output_pattern &&
+				state->output_pattern != NULL) {
+			if (!pwm->chip->ops->set_output_pattern)
+				return -ENOTSUPP;
+
+			err = pwm->chip->ops->set_output_pattern(pwm->chip,
+					pwm, state->output_pattern);
+			if (err)
+				return err;
+
+			pwm->state.output_pattern = state->output_pattern;
+		}
+
 		if (state->period != pwm->state.period ||
 		    state->duty_cycle != pwm->state.duty_cycle) {
 			err = pwm->chip->ops->config(pwm->chip, pwm,
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
index 85a5ea0..31f5204 100644
--- a/drivers/pwm/pwm-qti-lpg.c
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -24,11 +24,16 @@
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
 #include <linux/types.h>
 
 #define REG_SIZE_PER_LPG	0x100
+#define LPG_BASE		"lpg-base"
+#define LUT_BASE		"lut-base"
 
+/* LPG module registers */
 #define REG_LPG_PERPH_SUBTYPE		0x05
+#define REG_LPG_PATTERN_CONFIG		0x40
 #define REG_LPG_PWM_SIZE_CLK		0x41
 #define REG_LPG_PWM_FREQ_PREDIV_CLK	0x42
 #define REG_LPG_PWM_TYPE_CONFIG		0x43
@@ -36,16 +41,29 @@
 #define REG_LPG_PWM_VALUE_MSB		0x45
 #define REG_LPG_ENABLE_CONTROL		0x46
 #define REG_LPG_PWM_SYNC		0x47
+#define REG_LPG_RAMP_STEP_DURATION_LSB	0x50
+#define REG_LPG_RAMP_STEP_DURATION_MSB	0x51
+#define REG_LPG_PAUSE_HI_MULTIPLIER	0x52
+#define REG_LPG_PAUSE_LO_MULTIPLIER	0x54
+#define REG_LPG_HI_INDEX		0x56
+#define REG_LPG_LO_INDEX		0x57
+
+/* REG_LPG_PATTERN_CONFIG */
+#define LPG_PATTERN_EN_PAUSE_LO		BIT(0)
+#define LPG_PATTERN_EN_PAUSE_HI		BIT(1)
+#define LPG_PATTERN_RAMP_TOGGLE		BIT(2)
+#define LPG_PATTERN_REPEAT		BIT(3)
+#define LPG_PATTERN_RAMP_LO_TO_HI	BIT(4)
 
 /* REG_LPG_PERPH_SUBTYPE */
 #define SUBTYPE_PWM			0x0b
 #define SUBTYPE_LPG_LITE		0x11
 
 /* REG_LPG_PWM_SIZE_CLK */
-#define LPG_PWM_SIZE_MASK_LPG		BIT(4)
-#define LPG_PWM_SIZE_MASK_PWM		BIT(2)
-#define LPG_PWM_SIZE_SHIFT_LPG		4
-#define LPG_PWM_SIZE_SHIFT_PWM		2
+#define LPG_PWM_SIZE_LPG_MASK		BIT(4)
+#define LPG_PWM_SIZE_PWM_MASK		BIT(2)
+#define LPG_PWM_SIZE_LPG_SHIFT		4
+#define LPG_PWM_SIZE_PWM_SHIFT		2
 #define LPG_PWM_CLK_FREQ_SEL_MASK	GENMASK(1, 0)
 
 /* REG_LPG_PWM_FREQ_PREDIV_CLK */
@@ -64,6 +82,7 @@
 
 /* REG_LPG_ENABLE_CONTROL */
 #define LPG_EN_LPG_OUT_BIT		BIT(7)
+#define LPG_EN_LPG_OUT_SHIFT		7
 #define LPG_PWM_SRC_SELECT_MASK		BIT(2)
 #define LPG_PWM_SRC_SELECT_SHIFT	2
 #define LPG_EN_RAMP_GEN_MASK		BIT(1)
@@ -77,9 +96,18 @@
 #define NUM_CLK_PREDIV			4
 #define NUM_PWM_EXP			8
 
-enum {
+#define LPG_HI_LO_IDX_MASK		GENMASK(5, 0)
+
+/* LUT module registers */
+#define REG_LPG_LUT_1_LSB		0x42
+#define REG_LPG_LUT_RAMP_CONTROL	0xc8
+
+#define LPG_LUT_VALUE_MSB_MASK		BIT(0)
+#define LPG_LUT_COUNT_MAX		47
+
+enum lpg_src {
 	LUT_PATTERN = 0,
-	PWM_OUTPUT,
+	PWM_VALUE,
 };
 
 static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
@@ -87,6 +115,19 @@
 static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
 static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
 
+struct lpg_ramp_config {
+	u16			step_ms;
+	u8			pause_hi_count;
+	u8			pause_lo_count;
+	u8			hi_idx;
+	u8			lo_idx;
+	bool			ramp_dir_low_to_hi;
+	bool			pattern_repeat;
+	bool			toggle;
+	u32			*pattern;
+	u32			pattern_length;
+};
+
 struct lpg_pwm_config {
 	u32	pwm_size;
 	u32	pwm_clk;
@@ -96,13 +137,23 @@
 	u32	best_period_ns;
 };
 
+struct qpnp_lpg_lut {
+	struct qpnp_lpg_chip	*chip;
+	struct mutex		lock;
+	u32			reg_base;
+	u32			*pattern; /* patterns in percentage */
+};
+
 struct qpnp_lpg_channel {
 	struct qpnp_lpg_chip		*chip;
 	struct lpg_pwm_config		pwm_config;
+	struct lpg_ramp_config		ramp_config;
 	u32				lpg_idx;
 	u32				reg_base;
+	u32				max_pattern_length;
 	u8				src_sel;
 	u8				subtype;
+	bool				lut_written;
 	int				current_period_ns;
 	int				current_duty_ns;
 };
@@ -112,6 +163,7 @@
 	struct regmap		*regmap;
 	struct device		*dev;
 	struct qpnp_lpg_channel	*lpgs;
+	struct qpnp_lpg_lut	*lut;
 	struct mutex		bus_lock;
 	u32			num_lpgs;
 };
@@ -163,6 +215,36 @@
 	return rc;
 }
 
+static int qpnp_lut_write(struct qpnp_lpg_lut *lut, u16 addr, u8 val)
+{
+	int rc;
+
+	mutex_lock(&lut->chip->bus_lock);
+	rc = regmap_write(lut->chip->regmap, lut->reg_base + addr, val);
+	if (rc < 0)
+		dev_err(lut->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+				lut->reg_base + addr, val, rc);
+	mutex_unlock(&lut->chip->bus_lock);
+
+	return rc;
+}
+
+static int qpnp_lut_masked_write(struct qpnp_lpg_lut *lut,
+				u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	mutex_lock(&lut->chip->bus_lock);
+	rc = regmap_update_bits(lut->chip->regmap, lut->reg_base + addr,
+							mask, val);
+	if (rc < 0)
+		dev_err(lut->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+				lut->reg_base + addr, val, mask, rc);
+	mutex_unlock(&lut->chip->bus_lock);
+
+	return rc;
+}
+
 static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
 				struct pwm_device *pwm) {
 
@@ -227,11 +309,11 @@
 	/* pwm_clk_idx is 1 bit lower than the register value */
 	pwm_clk_idx += 1;
 	if (lpg->subtype == SUBTYPE_PWM) {
-		shift = LPG_PWM_SIZE_SHIFT_PWM;
-		mask = LPG_PWM_SIZE_MASK_PWM;
+		shift = LPG_PWM_SIZE_PWM_SHIFT;
+		mask = LPG_PWM_SIZE_PWM_MASK;
 	} else {
-		shift = LPG_PWM_SIZE_SHIFT_LPG;
-		mask = LPG_PWM_SIZE_MASK_LPG;
+		shift = LPG_PWM_SIZE_LPG_SHIFT;
+		mask = LPG_PWM_SIZE_LPG_MASK;
 	}
 
 	val = pwm_size_idx << shift | pwm_clk_idx;
@@ -252,6 +334,9 @@
 		return rc;
 	}
 
+	if (lpg->src_sel == LUT_PATTERN)
+		return 0;
+
 	val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
 	rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
 	if (rc < 0) {
@@ -280,6 +365,145 @@
 	return rc;
 }
 
+static int qpnp_lpg_set_lut_pattern(struct qpnp_lpg_channel *lpg,
+		unsigned int *pattern, unsigned int length)
+{
+	struct qpnp_lpg_lut *lut = lpg->chip->lut;
+	int i, rc = 0;
+	u16 full_duty_value, pwm_values[LPG_LUT_COUNT_MAX + 1] = {0};
+	u8 lsb, msb, addr;
+
+	if (length > lpg->max_pattern_length) {
+		dev_err(lpg->chip->dev, "new pattern length (%d) larger than predefined (%d)\n",
+				length, lpg->max_pattern_length);
+		return -EINVAL;
+	}
+
+	/* Program LUT pattern */
+	mutex_lock(&lut->lock);
+	addr = REG_LPG_LUT_1_LSB + lpg->ramp_config.lo_idx * 2;
+	for (i = 0; i < length; i++) {
+		full_duty_value = 1 << lpg->pwm_config.pwm_size;
+		pwm_values[i] = pattern[i] * full_duty_value / 100;
+
+		if (unlikely(pwm_values[i] > full_duty_value)) {
+			dev_err(lpg->chip->dev, "PWM value %d exceed the max %d\n",
+					pwm_values[i], full_duty_value);
+			rc = -EINVAL;
+			goto unlock;
+		}
+
+		if (pwm_values[i] == full_duty_value)
+			pwm_values[i] = full_duty_value - 1;
+
+		lsb = pwm_values[i] & 0xff;
+		msb = pwm_values[i] >> 8;
+		rc = qpnp_lut_write(lut, addr++, lsb);
+		if (rc < 0) {
+			dev_err(lpg->chip->dev, "Write NO.%d LUT pattern LSB (%d) failed, rc=%d",
+					i, lsb, rc);
+			goto unlock;
+		}
+
+		rc = qpnp_lut_masked_write(lut, addr++,
+				LPG_LUT_VALUE_MSB_MASK, msb);
+		if (rc < 0) {
+			dev_err(lpg->chip->dev, "Write NO.%d LUT pattern MSB (%d) failed, rc=%d",
+					i, msb, rc);
+			goto unlock;
+		}
+	}
+	lpg->ramp_config.pattern_length = length;
+unlock:
+	mutex_unlock(&lut->lock);
+
+	return rc;
+}
+
+static int qpnp_lpg_set_ramp_config(struct qpnp_lpg_channel *lpg)
+{
+	struct lpg_ramp_config *ramp = &lpg->ramp_config;
+	u8 lsb, msb, addr, mask, val;
+	int rc = 0;
+
+	/* Set ramp step duration */
+	lsb = ramp->step_ms & 0xff;
+	msb = ramp->step_ms >> 8;
+	addr = REG_LPG_RAMP_STEP_DURATION_LSB;
+	rc = qpnp_lpg_write(lpg, addr, lsb);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_LSB failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+	rc = qpnp_lpg_write(lpg, addr + 1, msb);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write RAMP_STEP_DURATION_MSB failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+
+	/* Set hi_idx and lo_idx */
+	rc = qpnp_lpg_masked_write(lpg, REG_LPG_HI_INDEX,
+			LPG_HI_LO_IDX_MASK, ramp->hi_idx);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_HI_IDX failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+
+	rc = qpnp_lpg_masked_write(lpg, REG_LPG_LO_INDEX,
+			LPG_HI_LO_IDX_MASK, ramp->lo_idx);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_LO_IDX failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+
+	/* Set pause_hi/lo_count */
+	rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_HI_MULTIPLIER,
+					ramp->pause_hi_count);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PAUSE_HI_MULTIPLIER failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+
+	rc = qpnp_lpg_write(lpg, REG_LPG_PAUSE_LO_MULTIPLIER,
+					ramp->pause_lo_count);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PAUSE_LO_MULTIPLIER failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+
+	/* Set LPG_PATTERN_CONFIG */
+	addr = REG_LPG_PATTERN_CONFIG;
+	mask = LPG_PATTERN_EN_PAUSE_LO | LPG_PATTERN_EN_PAUSE_HI
+		| LPG_PATTERN_RAMP_TOGGLE | LPG_PATTERN_REPEAT
+		| LPG_PATTERN_RAMP_LO_TO_HI;
+	val = 0;
+	if (ramp->pause_lo_count != 0)
+		val |= LPG_PATTERN_EN_PAUSE_LO;
+	if (ramp->pause_hi_count != 0)
+		val |= LPG_PATTERN_EN_PAUSE_HI;
+	if (ramp->ramp_dir_low_to_hi)
+		val |= LPG_PATTERN_RAMP_LO_TO_HI;
+	if (ramp->pattern_repeat)
+		val |= LPG_PATTERN_REPEAT;
+	if (ramp->toggle)
+		val |= LPG_PATTERN_RAMP_TOGGLE;
+
+	rc = qpnp_lpg_masked_write(lpg, addr, mask, val);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Write LPG_PATTERN_CONFIG failed, rc=%d\n",
+					rc);
+		return rc;
+	}
+
+	return rc;
+}
+
 static void __qpnp_lpg_calc_pwm_period(int period_ns,
 			struct lpg_pwm_config *pwm_config)
 {
@@ -396,17 +620,202 @@
 		return -EINVAL;
 	}
 
-	if (period_ns != lpg->current_period_ns)
+	if (period_ns != lpg->current_period_ns) {
 		__qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
 
+		/* program LUT if PWM period is changed */
+		if (lpg->src_sel == LUT_PATTERN) {
+			rc = qpnp_lpg_set_lut_pattern(lpg,
+					lpg->ramp_config.pattern,
+					lpg->ramp_config.pattern_length);
+			if (rc < 0) {
+				dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n",
+						lpg->lpg_idx, rc);
+				return rc;
+			}
+			lpg->lut_written = true;
+		}
+	}
+
 	if (period_ns != lpg->current_period_ns ||
 			duty_ns != lpg->current_duty_ns)
 		__qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
 
 	rc = qpnp_lpg_set_pwm_config(lpg);
-	if (rc < 0)
+	if (rc < 0) {
 		dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
 						lpg->lpg_idx, rc);
+		return rc;
+	}
+
+	lpg->current_period_ns = period_ns;
+	lpg->current_duty_ns = duty_ns;
+
+	return rc;
+}
+
+static int qpnp_lpg_pwm_src_enable(struct qpnp_lpg_channel *lpg, bool en)
+{
+	struct qpnp_lpg_chip *chip = lpg->chip;
+	struct qpnp_lpg_lut *lut = chip->lut;
+	u8 mask, val;
+	int rc;
+
+	mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT |
+					LPG_EN_RAMP_GEN_MASK;
+	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+	if (lpg->src_sel == LUT_PATTERN)
+		val |= 1 << LPG_EN_RAMP_GEN_SHIFT;
+
+	if (en)
+		val |= 1 << LPG_EN_LPG_OUT_SHIFT;
+
+	rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+	if (rc < 0) {
+		dev_err(chip->dev, "Write LPG_ENABLE_CONTROL failed, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	if (lpg->src_sel == LUT_PATTERN && en) {
+		mutex_lock(&lut->lock);
+		val = 1 << lpg->lpg_idx;
+		rc = qpnp_lut_write(lut, REG_LPG_LUT_RAMP_CONTROL, val);
+		if (rc < 0)
+			dev_err(chip->dev, "Write LPG_LUT_RAMP_CONTROL failed, rc=%d\n",
+					rc);
+		mutex_unlock(&lut->lock);
+	}
+
+	return rc;
+}
+
+static int qpnp_lpg_pwm_set_output_type(struct pwm_chip *pwm_chip,
+		struct pwm_device *pwm, enum pwm_output_type output_type)
+{
+	struct qpnp_lpg_channel *lpg;
+	enum lpg_src src_sel;
+	int rc;
+
+	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+	if (lpg == NULL) {
+		dev_err(pwm_chip->dev, "lpg not found\n");
+		return -ENODEV;
+	}
+
+	if (lpg->chip->lut == NULL) {
+		pr_debug("lpg%d only support PWM mode\n", lpg->lpg_idx);
+		return 0;
+	}
+
+	src_sel = (output_type == PWM_OUTPUT_MODULATED) ?
+				LUT_PATTERN : PWM_VALUE;
+	if (src_sel == lpg->src_sel)
+		return 0;
+
+	if (src_sel == LUT_PATTERN) {
+		/* program LUT if it's never been programmed */
+		if (!lpg->lut_written) {
+			rc = qpnp_lpg_set_lut_pattern(lpg,
+					lpg->ramp_config.pattern,
+					lpg->ramp_config.pattern_length);
+			if (rc < 0) {
+				dev_err(pwm_chip->dev, "set LUT pattern failed for LPG%d, rc=%d\n",
+						lpg->lpg_idx, rc);
+				return rc;
+			}
+			lpg->lut_written = true;
+		}
+
+		rc = qpnp_lpg_set_ramp_config(lpg);
+		if (rc < 0) {
+			dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
+					lpg->lpg_idx, rc);
+			return rc;
+		}
+	}
+
+	lpg->src_sel = src_sel;
+
+	if (pwm_is_enabled(pwm)) {
+		rc = qpnp_lpg_pwm_src_enable(lpg, true);
+		if (rc < 0) {
+			dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+					lpg->lpg_idx, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_lpg_pwm_set_output_pattern(struct pwm_chip *pwm_chip,
+	struct pwm_device *pwm, struct pwm_output_pattern *output_pattern)
+{
+	struct qpnp_lpg_channel *lpg;
+	int rc = 0, i, period_ns, duty_ns;
+	u32 *percentages;
+
+	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+	if (lpg == NULL) {
+		dev_err(pwm_chip->dev, "lpg not found\n");
+		return -ENODEV;
+	}
+
+	if (output_pattern->num_entries > lpg->max_pattern_length) {
+		dev_err(lpg->chip->dev, "pattern length %d shouldn't exceed %d\n",
+				output_pattern->num_entries,
+				lpg->max_pattern_length);
+		return -EINVAL;
+	}
+
+	percentages = kcalloc(output_pattern->num_entries,
+				sizeof(u32), GFP_KERNEL);
+	if (!percentages)
+		return -ENOMEM;
+
+	period_ns = pwm_get_period(pwm);
+	for (i = 0; i < output_pattern->num_entries; i++) {
+		duty_ns = output_pattern->duty_pattern[i];
+		if (duty_ns > period_ns) {
+			dev_err(lpg->chip->dev, "duty %dns is larger than period %dns\n",
+					duty_ns, period_ns);
+			goto err;
+		}
+		/* Translate the pattern in duty_ns to percentage */
+		if ((INT_MAX / duty_ns) < 100)
+			percentages[i] = duty_ns / (period_ns / 100);
+		else
+			percentages[i] = (duty_ns * 100) / period_ns;
+	}
+
+	rc = qpnp_lpg_set_lut_pattern(lpg, percentages,
+			output_pattern->num_entries);
+	if (rc < 0) {
+		dev_err(lpg->chip->dev, "Set LUT pattern failed for LPG%d, rc=%d\n",
+				lpg->lpg_idx, rc);
+		goto err;
+	}
+
+	lpg->lut_written = true;
+	memcpy(lpg->ramp_config.pattern, percentages,
+			output_pattern->num_entries);
+	lpg->ramp_config.hi_idx = lpg->ramp_config.lo_idx +
+				output_pattern->num_entries - 1;
+	if ((INT_MAX / period_ns) > output_pattern->cycles_per_duty)
+		lpg->ramp_config.step_ms = output_pattern->cycles_per_duty *
+			period_ns / NSEC_PER_MSEC;
+	else
+		lpg->ramp_config.step_ms = (period_ns / NSEC_PER_MSEC) *
+			output_pattern->cycles_per_duty;
+
+	rc = qpnp_lpg_set_ramp_config(lpg);
+	if (rc < 0)
+		dev_err(pwm_chip->dev, "Config LPG%d ramping failed, rc=%d\n",
+				lpg->lpg_idx, rc);
+err:
+	kfree(percentages);
 
 	return rc;
 }
@@ -416,7 +825,6 @@
 {
 	struct qpnp_lpg_channel *lpg;
 	int rc = 0;
-	u8 mask, val;
 
 	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
 	if (lpg == NULL) {
@@ -431,10 +839,7 @@
 		return rc;
 	}
 
-	mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
-	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
-
-	rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+	rc = qpnp_lpg_pwm_src_enable(lpg, true);
 	if (rc < 0)
 		dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
 						lpg->lpg_idx, rc);
@@ -447,7 +852,6 @@
 {
 	struct qpnp_lpg_channel *lpg;
 	int rc;
-	u8 mask, val;
 
 	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
 	if (lpg == NULL) {
@@ -455,10 +859,7 @@
 		return;
 	}
 
-	mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
-	val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
-
-	rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+	rc = qpnp_lpg_pwm_src_enable(lpg, false);
 	if (rc < 0) {
 		dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
 						lpg->lpg_idx, rc);
@@ -471,13 +872,32 @@
 							rc);
 }
 
+static int qpnp_lpg_pwm_output_types_supported(struct pwm_chip *pwm_chip,
+				struct pwm_device *pwm)
+{
+	enum pwm_output_type type = PWM_OUTPUT_FIXED;
+	struct qpnp_lpg_channel *lpg;
+
+	lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+	if (lpg == NULL) {
+		dev_err(pwm_chip->dev, "lpg not found\n");
+		return type;
+	}
+
+	if (lpg->chip->lut != NULL)
+		type |= PWM_OUTPUT_MODULATED;
+
+	return type;
+}
+
 #ifdef CONFIG_DEBUG_FS
 static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
 {
 	struct qpnp_lpg_channel *lpg;
 	struct lpg_pwm_config *cfg;
+	struct lpg_ramp_config *ramp;
 	struct pwm_device *pwm;
-	int i;
+	int i, j;
 
 	for (i = 0; i < pwm_chip->npwm; i++) {
 		pwm = &pwm_chip->pwms[i];
@@ -512,12 +932,39 @@
 		seq_printf(s, "     pwm_value = %d\n", cfg->pwm_value);
 		seq_printf(s, "  Requested period: %dns, best period = %dns\n",
 				pwm_get_period(pwm), cfg->best_period_ns);
+
+		ramp = &lpg->ramp_config;
+		if (pwm_get_output_type(pwm) == PWM_OUTPUT_MODULATED) {
+			seq_puts(s, "  ramping duty percentages:");
+			for (j = 0; j < ramp->pattern_length; j++)
+				seq_printf(s, " %d", ramp->pattern[j]);
+			seq_puts(s, "\n");
+			seq_printf(s, "  ramping time per step: %dms\n",
+					ramp->step_ms);
+			seq_printf(s, "  ramping low index: %d\n",
+					ramp->lo_idx);
+			seq_printf(s, "  ramping high index: %d\n",
+					ramp->hi_idx);
+			seq_printf(s, "  ramping from low to high: %d\n",
+					ramp->ramp_dir_low_to_hi);
+			seq_printf(s, "  ramping pattern repeat: %d\n",
+					ramp->pattern_repeat);
+			seq_printf(s, "  ramping toggle: %d\n",
+					ramp->toggle);
+			seq_printf(s, "  ramping pause count at low index: %d\n",
+					ramp->pause_lo_count);
+			seq_printf(s, "  ramping pause count at high index: %d\n",
+					ramp->pause_hi_count);
+		}
 	}
 }
 #endif
 
 static const struct pwm_ops qpnp_lpg_pwm_ops = {
 	.config = qpnp_lpg_pwm_config,
+	.get_output_type_supported = qpnp_lpg_pwm_output_types_supported,
+	.set_output_type = qpnp_lpg_pwm_set_output_type,
+	.set_output_pattern = qpnp_lpg_pwm_set_output_pattern,
 	.enable = qpnp_lpg_pwm_enable,
 	.disable = qpnp_lpg_pwm_disable,
 #ifdef CONFIG_DEBUG_FS
@@ -528,15 +975,19 @@
 
 static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
 {
+	struct device_node *child;
+	struct qpnp_lpg_channel *lpg;
+	struct lpg_ramp_config *ramp;
 	int rc = 0, i;
-	u64 base, length;
+	u32 base, length, lpg_chan_id, tmp;
 	const __be32 *addr;
 
 	addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
 	if (!addr) {
-		dev_err(chip->dev, "Getting address failed\n");
+		dev_err(chip->dev, "Get %s address failed\n", LPG_BASE);
 		return -EINVAL;
 	}
+
 	base = be32_to_cpu(addr[0]);
 	length = be32_to_cpu(addr[1]);
 
@@ -550,7 +1001,7 @@
 		chip->lpgs[i].chip = chip;
 		chip->lpgs[i].lpg_idx = i;
 		chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
-		chip->lpgs[i].src_sel = PWM_OUTPUT;
+		chip->lpgs[i].src_sel = PWM_VALUE;
 		rc = qpnp_lpg_read(&chip->lpgs[i], REG_LPG_PERPH_SUBTYPE,
 				&chip->lpgs[i].subtype);
 		if (rc < 0) {
@@ -559,7 +1010,142 @@
 		}
 	}
 
-	return rc;
+	addr = of_get_address(chip->dev->of_node, 1, NULL, NULL);
+	if (!addr) {
+		pr_debug("NO LUT address assigned\n");
+		return 0;
+	}
+
+	chip->lut = devm_kmalloc(chip->dev, sizeof(*chip->lut), GFP_KERNEL);
+	if (!chip->lut)
+		return -ENOMEM;
+
+	chip->lut->chip = chip;
+	chip->lut->reg_base = be32_to_cpu(*addr);
+	mutex_init(&chip->lut->lock);
+
+	rc = of_property_count_elems_of_size(chip->dev->of_node,
+			"qcom,lut-patterns", sizeof(u32));
+	if (rc < 0) {
+		dev_err(chip->dev, "Read qcom,lut-patterns failed, rc=%d\n",
+							rc);
+		return rc;
+	}
+
+	length = rc;
+	if (length > LPG_LUT_COUNT_MAX) {
+		dev_err(chip->dev, "qcom,lut-patterns length %d exceed max %d\n",
+				length, LPG_LUT_COUNT_MAX);
+		return -EINVAL;
+	}
+
+	chip->lut->pattern = devm_kcalloc(chip->dev, LPG_LUT_COUNT_MAX,
+			sizeof(*chip->lut->pattern), GFP_KERNEL);
+	if (!chip->lut->pattern)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(chip->dev->of_node, "qcom,lut-patterns",
+					chip->lut->pattern, length);
+	if (rc < 0) {
+		dev_err(chip->dev, "Get qcom,lut-patterns failed, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	if (of_get_available_child_count(chip->dev->of_node) == 0) {
+		dev_err(chip->dev, "No ramp configuration for any LPG\n");
+		return -EINVAL;
+	}
+
+	for_each_available_child_of_node(chip->dev->of_node, child) {
+		rc = of_property_read_u32(child, "qcom,lpg-chan-id",
+						&lpg_chan_id);
+		if (rc < 0) {
+			dev_err(chip->dev, "Get qcom,lpg-chan-id failed for node %s, rc=%d\n",
+					child->name, rc);
+			return rc;
+		}
+
+		if (lpg_chan_id > chip->num_lpgs) {
+			dev_err(chip->dev, "lpg-chann-id %d is out of range 1~%d\n",
+					lpg_chan_id, chip->num_lpgs);
+			return -EINVAL;
+		}
+
+		/* lpg channel id is indexed from 1 in hardware */
+		lpg = &chip->lpgs[lpg_chan_id - 1];
+		ramp = &lpg->ramp_config;
+
+		rc = of_property_read_u32(child, "qcom,ramp-step-ms", &tmp);
+		if (rc < 0) {
+			dev_err(chip->dev, "get qcom,ramp-step-ms failed for lpg%d, rc=%d\n",
+					lpg_chan_id, rc);
+			return rc;
+		}
+		ramp->step_ms = (u16)tmp;
+
+		rc = of_property_read_u32(child, "qcom,ramp-low-index", &tmp);
+		if (rc < 0) {
+			dev_err(chip->dev, "get qcom,ramp-low-index failed for lpg%d, rc=%d\n",
+						lpg_chan_id, rc);
+			return rc;
+		}
+		ramp->lo_idx = (u8)tmp;
+		if (ramp->lo_idx >= LPG_LUT_COUNT_MAX) {
+			dev_err(chip->dev, "qcom,ramp-low-index should less than max %d\n",
+					LPG_LUT_COUNT_MAX);
+			return -EINVAL;
+		}
+
+		rc = of_property_read_u32(child, "qcom,ramp-high-index", &tmp);
+		if (rc < 0) {
+			dev_err(chip->dev, "get qcom,ramp-high-index failed for lpg%d, rc=%d\n",
+						lpg_chan_id, rc);
+			return rc;
+		}
+		ramp->hi_idx = (u8)tmp;
+
+		if (ramp->hi_idx > LPG_LUT_COUNT_MAX) {
+			dev_err(chip->dev, "qcom,ramp-high-index shouldn't exceed max %d\n",
+						LPG_LUT_COUNT_MAX);
+			return -EINVAL;
+		}
+
+		if (ramp->hi_idx <= ramp->lo_idx) {
+			dev_err(chip->dev, "high-index(%d) should be larger than low-index(%d)\n",
+						ramp->hi_idx, ramp->lo_idx);
+			return -EINVAL;
+		}
+
+		ramp->pattern_length = ramp->hi_idx - ramp->lo_idx + 1;
+		ramp->pattern = &chip->lut->pattern[ramp->lo_idx];
+		lpg->max_pattern_length = ramp->pattern_length;
+
+		rc = of_property_read_u32(child,
+				"qcom,ramp-pause-hi-count", &tmp);
+		if (rc < 0)
+			ramp->pause_hi_count = 0;
+		else
+			ramp->pause_hi_count = (u8)tmp;
+
+		rc = of_property_read_u32(child,
+				"qcom,ramp-pause-lo-count", &tmp);
+		if (rc < 0)
+			ramp->pause_lo_count = 0;
+		else
+			ramp->pause_lo_count = (u8)tmp;
+
+		ramp->ramp_dir_low_to_hi = of_property_read_bool(child,
+				"qcom,ramp-from-low-to-high");
+
+		ramp->pattern_repeat = of_property_read_bool(child,
+				"qcom,ramp-pattern-repeat");
+
+		ramp->toggle =  of_property_read_bool(child,
+				"qcom,ramp-toggle");
+	}
+
+	return 0;
 }
 
 static int qpnp_lpg_probe(struct platform_device *pdev)
@@ -583,7 +1169,7 @@
 	if (rc < 0) {
 		dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
 				rc);
-		goto destroy;
+		goto err_out;
 	}
 
 	dev_set_drvdata(chip->dev, chip);
@@ -595,11 +1181,11 @@
 	rc = pwmchip_add(&chip->pwm_chip);
 	if (rc < 0) {
 		dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
-		goto destroy;
+		goto err_out;
 	}
 
 	return 0;
-destroy:
+err_out:
 	mutex_destroy(&chip->bus_lock);
 	return rc;
 }
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index a813239..ea2b53d 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -223,11 +223,60 @@
 	return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
 }
 
+static ssize_t output_type_show(struct device *child,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+	const char *output_type = "unknown";
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+	switch (state.output_type) {
+	case PWM_OUTPUT_FIXED:
+		output_type = "fixed";
+		break;
+	case PWM_OUTPUT_MODULATED:
+		output_type = "modulated";
+		break;
+	default:
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", output_type);
+}
+
+static ssize_t output_type_store(struct device *child,
+			      struct device_attribute *attr,
+			      const char *buf, size_t size)
+{
+	struct pwm_export *export = child_to_pwm_export(child);
+	struct pwm_device *pwm = export->pwm;
+	struct pwm_state state;
+	int ret = -EINVAL;
+
+	mutex_lock(&export->lock);
+	pwm_get_state(pwm, &state);
+	if (sysfs_streq(buf, "fixed"))
+		state.output_type = PWM_OUTPUT_FIXED;
+	else if (sysfs_streq(buf, "modulated"))
+		state.output_type = PWM_OUTPUT_MODULATED;
+	else
+		goto unlock;
+
+	ret = pwm_apply_state(pwm, &state);
+unlock:
+	mutex_unlock(&export->lock);
+
+	return ret ? : size;
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
 static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(output_type);
 
 static struct attribute *pwm_attrs[] = {
 	&dev_attr_period.attr,
@@ -235,6 +284,7 @@
 	&dev_attr_enable.attr,
 	&dev_attr_polarity.attr,
 	&dev_attr_capture.attr,
+	&dev_attr_output_type.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c
index 79d7cba..07a6198 100644
--- a/drivers/regulator/qpnp-lcdb-regulator.c
+++ b/drivers/regulator/qpnp-lcdb-regulator.c
@@ -991,7 +991,8 @@
 }
 
 #define MIN_BST_VOLTAGE_MV			4700
-#define MAX_BST_VOLTAGE_MV			6250
+#define PM660_MAX_BST_VOLTAGE_MV		6250
+#define MAX_BST_VOLTAGE_MV			6275
 #define MIN_VOLTAGE_MV				4000
 #define MAX_VOLTAGE_MV				6000
 #define VOLTAGE_MIN_STEP_100_MV			4000
@@ -1017,8 +1018,14 @@
 
 	if (bst_voltage_mv < MIN_BST_VOLTAGE_MV)
 		bst_voltage_mv = MIN_BST_VOLTAGE_MV;
-	else if (bst_voltage_mv > MAX_BST_VOLTAGE_MV)
-		bst_voltage_mv = MAX_BST_VOLTAGE_MV;
+
+	if (pmic_subtype == PM660L_SUBTYPE) {
+		if (bst_voltage_mv > PM660_MAX_BST_VOLTAGE_MV)
+			bst_voltage_mv = PM660_MAX_BST_VOLTAGE_MV;
+	} else {
+		if (bst_voltage_mv > MAX_BST_VOLTAGE_MV)
+			bst_voltage_mv = MAX_BST_VOLTAGE_MV;
+	}
 
 	if (bst_voltage_mv != bst->voltage_mv) {
 		if (pmic_subtype == PM660L_SUBTYPE) {
@@ -1883,6 +1890,8 @@
 			return rc;
 		}
 		lcdb->bst.soft_start_us = (val & SOFT_START_MASK) * 200 + 200;
+		if (!lcdb->bst.headroom_mv)
+			lcdb->bst.headroom_mv = PM660_BST_HEADROOM_DEFAULT_MV;
 	} else {
 		rc = qpnp_lcdb_read(lcdb, lcdb->base +
 				    LCDB_BST_SS_CTL_REG, &val, 1);
@@ -1891,6 +1900,8 @@
 			return rc;
 		}
 		lcdb->bst.soft_start_us = soft_start_us[val & SOFT_START_MASK];
+		if (!lcdb->bst.headroom_mv)
+			lcdb->bst.headroom_mv = BST_HEADROOM_DEFAULT_MV;
 	}
 
 	return 0;
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index f0267b4..5fafaca 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -821,4 +821,50 @@
 	  provides a means to support more logical channels
 	  via muxing than BAM could without muxing.
 
+	config MSM_GLINK_BGCOM_XPRT
+	depends on MSM_GLINK
+	depends on MSM_BGCOM
+	tristate "Generic Link (G-Link) BGCOM Transport"
+	help
+		G-Link BGCOM Transport is a Transport plug-in developed over BGCOM.
+		This transport plug-in performs marshaling of G-Link
+		commands & data to the appropriate BGCOM format and
+		allows for G-Link communication with remote subsystems that are
+		external to the System-on-Chip.
+
+	config MSM_BGCOM_INTERFACE
+	bool "Driver support for Blackghost Communication"
+	depends on MSM_BGCOM
+	help
+		Create a bg_com_dev device node for user space communication.
+		Single user space client can open device node for communication
+		from hardware. Hardware will provide access to read
+		registers and read/write AHB memory in the device.
+
+	config MSM_BGRSB
+	bool "Provide support for rsb events on Blackghost chipset"
+	depends on MSM_GLINK
+	help
+		BGRSB communicates to BG over Glink for RSB configuration and
+		enable/disable on device power state change. It enables/disables
+		the regulator specific to RSB. Sends the side band events generated
+		by BG to input framework.
+
+	config MSM_PIL_SSR_BG
+	tristate "MSM Subsystem Blackghost(BG) Support"
+	depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
+	help
+		Support for booting and shutting down Blackghost(BG) SOC which is
+		an external SOC. This driver communicates with Blackghost(BG) SOC
+		via pair of IPC GPIOs for inward and outward signals between MSM
+		and Blackghost(BG) SOC.
+
+	config MSM_BGCOM
+	bool "Provide APIs to communicate with Blackghost chipset"
+	help
+		BGCOM is a thin layer above SPI. It is used whithin a SoC for
+		communication between G-Link/bg_com_dev and BG processor over SPI.
+		This handle the interrupts raised by BG and notify the G-link with
+		interrupt event and event data.
+
 source "drivers/soc/qcom/wcnss/Kconfig"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index ba298a6..5edf3b8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -37,6 +37,7 @@
 obj-$(CONFIG_MSM_GLINK) += glink.o glink_debugfs.o glink_ssr.o
 obj-$(CONFIG_MSM_TZ_SMMU) += msm_tz_smmu.o
 obj-$(CONFIG_MSM_GLINK_LOOPBACK_SERVER) += glink_loopback_server.o
+obj-$(CONFIG_MSM_GLINK_BGCOM_XPRT) += glink_bgcom_xprt.o
 obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o
 obj-$(CONFIG_MSM_GLINK_SPI_XPRT) += glink_spi_xprt.o
 obj-$(CONFIG_MSM_SPCOM) += spcom.o
@@ -67,7 +68,9 @@
 obj-$(CONFIG_MEM_SHARE_QMI_SERVICE)		+= memshare/
 obj-$(CONFIG_MSM_PIL)   +=      peripheral-loader.o
 obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
+obj-$(CONFIG_MSM_PIL_SSR_BG) += subsys-pil-bg.o
 obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o
+obj-$(CONFIG_MSM_BGCOM) += bgcom_spi.o
 
 obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
 
@@ -78,6 +81,8 @@
        obj-y += microdump_collector.o
 endif
 obj-$(CONFIG_MSM_SYSMON_COMM) += sysmon.o sysmon-qmi.o
+obj-$(CONFIG_MSM_BGRSB) += bg_rsb.o
+obj-$(CONFIG_MSM_BGCOM_INTERFACE) += bgcom_interface.o
 obj-$(CONFIG_MSM_JTAGV8) += jtagv8.o jtagv8-etm.o
 obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
 obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
diff --git a/drivers/soc/qcom/bg_rsb.c b/drivers/soc/qcom/bg_rsb.c
new file mode 100644
index 0000000..fdfd7b7
--- /dev/null
+++ b/drivers/soc/qcom/bg_rsb.c
@@ -0,0 +1,1037 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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(msg) "bgrsb: %s: " msg, __func__
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <soc/qcom/glink.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/regulator/consumer.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/subsystem_notif.h>
+
+#include "bgrsb.h"
+
+#define BGRSB_GLINK_INTENT_SIZE 0x04
+#define BGRSB_MSG_SIZE 0x08
+#define TIMEOUT_MS 500
+
+#define BGRSB_LDO15_VTG_MIN_UV 3300000
+#define BGRSB_LDO15_VTG_MAX_UV 3300000
+
+#define BGRSB_LDO11_VTG_MIN_UV 1800000
+#define BGRSB_LDO11_VTG_MAX_UV 1800000
+
+#define BGRSB_BGWEAR_SUBSYS "bg-wear"
+
+#define BGRSB_BTTN_CONFIGURE 5
+#define BGRSB_POWER_CALIBRATION 2
+#define BGRSB_POWER_ENABLE 1
+#define BGRSB_POWER_DISABLE 0
+#define BGRSB_GLINK_POWER_ENABLE 6
+#define BGRSB_GLINK_POWER_DISABLE 7
+
+
+struct bgrsb_regulator {
+	struct regulator *regldo11;
+	struct regulator *regldo15;
+};
+
+enum ldo_task {
+	BGRSB_ENABLE_LDO11,
+	BGRSB_ENABLE_LDO15,
+	BGRSB_DISABLE_LDO11,
+	BGRSB_DISABLE_LDO15,
+	BGRSB_NO_ACTION
+};
+
+enum bgrsb_state {
+	BGRSB_STATE_UNKNOWN,
+	BGRSB_STATE_INIT,
+	BGRSB_STATE_LDO11_ENABLED,
+	BGRSB_STATE_RSB_CONFIGURED,
+	BGRSB_STATE_LDO15_ENABLED,
+	BGRSB_STATE_RSB_ENABLED
+};
+
+struct bgrsb_msg {
+	uint32_t cmd_id;
+	uint32_t data;
+};
+
+struct bgrsb_priv {
+	void *handle;
+	struct input_dev *input;
+	struct mutex glink_mutex;
+
+	enum bgrsb_state bgrsb_current_state;
+	enum glink_link_state link_state;
+
+	bool chnl_state;
+	void *lhndl;
+
+	struct work_struct bg_up_work;
+	struct work_struct bg_down_work;
+
+	struct work_struct rsb_up_work;
+	struct work_struct rsb_down_work;
+
+	struct work_struct rsb_glink_up_work;
+	struct work_struct rsb_glink_down_work;
+
+	struct work_struct rsb_calibration_work;
+	struct work_struct bttn_configr_work;
+
+	struct work_struct glink_work;
+
+	struct workqueue_struct *bgrsb_event_wq;
+	struct workqueue_struct *bgrsb_wq;
+
+	struct bg_glink_chnl chnl;
+	char rx_buf[BGRSB_GLINK_INTENT_SIZE];
+
+	struct bgrsb_regulator rgltr;
+
+	enum ldo_task ldo_action;
+
+	void *bgwear_subsys_handle;
+
+	struct completion bg_resp_cmplt;
+	struct completion wrk_cmplt;
+	struct completion bg_lnikup_cmplt;
+	struct completion tx_done;
+
+	struct device *ldev;
+
+	wait_queue_head_t link_state_wait;
+
+	uint32_t calbrtion_intrvl;
+	uint32_t calbrtion_cpi;
+
+	uint8_t bttn_configs;
+
+	bool calibration_needed;
+	bool is_calibrd;
+};
+
+static void *bgrsb_drv;
+static int bgrsb_enable(struct bgrsb_priv *dev, bool enable);
+
+int bgrsb_send_input(struct event *evnt)
+{
+	uint8_t press_code;
+	uint8_t value;
+
+	struct bgrsb_priv *dev =
+			container_of(bgrsb_drv, struct bgrsb_priv, lhndl);
+
+	if (!evnt)
+		return -EINVAL;
+
+	if (evnt->sub_id == 1) {
+		input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data);
+		input_sync(dev->input);
+	} else if (evnt->sub_id == 2) {
+
+		press_code = (uint8_t) evnt->evnt_data;
+		value = (uint8_t) (evnt->evnt_data >> 8);
+
+		switch (press_code) {
+		case 0x1:
+			if (value == 0) {
+				input_report_key(dev->input, KEY_VOLUMEDOWN, 1);
+				input_sync(dev->input);
+			} else {
+				input_report_key(dev->input, KEY_VOLUMEDOWN, 0);
+				input_sync(dev->input);
+			}
+			break;
+		case 0x2:
+			if (value == 0) {
+				input_report_key(dev->input, KEY_VOLUMEUP, 1);
+				input_sync(dev->input);
+			} else {
+				input_report_key(dev->input, KEY_VOLUMEUP, 0);
+				input_sync(dev->input);
+			}
+			break;
+		case 0x3:
+			if (value == 0) {
+				input_report_key(dev->input, KEY_POWER, 1);
+				input_sync(dev->input);
+			} else {
+				input_report_key(dev->input, KEY_POWER, 0);
+				input_sync(dev->input);
+			}
+			break;
+		default:
+			pr_info("event: type[%d] , data: %d\n",
+						evnt->sub_id, evnt->evnt_data);
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL(bgrsb_send_input);
+
+static void bgrsb_glink_notify_rx(void *handle, const void *priv,
+	const void *pkt_priv, const void *ptr, size_t size)
+{
+	struct bgrsb_priv *dev = (struct bgrsb_priv *)priv;
+
+	memcpy(dev->rx_buf, ptr, size);
+	glink_rx_done(dev->handle, ptr, false);
+	complete(&dev->bg_resp_cmplt);
+}
+
+static void bgrsb_glink_notify_state(void *handle, const void *priv,
+	unsigned int event)
+{
+	struct bgrsb_priv *dev = (struct bgrsb_priv *)priv;
+
+	switch (event) {
+	case GLINK_CONNECTED:
+		complete(&dev->bg_lnikup_cmplt);
+		break;
+	case GLINK_REMOTE_DISCONNECTED:
+	case GLINK_LOCAL_DISCONNECTED:
+		dev->chnl_state = false;
+		break;
+	}
+}
+
+static void bgrsb_glink_notify_tx_done(void *handle, const void *priv,
+	const void *pkt_priv, const void *ptr)
+{
+	struct bgrsb_priv *dev = (struct bgrsb_priv *)priv;
+
+	complete(&dev->tx_done);
+}
+
+static void bgrsb_glink_close_work(struct work_struct *work)
+{
+	struct bgrsb_priv *dev =
+			container_of(work, struct bgrsb_priv, glink_work);
+
+	if (dev->handle)
+		glink_close(dev->handle);
+	dev->handle = NULL;
+}
+
+static void bgrsb_glink_open_work(struct work_struct *work)
+{
+	struct glink_open_config open_cfg;
+	void *hndl = NULL;
+	int rc = 0;
+	struct bgrsb_priv *dev =
+			container_of(work, struct bgrsb_priv, glink_work);
+
+	if (dev->handle)
+		return;
+
+	memset(&open_cfg, 0, sizeof(struct glink_open_config));
+	open_cfg.priv = (void *)dev;
+	open_cfg.edge = dev->chnl.chnl_edge;
+	open_cfg.transport = dev->chnl.chnl_trnsprt;
+	open_cfg.name = dev->chnl.chnl_name;
+	open_cfg.notify_tx_done = bgrsb_glink_notify_tx_done;
+	open_cfg.notify_state = bgrsb_glink_notify_state;
+	open_cfg.notify_rx = bgrsb_glink_notify_rx;
+
+	init_completion(&dev->bg_lnikup_cmplt);
+	hndl = glink_open(&open_cfg);
+
+	if (IS_ERR_OR_NULL(hndl)) {
+		pr_err("Glink open failed[%s]\n",
+						dev->chnl.chnl_name);
+		dev->handle = NULL;
+		return;
+	}
+
+	rc = wait_for_completion_timeout(&dev->bg_lnikup_cmplt,
+						msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("Channel open failed. Time out\n");
+		return;
+	}
+	dev->chnl_state = true;
+	dev->handle = hndl;
+}
+
+static void bgrsb_glink_state_cb(struct glink_link_state_cb_info *cb_info,
+	void *data)
+{
+	struct bgrsb_priv *dev = (struct bgrsb_priv *)data;
+
+	dev->link_state = cb_info->link_state;
+	switch (dev->link_state) {
+	case GLINK_LINK_STATE_UP:
+		INIT_WORK(&dev->glink_work, bgrsb_glink_open_work);
+		queue_work(dev->bgrsb_event_wq, &dev->glink_work);
+		break;
+	case GLINK_LINK_STATE_DOWN:
+		INIT_WORK(&dev->glink_work, bgrsb_glink_close_work);
+		queue_work(dev->bgrsb_event_wq, &dev->glink_work);
+		break;
+	}
+}
+
+static int bgrsb_init_link_inf(struct bgrsb_priv *dev)
+{
+	struct glink_link_info link_info;
+	void *hndl;
+
+	link_info.glink_link_state_notif_cb = bgrsb_glink_state_cb;
+	link_info.transport = dev->chnl.chnl_trnsprt;
+	link_info.edge = dev->chnl.chnl_edge;
+
+	hndl = glink_register_link_state_cb(&link_info, (void *)dev);
+	if (IS_ERR_OR_NULL(hndl)) {
+		pr_err("Unable to register link[%s]\n",
+							dev->chnl.chnl_name);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int bgrsb_init_regulators(struct device *pdev)
+{
+	struct regulator *reg11;
+	struct regulator *reg15;
+	struct bgrsb_priv *dev = dev_get_drvdata(pdev);
+
+	reg11 = regulator_get(pdev, "vdd-ldo1");
+	if (IS_ERR_OR_NULL(reg11)) {
+		pr_err("Unable to get regulator for LDO-11\n");
+		return PTR_ERR(reg11);
+	}
+
+	reg15 = regulator_get(pdev, "vdd-ldo2");
+	if (IS_ERR_OR_NULL(reg15)) {
+		pr_err("Unable to get regulator for LDO-15\n");
+		return PTR_ERR(reg15);
+	}
+
+	dev->rgltr.regldo11 = reg11;
+	dev->rgltr.regldo15 = reg15;
+
+	return 0;
+}
+
+static int bgrsb_ldo_work(struct bgrsb_priv *dev, enum ldo_task ldo_action)
+{
+	int ret = 0;
+
+	switch (ldo_action) {
+	case BGRSB_ENABLE_LDO11:
+		ret = regulator_set_voltage(dev->rgltr.regldo11,
+				BGRSB_LDO11_VTG_MIN_UV, BGRSB_LDO11_VTG_MAX_UV);
+		if (ret) {
+			pr_err("Failed to request LDO-11 voltage.\n");
+			goto err_ret;
+		}
+		ret = regulator_enable(dev->rgltr.regldo11);
+		if (ret) {
+			pr_err("Failed to enable LDO-11 %d\n", ret);
+			goto err_ret;
+		}
+		break;
+
+	case BGRSB_ENABLE_LDO15:
+		ret = regulator_set_voltage(dev->rgltr.regldo15,
+				BGRSB_LDO15_VTG_MIN_UV, BGRSB_LDO15_VTG_MAX_UV);
+		if (ret) {
+			pr_err("Failed to request LDO-15 voltage.\n");
+			goto err_ret;
+		}
+		ret = regulator_enable(dev->rgltr.regldo15);
+		if (ret) {
+			pr_err("Failed to enable LDO-15 %d\n", ret);
+			goto err_ret;
+		}
+		break;
+	case BGRSB_DISABLE_LDO11:
+		ret = regulator_disable(dev->rgltr.regldo11);
+		if (ret) {
+			pr_err("Failed to disable LDO-11 %d\n", ret);
+			goto err_ret;
+		}
+		break;
+
+	case BGRSB_DISABLE_LDO15:
+		ret = regulator_disable(dev->rgltr.regldo15);
+		if (ret) {
+			pr_err("Failed to disable LDO-15 %d\n", ret);
+			goto err_ret;
+		}
+		regulator_set_load(dev->rgltr.regldo15, 0);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+err_ret:
+	return ret;
+}
+
+static void bgrsb_bgdown_work(struct work_struct *work)
+{
+	struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv,
+								bg_down_work);
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) {
+		if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) == 0)
+			dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+		else
+			pr_err("Failed to unvote LDO-15 on BG down\n");
+	}
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) {
+		if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0)
+			dev->bgrsb_current_state = BGRSB_STATE_INIT;
+		else
+			pr_err("Failed to unvote LDO-11 on BG down\n");
+	}
+
+	pr_info("RSB current state is : %d\n", dev->bgrsb_current_state);
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_INIT) {
+		if (dev->is_calibrd)
+			dev->calibration_needed = true;
+	}
+}
+
+static void bgrsb_glink_bgdown_work(struct work_struct *work)
+{
+	int rc;
+	struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv,
+							rsb_glink_down_work);
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) {
+
+		rc = bgrsb_enable(dev, false);
+		if (rc != 0) {
+			pr_err("Failed to send disable command to BG\n");
+			return;
+		}
+
+		if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0) {
+			pr_err("Failed to un-vote LDO-15\n");
+			return;
+		}
+
+		dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+		pr_info("RSB Disabled\n");
+	}
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) {
+		if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0)
+			dev->bgrsb_current_state = BGRSB_STATE_INIT;
+		else
+			pr_err("Failed to unvote LDO-11 on BG Glink down\n");
+	}
+	if (dev->handle)
+		glink_close(dev->handle);
+	dev->handle = NULL;
+	pr_debug("BG Glink Close connection\n");
+}
+
+static int bgrsb_tx_msg(struct bgrsb_priv *dev, void  *msg, size_t len)
+{
+	int rc = 0;
+	uint8_t resp = 0;
+
+	if (!dev->chnl_state)
+		return -ENODEV;
+
+	mutex_lock(&dev->glink_mutex);
+	init_completion(&dev->tx_done);
+	init_completion(&dev->bg_resp_cmplt);
+
+	rc = glink_queue_rx_intent(dev->handle,
+					(void *)dev, BGRSB_GLINK_INTENT_SIZE);
+
+	if (rc) {
+		pr_err("Failed to queue intent\n");
+		goto err_ret;
+	}
+
+	rc = glink_tx(dev->handle, (void *)dev, msg,
+					len, GLINK_TX_REQ_INTENT);
+	if (rc) {
+		pr_err("Failed to send command\n");
+		goto err_ret;
+	}
+
+	rc = wait_for_completion_timeout(&dev->tx_done,
+						msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("Timed out waiting for Command to send\n");
+		rc = -ETIMEDOUT;
+		goto err_ret;
+	}
+
+	rc = wait_for_completion_timeout(&dev->bg_resp_cmplt,
+						msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("Timed out waiting for response\n");
+		rc = -ETIMEDOUT;
+		goto err_ret;
+	}
+
+	resp = *(uint8_t *)dev->rx_buf;
+	if (!(resp == 0x01)) {
+		pr_err("Bad RSB response\n");
+		rc = -EINVAL;
+		goto err_ret;
+	}
+	rc = 0;
+
+err_ret:
+	mutex_unlock(&dev->glink_mutex);
+	return rc;
+}
+
+static int bgrsb_enable(struct bgrsb_priv *dev, bool enable)
+{
+	int rc = 0;
+	struct bgrsb_msg req = {0};
+
+	req.cmd_id = 0x02;
+	req.data = enable ? 0x01 : 0x00;
+
+	rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE);
+	return rc;
+}
+
+static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable)
+{
+	int rc = 0;
+	struct bgrsb_msg req = {0};
+
+	req.cmd_id = 0x01;
+	req.data = enable ? 0x01 : 0x00;
+
+	rc = bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE);
+	return rc;
+}
+
+static void bgrsb_bgup_work(struct work_struct *work)
+{
+	int rc = 0;
+	struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv,
+								bg_up_work);
+
+	if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) {
+
+		rc = wait_event_timeout(dev->link_state_wait,
+				(dev->chnl_state == true),
+					msecs_to_jiffies(TIMEOUT_MS*2));
+		if (rc == 0) {
+			pr_err("Glink channel connection time out\n");
+			return;
+		}
+		rc = bgrsb_configr_rsb(dev, true);
+		if (rc != 0) {
+			pr_err("BG failed to configure RSB %d\n", rc);
+			if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0)
+				dev->bgrsb_current_state = BGRSB_STATE_INIT;
+			return;
+		}
+		dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+		pr_debug("RSB Cofigured\n");
+	}
+}
+
+static void bgrsb_glink_bgup_work(struct work_struct *work)
+{
+	int rc = 0;
+	struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv,
+							rsb_glink_up_work);
+
+	if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) {
+
+		INIT_WORK(&dev->glink_work, bgrsb_glink_open_work);
+		queue_work(dev->bgrsb_event_wq, &dev->glink_work);
+
+		rc = wait_event_timeout(dev->link_state_wait,
+					(dev->chnl_state == true),
+						msecs_to_jiffies(TIMEOUT_MS*2));
+		if (rc == 0) {
+			pr_err("Glink channel connection time out\n");
+			return;
+		}
+		rc = bgrsb_configr_rsb(dev, true);
+		if (rc != 0) {
+			pr_err("BG Glink failed to configure RSB %d\n", rc);
+			if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0)
+				dev->bgrsb_current_state = BGRSB_STATE_INIT;
+			return;
+		}
+		dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+		pr_debug("Glink RSB Cofigured\n");
+	}
+}
+
+/**
+ *ssr_bg_cb(): callback function is called
+ *by ssr framework when BG goes down, up and during ramdump
+ *collection. It handles BG shutdown and power up events.
+ */
+static int ssr_bgrsb_cb(struct notifier_block *this,
+		unsigned long opcode, void *data)
+{
+	struct bgrsb_priv *dev = container_of(bgrsb_drv,
+				struct bgrsb_priv, lhndl);
+
+	switch (opcode) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		queue_work(dev->bgrsb_wq, &dev->bg_down_work);
+		break;
+	case SUBSYS_AFTER_POWERUP:
+		if (dev->bgrsb_current_state == BGRSB_STATE_INIT)
+			queue_work(dev->bgrsb_wq, &dev->bg_up_work);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block ssr_bg_nb = {
+	.notifier_call = ssr_bgrsb_cb,
+	.priority = 0,
+};
+
+/**
+ * ssr_register checks that domain id should be in range and register
+ * SSR framework for value at domain id.
+ */
+static int bgrsb_ssr_register(struct bgrsb_priv *dev)
+{
+	struct notifier_block *nb;
+
+	if (!dev)
+		return -ENODEV;
+
+	nb = &ssr_bg_nb;
+	dev->bgwear_subsys_handle =
+			subsys_notif_register_notifier(BGRSB_BGWEAR_SUBSYS, nb);
+
+	if (!dev->bgwear_subsys_handle) {
+		dev->bgwear_subsys_handle = NULL;
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static void bgrsb_enable_rsb(struct work_struct *work)
+{
+	int rc = 0;
+	struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv,
+								rsb_up_work);
+
+	if (dev->bgrsb_current_state != BGRSB_STATE_RSB_CONFIGURED) {
+		pr_err("BG is not yet configured for RSB\n");
+		return;
+	}
+
+	if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO15) == 0) {
+
+		rc = bgrsb_enable(dev, true);
+		if (rc != 0) {
+			pr_err("Failed to send enable command to BG\n");
+			bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15);
+			dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+			return;
+		}
+	}
+	dev->bgrsb_current_state = BGRSB_STATE_RSB_ENABLED;
+	pr_debug("RSB Enabled\n");
+
+	if (dev->calibration_needed) {
+		dev->calibration_needed = false;
+		queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work);
+	}
+}
+
+static void bgrsb_disable_rsb(struct work_struct *work)
+{
+	int rc = 0;
+	struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv,
+								rsb_down_work);
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) {
+
+		rc = bgrsb_enable(dev, false);
+		if (rc != 0) {
+			pr_err("Failed to send disable command to BG\n");
+			return;
+		}
+
+		if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0)
+			return;
+
+		dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+		pr_debug("RSB Disabled\n");
+	}
+}
+
+static void bgrsb_calibration(struct work_struct *work)
+{
+	int rc = 0;
+	struct bgrsb_msg req = {0};
+	struct bgrsb_priv *dev =
+			container_of(work, struct bgrsb_priv,
+							rsb_calibration_work);
+
+	req.cmd_id = 0x03;
+	req.data = dev->calbrtion_cpi;
+
+	rc = bgrsb_tx_msg(dev, &req, 5);
+	if (rc != 0) {
+		pr_err("Failed to send resolution value to BG\n");
+		return;
+	}
+
+	req.cmd_id = 0x04;
+	req.data = dev->calbrtion_intrvl;
+
+	rc = bgrsb_tx_msg(dev, &req, 5);
+	if (rc != 0) {
+		pr_err("Failed to send interval value to BG\n");
+		return;
+	}
+	dev->is_calibrd = true;
+	pr_debug("RSB Calibbered\n");
+}
+
+static void bgrsb_buttn_configration(struct work_struct *work)
+{
+	int rc = 0;
+	struct bgrsb_msg req = {0};
+	struct bgrsb_priv *dev =
+			container_of(work, struct bgrsb_priv,
+							bttn_configr_work);
+
+	req.cmd_id = 0x05;
+	req.data = dev->bttn_configs;
+
+	rc = bgrsb_tx_msg(dev, &req, 5);
+	if (rc != 0) {
+		pr_err("Failed to send button configuration cmnd to BG\n");
+		return;
+	}
+
+	dev->bttn_configs = 0;
+	pr_debug("Button configured\n");
+}
+
+static int split_bg_work(struct bgrsb_priv *dev, char *str)
+{
+	long val;
+	int ret;
+	char *tmp;
+
+	tmp = strsep(&str, ":");
+	if (!tmp)
+		return -EINVAL;
+
+	ret = kstrtol(tmp, 10, &val);
+	if (ret < 0)
+		return ret;
+
+	switch (val) {
+	case BGRSB_POWER_DISABLE:
+		if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED)
+			return 0;
+		queue_work(dev->bgrsb_wq, &dev->rsb_down_work);
+		break;
+	case BGRSB_POWER_ENABLE:
+		if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED)
+			return 0;
+		queue_work(dev->bgrsb_wq, &dev->rsb_up_work);
+		break;
+	case BGRSB_POWER_CALIBRATION:
+		tmp = strsep(&str, ":");
+		if (!tmp)
+			return -EINVAL;
+
+		ret = kstrtol(tmp, 10, &val);
+		if (ret < 0)
+			return ret;
+
+		dev->calbrtion_intrvl = (uint32_t)val;
+
+		tmp = strsep(&str, ":");
+		if (!tmp)
+			return -EINVAL;
+
+		ret = kstrtol(tmp, 10, &val);
+		if (ret < 0)
+			return ret;
+
+		dev->calbrtion_cpi = (uint32_t)val;
+
+		queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work);
+		break;
+	case BGRSB_BTTN_CONFIGURE:
+		tmp = strsep(&str, ":");
+		if (!tmp)
+			return -EINVAL;
+
+		ret = kstrtol(tmp, 10, &val);
+		if (ret < 0)
+			return ret;
+
+		dev->bttn_configs = (uint8_t)val;
+		queue_work(dev->bgrsb_wq, &dev->bttn_configr_work);
+		break;
+	case BGRSB_GLINK_POWER_DISABLE:
+		queue_work(dev->bgrsb_wq, &dev->rsb_glink_down_work);
+		break;
+	case BGRSB_GLINK_POWER_ENABLE:
+		queue_work(dev->bgrsb_wq, &dev->rsb_glink_up_work);
+		break;
+	}
+	return 0;
+}
+
+static int store_enable(struct device *pdev, struct device_attribute *attr,
+		const char *buff, size_t count)
+{
+	int rc;
+	struct bgrsb_priv *dev = dev_get_drvdata(pdev);
+	char *arr = kstrdup(buff, GFP_KERNEL);
+
+	if (!arr)
+		goto err_ret;
+
+	rc = split_bg_work(dev, arr);
+	if (rc != 0)
+		pr_err("Not able to process request\n");
+
+err_ret:
+	return count;
+}
+
+static int show_enable(struct device *dev, struct device_attribute *attr,
+			char *buff)
+{
+	return 0;
+}
+
+static struct device_attribute dev_attr_rsb = {
+	.attr = {
+		.name = "enable",
+		.mode = 00660,
+	},
+	.show = show_enable,
+	.store = store_enable,
+};
+
+static int bgrsb_init(struct bgrsb_priv *dev)
+{
+	bgrsb_drv = &dev->lhndl;
+	dev->chnl.chnl_name = "RSB_CTRL";
+	dev->chnl.chnl_edge = "bg";
+	dev->chnl.chnl_trnsprt = "bgcom";
+	mutex_init(&dev->glink_mutex);
+	dev->link_state = GLINK_LINK_STATE_DOWN;
+
+	dev->ldo_action = BGRSB_NO_ACTION;
+
+	dev->bgrsb_event_wq =
+		create_singlethread_workqueue(dev->chnl.chnl_name);
+	if (!dev->bgrsb_event_wq) {
+		pr_err("Failed to init Glink work-queue\n");
+		goto err_ret;
+	}
+
+	dev->bgrsb_wq =
+		create_singlethread_workqueue("bg-work-queue");
+	if (!dev->bgrsb_wq) {
+		pr_err("Failed to init BG-RSB work-queue\n");
+		goto free_rsb_wq;
+	}
+
+	init_waitqueue_head(&dev->link_state_wait);
+
+	/* set default bgrsb state */
+	dev->bgrsb_current_state = BGRSB_STATE_INIT;
+
+	/* Init all works */
+	INIT_WORK(&dev->bg_up_work, bgrsb_bgup_work);
+	INIT_WORK(&dev->bg_down_work, bgrsb_bgdown_work);
+	INIT_WORK(&dev->rsb_up_work, bgrsb_enable_rsb);
+	INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb);
+	INIT_WORK(&dev->rsb_calibration_work, bgrsb_calibration);
+	INIT_WORK(&dev->bttn_configr_work, bgrsb_buttn_configration);
+	INIT_WORK(&dev->rsb_glink_down_work, bgrsb_glink_bgdown_work);
+	INIT_WORK(&dev->rsb_glink_up_work, bgrsb_glink_bgup_work);
+
+	return 0;
+
+free_rsb_wq:
+	destroy_workqueue(dev->bgrsb_event_wq);
+err_ret:
+	return -EFAULT;
+}
+
+static int bg_rsb_probe(struct platform_device *pdev)
+{
+	struct bgrsb_priv *dev;
+	struct input_dev *input;
+	int rc;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->bgrsb_current_state = BGRSB_STATE_UNKNOWN;
+	rc = bgrsb_init(dev);
+	if (rc)
+		goto err_ret_dev;
+
+	rc = bgrsb_init_link_inf(dev);
+	if (rc)
+		goto err_ret_dev;
+
+	/* Set up input device */
+	input = input_allocate_device();
+	if (!input)
+		goto err_ret_dev;
+
+	input_set_capability(input, EV_REL, REL_WHEEL);
+	input_set_capability(input, EV_KEY, KEY_VOLUMEUP);
+	input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN);
+	input->name = "bg-spi";
+
+	rc = input_register_device(input);
+	if (rc) {
+		pr_err("Input device registration failed\n");
+		goto err_ret_inp;
+	}
+	dev->input = input;
+
+	/* register device for bg-wear ssr */
+	rc = bgrsb_ssr_register(dev);
+	if (rc) {
+		pr_err("Failed to register for bg ssr\n");
+		goto err_ret_inp;
+	}
+	rc = device_create_file(&pdev->dev, &dev_attr_rsb);
+	if (rc) {
+		pr_err("Not able to create the file bg-rsb/enable\n");
+		goto err_ret_inp;
+	}
+	dev_set_drvdata(&pdev->dev, dev);
+	rc = bgrsb_init_regulators(&pdev->dev);
+	if (rc) {
+		pr_err("Failed to set regulators\n");
+		goto err_ret_inp;
+	}
+	return 0;
+
+err_ret_inp:
+	input_free_device(input);
+
+err_ret_dev:
+	devm_kfree(&pdev->dev, dev);
+	return -ENODEV;
+}
+
+static int bg_rsb_remove(struct platform_device *pdev)
+{
+	struct bgrsb_priv *dev = platform_get_drvdata(pdev);
+
+	destroy_workqueue(dev->bgrsb_event_wq);
+	destroy_workqueue(dev->bgrsb_wq);
+	input_free_device(dev->input);
+
+	return 0;
+}
+
+static int bg_rsb_resume(struct platform_device *pdev)
+{
+	int rc;
+	struct bgrsb_priv *dev = platform_get_drvdata(pdev);
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED)
+		return 0;
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_INIT) {
+		if (bgrsb_ldo_work(dev, BGRSB_ENABLE_LDO11) == 0) {
+			rc = bgrsb_configr_rsb(dev, true);
+			if (rc != 0) {
+				pr_err("BG failed to configure RSB %d\n", rc);
+				bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11);
+				return rc;
+			}
+			dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED;
+			pr_debug("RSB Cofigured\n");
+			return 0;
+		}
+		pr_err("RSB failed to resume\n");
+	}
+	return -EINVAL;
+}
+
+static int bg_rsb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct bgrsb_priv *dev = platform_get_drvdata(pdev);
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_INIT)
+		return 0;
+
+	if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) {
+		if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO15) != 0)
+			goto ret_err;
+	}
+
+	if (bgrsb_ldo_work(dev, BGRSB_DISABLE_LDO11) == 0) {
+		dev->bgrsb_current_state = BGRSB_STATE_INIT;
+		pr_debug("RSB Init\n");
+		return 0;
+	}
+
+ret_err:
+	pr_err("RSB failed to suspend\n");
+	return -EINVAL;
+}
+
+static const struct of_device_id bg_rsb_of_match[] = {
+	{ .compatible = "qcom,bg-rsb", },
+	{ }
+};
+
+static struct platform_driver bg_rsb_driver = {
+	.driver = {
+		.name = "bg-rsb",
+		.of_match_table = bg_rsb_of_match,
+	},
+	.probe		= bg_rsb_probe,
+	.remove		= bg_rsb_remove,
+	.resume		= bg_rsb_resume,
+	.suspend	= bg_rsb_suspend,
+};
+
+module_platform_driver(bg_rsb_driver);
+MODULE_DESCRIPTION("SoC BG RSB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/bgcom.h b/drivers/soc/qcom/bgcom.h
new file mode 100644
index 0000000..b389a91
--- /dev/null
+++ b/drivers/soc/qcom/bgcom.h
@@ -0,0 +1,214 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 BGCOM_H
+#define BGCOM_H
+
+#define BGCOM_REG_TZ_TO_MASTER_STATUS        0x01
+#define BGCOM_REG_TZ_TO_MASTER_DATA          0x03
+#define BGCOM_REG_SLAVE_STATUS               0x05
+#define BGCOM_REG_TIMESTAMP                  0x07
+#define BGCOM_REG_SLAVE_STATUS_AUTO_CLEAR    0x09
+#define BGCOM_REG_FIFO_FILL                  0x0B
+#define BGCOM_REG_FIFO_SIZE                  0x0D
+#define BGCOM_REG_TZ_TO_SLAVE_COMMAND        0x0E
+#define BGCOM_REG_TZ_TO_SLAVE_DATA           0x10
+#define BGCOM_REG_MASTER_STATUS              0x12
+#define BGCOM_REG_MASTER_COMMAND             0x14
+#define BGCOM_REG_MSG_WR_REG_4               0x16
+#define BGCOM_REG_TO_SLAVE_FIFO              0x40
+#define BGCOM_REG_TO_MASTER_FIFO             0x41
+#define BGCOM_REG_TO_SLAVE_AHB               0x42
+#define BGCOM_REG_TO_MASTER_AHB              0x43
+
+/* Enum to define the bgcom SPI state */
+enum bgcom_spi_state {
+	BGCOM_SPI_FREE = 0,
+	BGCOM_SPI_BUSY,
+};
+
+/* Enums to identify Blackghost events */
+enum bgcom_event_type {
+	BGCOM_EVENT_NONE	= 0,
+	BGCOM_EVENT_APPLICATION_RUNNING,
+	BGCOM_EVENT_TO_SLAVE_FIFO_READY,
+	BGCOM_EVENT_TO_MASTER_FIFO_READY,
+	BGCOM_EVENT_AHB_READY,
+	BGCOM_EVENT_TO_MASTER_FIFO_USED,
+	BGCOM_EVENT_TO_SLAVE_FIFO_FREE,
+	BGCOM_EVENT_TIMESTAMP_UPDATE,
+	BGCOM_EVENT_RESET_OCCURRED,
+
+	BGCOM_EVENT_ERROR_WRITE_FIFO_OVERRUN,
+	BGCOM_EVENT_ERROR_WRITE_FIFO_BUS_ERR,
+	BGCOM_EVENT_ERROR_WRITE_FIFO_ACCESS,
+	BGCOM_EVENT_ERROR_READ_FIFO_UNDERRUN,
+	BGCOM_EVENT_ERROR_READ_FIFO_BUS_ERR,
+	BGCOM_EVENT_ERROR_READ_FIFO_ACCESS,
+	BGCOM_EVENT_ERROR_TRUNCATED_READ,
+	BGCOM_EVENT_ERROR_TRUNCATED_WRITE,
+	BGCOM_EVENT_ERROR_AHB_ILLEGAL_ADDRESS,
+	BGCOM_EVENT_ERROR_AHB_BUS_ERR,
+	BGCOM_EVENT_ERROR_UNKNOWN,
+};
+
+/* Event specific data */
+union bgcom_event_data_type {
+	uint32_t unused;
+	bool application_running;      /* BGCOM_EVENT_APPLICATION_RUNNING */
+	bool to_slave_fifo_ready;      /* BGCOM_EVENT_TO_SLAVE_FIFO_READY */
+	bool to_master_fifo_ready;     /* BGCOM_EVENT_TO_MASTER_FIFO_READY */
+	bool ahb_ready;                /* BGCOM_EVENT_AHB_READY */
+	uint16_t to_slave_fifo_free;	/* BGCOM_EVENT_TO_SLAVE_FIFO_FREE */
+	struct fifo_event_data {
+		uint16_t to_master_fifo_used;
+		void *data;
+	} fifo_data;
+};
+
+/* Client specific data */
+struct bgcom_open_config_type {
+	/** Private data pointer for client to maintain context.
+	 * This data is passed back to client in the notification callbacks.
+	 */
+	void		*priv;
+
+	/* Notification callbacks to notify the BG events */
+	void (*bgcom_notification_cb)(void *handle, void *priv,
+			enum bgcom_event_type event,
+			union bgcom_event_data_type *event_data);
+};
+
+/**
+ * bgcom_open() - opens a channel to interact with Blackghost
+ * @open_config: pointer to the open configuration structure
+ *
+ * Open a new connection to blackghost
+ *
+ * Return a handle on success or NULL on error
+ */
+void *bgcom_open(struct bgcom_open_config_type *open_config);
+
+/**
+ * bgcom_close() - close the exsting with Blackghost
+ * @handle: pointer to the handle, provided by bgcom at
+ *	bgcom_open
+ *
+ * Open a new connection to blackghost
+ *
+ * Return 0 on success or error on invalid handle
+ */
+int bgcom_close(void **handle);
+
+/**
+ * bgcom_reg_read() - Read from the one or more contiguous registers from BG
+ * @handle: BGCOM handle associated with the channel
+ * @reg_start_addr : 8 bit start address of the registers to read from
+ * @num_regs :	Number of contiguous registers to read, starting
+ *				from reg_start_addr.
+ * @read_buf : Buffer to read from the registers.
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_reg_read(void *handle, uint8_t reg_start_addr,
+			uint32_t num_regs, void *read_buf);
+
+/**
+ * Write into the one or more contiguous registers.
+ *
+ * @param[in] handle         BGCOM handle associated with the channel.
+ * @param[in] reg_start_addr 8bit start address of the registers to write into.
+ * @param[in] num_regs       Number of contiguous registers to write, starting
+ *                           from reg_start_addr.
+ * @param[in] write_buf      Buffer to write into the registers.
+ *
+ * @return
+ * 0 if function is successful,
+ * Otherwise returns error code.
+ *
+ * @sideeffects  Causes the Blackghost SPI slave to wakeup. Depending up on
+ * the operation, it may also wakeup the complete Blackghost.
+ */
+
+/**
+ * bgcom_reg_write() - Write to the one or more contiguous registers on BG
+ * @handle: BGCOM handle associated with the channel
+ * @reg_start_addr : 8 bit start address of the registers to read from
+ * @num_regs :	Number of contiguous registers to write, starting
+ *				from reg_start_addr.
+ * @write_buf : Buffer to be written to the registers.
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_reg_write(void *handle, uint8_t reg_start_addr,
+	uint8_t num_regs, void *write_buf);
+
+/**
+ * bgcom_fifo_read() - Read data from the TO_MASTER_FIFO.
+ * @handle: BGCOM handle associated with the channel
+ * @num_words : number of words to read from FIFO
+ * @read_buf : Buffer read from FIFO.
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_fifo_read(void *handle, uint32_t num_words,
+		void *read_buf);
+
+/**
+ * bgcom_fifo_write() - Write data to the TO_SLAVE_FIFO.
+ * @handle: BGCOM handle associated with the channel
+ * @num_words : number of words to write on FIFO
+ * @write_buf : Buffer written to FIFO.
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_fifo_write(void *handle, uint32_t num_words,
+		void *write_buf);
+
+/**
+ * bgcom_ahb_read() - Read data from the AHB memory.
+ * @handle: BGCOM handle associated with the channel
+ * @ahb_start_addr : Memory start address from where to read
+ * @num_words : number of words to read from AHB
+ * @read_buf : Buffer read from FIFO.
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_ahb_read(void *handle, uint32_t ahb_start_addr,
+		uint32_t num_words, void *read_buf);
+
+/**
+ * bgcom_ahb_write() - Write data to the AHB memory.
+ * @handle: BGCOM handle associated with the channel
+ * @ahb_start_addr : Memory start address from where to start write
+ * @num_words : number of words to read from AHB
+ * @write_buf : Buffer to write in AHB.
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr,
+		uint32_t num_words, void *write_buf);
+
+/**
+ * bgcom_suspend() - Suspends the channel.
+ * @handle: BGCOM handle associated with the channel
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_suspend(void *handle);
+
+/**
+ * bgcom_resume() - Resumes the channel.
+ * @handle: BGCOM handle associated with the channel
+ * Return 0 on success or -Ve on error
+ */
+int bgcom_resume(void *handle);
+
+int bgcom_set_spi_state(enum bgcom_spi_state state);
+
+void bgcom_bgdown_handler(void);
+
+#endif /* BGCOM_H */
diff --git a/drivers/soc/qcom/bgcom_interface.c b/drivers/soc/qcom/bgcom_interface.c
new file mode 100644
index 0000000..efef26d
--- /dev/null
+++ b/drivers/soc/qcom/bgcom_interface.c
@@ -0,0 +1,614 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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(msg) "bgcom_dev:" msg
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/subsystem_notif.h>
+#include "bgcom.h"
+#include "linux/bgcom_interface.h"
+#include "bgcom_interface.h"
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define BGCOM "bg_com_dev"
+
+#define BGDAEMON_LDO09_LPM_VTG 0
+#define BGDAEMON_LDO09_NPM_VTG 10000
+
+#define BGDAEMON_LDO03_LPM_VTG 0
+#define BGDAEMON_LDO03_NPM_VTG 10000
+
+#define MPPS_DOWN_EVENT_TO_BG_TIMEOUT 100
+
+enum {
+	SSR_DOMAIN_BG,
+	SSR_DOMAIN_MODEM,
+	SSR_DOMAIN_MAX,
+};
+
+enum ldo_task {
+	ENABLE_LDO03,
+	ENABLE_LDO09,
+	DISABLE_LDO03,
+	DISABLE_LDO09
+};
+
+struct bgdaemon_regulator {
+	struct regulator *regldo03;
+	struct regulator *regldo09;
+};
+
+struct bgdaemon_priv {
+	struct bgdaemon_regulator rgltr;
+	enum ldo_task ldo_action;
+};
+
+struct bg_event {
+	enum bg_event_type e_type;
+};
+
+struct service_info {
+	const char                      name[32];
+	int                             domain_id;
+	void                            *handle;
+	struct notifier_block           *nb;
+};
+
+static char *ssr_domains[] = {
+	"bg-wear",
+	"modem",
+};
+
+static struct bgdaemon_priv *dev;
+static unsigned int bgreset_gpio;
+static  DEFINE_MUTEX(bg_char_mutex);
+static  struct cdev              bg_cdev;
+static  struct class             *bg_class;
+struct  device                   *dev_ret;
+static  dev_t                    bg_dev;
+static  int                      device_open;
+static  void                     *handle;
+static  struct   bgcom_open_config_type   config_type;
+static DECLARE_COMPLETION(bg_modem_down_wait);
+
+/**
+ * send_uevent(): send events to user space
+ * pce : ssr event handle value
+ * Return: 0 on success, standard Linux error code on error
+ *
+ * It adds pce value to event and broadcasts to user space.
+ */
+static int send_uevent(struct bg_event *pce)
+{
+	char event_string[32];
+	char *envp[2] = { event_string, NULL };
+
+	snprintf(event_string, ARRAY_SIZE(event_string),
+			"BG_EVENT=%d", pce->e_type);
+	return kobject_uevent_env(&dev_ret->kobj, KOBJ_CHANGE, envp);
+}
+
+static int bgdaemon_configure_regulators(bool state)
+{
+	int retval;
+
+	if (state == true) {
+		retval = regulator_enable(dev->rgltr.regldo03);
+		if (retval)
+			pr_err("Failed to enable LDO-03 regulator:%d\n",
+					retval);
+		retval = regulator_enable(dev->rgltr.regldo09);
+		if (retval)
+			pr_err("Failed to enable LDO-09 regulator:%d\n",
+					retval);
+	}
+	if (state == false) {
+		retval = regulator_disable(dev->rgltr.regldo03);
+		if (retval)
+			pr_err("Failed to disable LDO-03 regulator:%d\n",
+					retval);
+		retval = regulator_disable(dev->rgltr.regldo09);
+		if (retval)
+			pr_err("Failed to disable LDO-09 regulator:%d\n",
+					retval);
+	}
+	return retval;
+}
+static int bgdaemon_init_regulators(struct device *pdev)
+{
+	int rc;
+	struct regulator *reg03;
+	struct regulator *reg09;
+
+	reg03 = regulator_get(pdev, "ssr-reg1");
+	if (IS_ERR_OR_NULL(reg03)) {
+		rc = PTR_ERR(reg03);
+		pr_err("Unable to get regulator for LDO-03\n");
+		goto err_ret;
+	}
+	reg09 = regulator_get(pdev, "ssr-reg2");
+	if (IS_ERR_OR_NULL(reg09)) {
+		rc = PTR_ERR(reg09);
+		pr_err("Unable to get regulator for LDO-09\n");
+		goto err_ret;
+	}
+	dev->rgltr.regldo03 = reg03;
+	dev->rgltr.regldo09 = reg09;
+	return 0;
+err_ret:
+	return rc;
+}
+
+static int bgdaemon_ldowork(enum ldo_task do_action)
+{
+	int ret;
+
+	switch (do_action) {
+	case ENABLE_LDO03:
+		ret = regulator_set_load(dev->rgltr.regldo03,
+							BGDAEMON_LDO03_NPM_VTG);
+		if (ret < 0) {
+			pr_err("Failed to request LDO-03 voltage:%d\n",
+					ret);
+			goto err_ret;
+		}
+		break;
+	case ENABLE_LDO09:
+		ret = regulator_set_load(dev->rgltr.regldo09,
+							BGDAEMON_LDO09_NPM_VTG);
+		if (ret < 0) {
+			pr_err("Failed to request LDO-09 voltage:%d\n",
+					ret);
+			goto err_ret;
+		}
+		break;
+	case DISABLE_LDO03:
+		ret = regulator_set_load(dev->rgltr.regldo03,
+							BGDAEMON_LDO03_LPM_VTG);
+		if (ret < 0) {
+			pr_err("Failed to disable LDO-03:%d\n", ret);
+			goto err_ret;
+		}
+		break;
+	case DISABLE_LDO09:
+		ret = regulator_set_load(dev->rgltr.regldo09,
+							BGDAEMON_LDO09_LPM_VTG);
+		if (ret < 0) {
+			pr_err("Failed to disable LDO-09:%d\n", ret);
+			goto err_ret;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+err_ret:
+	return ret;
+}
+
+static int bgcom_char_open(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	mutex_lock(&bg_char_mutex);
+	if (device_open == 1) {
+		pr_err("device is already open\n");
+		mutex_unlock(&bg_char_mutex);
+		return -EBUSY;
+	}
+	device_open++;
+	handle = bgcom_open(&config_type);
+	mutex_unlock(&bg_char_mutex);
+	if (IS_ERR(handle)) {
+		device_open = 0;
+		ret = PTR_ERR(handle);
+		handle = NULL;
+		return ret;
+	}
+	return 0;
+}
+
+static int bgchar_read_cmd(struct bg_ui_data *fui_obj_msg,
+		int type)
+{
+	void              *read_buf;
+	int               ret;
+	void __user       *result   = (void *)
+			(uintptr_t)fui_obj_msg->result;
+
+	read_buf = kmalloc_array(fui_obj_msg->num_of_words, sizeof(uint32_t),
+			GFP_KERNEL);
+	if (read_buf == NULL)
+		return -ENOMEM;
+	switch (type) {
+	case REG_READ:
+		ret = bgcom_reg_read(handle, fui_obj_msg->cmd,
+				fui_obj_msg->num_of_words,
+				read_buf);
+		break;
+	case AHB_READ:
+		ret = bgcom_ahb_read(handle,
+				fui_obj_msg->bg_address,
+				fui_obj_msg->num_of_words,
+				read_buf);
+		break;
+	}
+	if (!ret && copy_to_user(result, read_buf,
+			fui_obj_msg->num_of_words * sizeof(uint32_t))) {
+		pr_err("copy to user failed\n");
+		ret = -EFAULT;
+	}
+	kfree(read_buf);
+	return ret;
+}
+
+static int bgchar_write_cmd(struct bg_ui_data *fui_obj_msg, int type)
+{
+	void              *write_buf;
+	int               ret;
+	void __user       *write     = (void *)
+			(uintptr_t)fui_obj_msg->write;
+
+	write_buf = kmalloc_array(fui_obj_msg->num_of_words, sizeof(uint32_t),
+			GFP_KERNEL);
+	if (write_buf == NULL)
+		return -ENOMEM;
+	write_buf = memdup_user(write,
+			fui_obj_msg->num_of_words * sizeof(uint32_t));
+	if (IS_ERR(write_buf)) {
+		ret = PTR_ERR(write_buf);
+		kfree(write_buf);
+		return ret;
+	}
+	switch (type) {
+	case REG_WRITE:
+		ret = bgcom_reg_write(handle, fui_obj_msg->cmd,
+				fui_obj_msg->num_of_words,
+				write_buf);
+		break;
+	case AHB_WRITE:
+		ret = bgcom_ahb_write(handle,
+				fui_obj_msg->bg_address,
+				fui_obj_msg->num_of_words,
+				write_buf);
+		break;
+	}
+	kfree(write_buf);
+	return ret;
+}
+
+int bg_soft_reset(void)
+{
+	/*pull down reset gpio */
+	gpio_direction_output(bgreset_gpio, 0);
+	msleep(50);
+	gpio_set_value(bgreset_gpio, 1);
+	return 0;
+}
+EXPORT_SYMBOL(bg_soft_reset);
+
+static int modem_down2_bg(void)
+{
+	complete(&bg_modem_down_wait);
+	return 0;
+}
+
+static long bg_com_ioctl(struct file *filp,
+		unsigned int ui_bgcom_cmd, unsigned long arg)
+{
+	int ret;
+	struct bg_ui_data ui_obj_msg;
+
+	switch (ui_bgcom_cmd) {
+	case REG_READ:
+	case AHB_READ:
+		if (copy_from_user(&ui_obj_msg, (void __user *) arg,
+				sizeof(ui_obj_msg))) {
+			pr_err("The copy from user failed\n");
+			ret = -EFAULT;
+		}
+		ret = bgchar_read_cmd(&ui_obj_msg,
+				ui_bgcom_cmd);
+		if (ret < 0)
+			pr_err("bgchar_read_cmd failed\n");
+		break;
+	case AHB_WRITE:
+	case REG_WRITE:
+		if (copy_from_user(&ui_obj_msg, (void __user *) arg,
+				sizeof(ui_obj_msg))) {
+			pr_err("The copy from user failed\n");
+			ret = -EFAULT;
+		}
+		ret = bgchar_write_cmd(&ui_obj_msg, ui_bgcom_cmd);
+		if (ret < 0)
+			pr_err("bgchar_write_cmd failed\n");
+		break;
+	case SET_SPI_FREE:
+		ret = bgcom_set_spi_state(BGCOM_SPI_FREE);
+		break;
+	case SET_SPI_BUSY:
+		ret = bgcom_set_spi_state(BGCOM_SPI_BUSY);
+		break;
+	case BG_SOFT_RESET:
+		ret = bg_soft_reset();
+		break;
+	case BG_MODEM_DOWN2_BG_DONE:
+		ret = modem_down2_bg();
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+	}
+	return ret;
+}
+
+static int bgcom_char_close(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	mutex_lock(&bg_char_mutex);
+	ret = bgcom_close(&handle);
+	device_open = 0;
+	mutex_unlock(&bg_char_mutex);
+	return ret;
+}
+
+static int bg_daemon_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	unsigned int reset_gpio;
+	int ret;
+
+	node = pdev->dev.of_node;
+
+	dev = kzalloc(sizeof(struct bgdaemon_priv), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	reset_gpio = of_get_named_gpio(node, "qcom,bg-reset-gpio", 0);
+	if (!gpio_is_valid(reset_gpio)) {
+		pr_err("gpio %d found is not valid\n", reset_gpio);
+		goto err_ret;
+	}
+
+	if (gpio_request(reset_gpio, "bg_reset_gpio")) {
+		pr_err("gpio %d request failed\n", reset_gpio);
+		goto err_ret;
+	}
+
+	if (gpio_direction_output(reset_gpio, 1)) {
+		pr_err("gpio %d direction not set\n", reset_gpio);
+		goto err_ret;
+	}
+
+	pr_info("bg-soft-reset gpio successfully requested\n");
+	bgreset_gpio = reset_gpio;
+
+	ret = bgdaemon_init_regulators(&pdev->dev);
+	if (ret != 0) {
+		pr_err("Failed to init regulators:%d\n", ret);
+		goto err_device;
+	}
+	ret = bgdaemon_configure_regulators(true);
+	if (ret) {
+		pr_err("Failed to confifigure regulators:%d\n", ret);
+		bgdaemon_configure_regulators(false);
+		goto err_ret;
+	}
+
+err_device:
+	return -ENODEV;
+err_ret:
+	return 0;
+}
+
+static const struct of_device_id bg_daemon_of_match[] = {
+	{ .compatible = "qcom,bg-daemon", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bg_daemon_of_match);
+
+static struct platform_driver bg_daemon_driver = {
+	.probe  = bg_daemon_probe,
+	.driver = {
+		.name = "bg-daemon",
+		.of_match_table = bg_daemon_of_match,
+	},
+};
+
+static const struct file_operations fops = {
+	.owner          = THIS_MODULE,
+	.open           = bgcom_char_open,
+	.release        = bgcom_char_close,
+	.unlocked_ioctl = bg_com_ioctl,
+};
+
+static int __init init_bg_com_dev(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&bg_dev, 0, 1, BGCOM);
+	if (ret  < 0) {
+		pr_err("failed with error %d\n", ret);
+		return ret;
+	}
+	cdev_init(&bg_cdev, &fops);
+	ret = cdev_add(&bg_cdev, bg_dev, 1);
+	if (ret < 0) {
+		unregister_chrdev_region(bg_dev, 1);
+		pr_err("device registration failed\n");
+		return ret;
+	}
+	bg_class = class_create(THIS_MODULE, BGCOM);
+	if (IS_ERR_OR_NULL(bg_class)) {
+		cdev_del(&bg_cdev);
+		unregister_chrdev_region(bg_dev, 1);
+		pr_err("class creation failed\n");
+		return PTR_ERR(bg_class);
+	}
+
+	dev_ret = device_create(bg_class, NULL, bg_dev, NULL, BGCOM);
+	if (IS_ERR_OR_NULL(dev_ret)) {
+		class_destroy(bg_class);
+		cdev_del(&bg_cdev);
+		unregister_chrdev_region(bg_dev, 1);
+		pr_err("device create failed\n");
+		return PTR_ERR(dev_ret);
+	}
+
+	if (platform_driver_register(&bg_daemon_driver))
+		pr_err("%s: failed to register bg-daemon register\n", __func__);
+
+	return 0;
+}
+
+static void __exit exit_bg_com_dev(void)
+{
+	device_destroy(bg_class, bg_dev);
+	class_destroy(bg_class);
+	cdev_del(&bg_cdev);
+	unregister_chrdev_region(bg_dev, 1);
+	bgdaemon_configure_regulators(false);
+	platform_driver_unregister(&bg_daemon_driver);
+}
+
+/**
+ *ssr_bg_cb(): callback function is called
+ *by ssr framework when BG goes down, up and during ramdump
+ *collection. It handles BG shutdown and power up events.
+ */
+static int ssr_bg_cb(struct notifier_block *this,
+		unsigned long opcode, void *data)
+{
+	struct bg_event bge;
+
+	switch (opcode) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		bge.e_type = BG_BEFORE_POWER_DOWN;
+		bgdaemon_ldowork(ENABLE_LDO03);
+		bgdaemon_ldowork(ENABLE_LDO09);
+		bgcom_bgdown_handler();
+		bgcom_set_spi_state(BGCOM_SPI_BUSY);
+		send_uevent(&bge);
+		break;
+	case SUBSYS_AFTER_POWERUP:
+		bge.e_type = BG_AFTER_POWER_UP;
+		bgdaemon_ldowork(DISABLE_LDO03);
+		bgdaemon_ldowork(DISABLE_LDO09);
+		bgcom_set_spi_state(BGCOM_SPI_FREE);
+		send_uevent(&bge);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+/**
+ *ssr_modem_cb(): callback function is called
+ *by ssr framework when modem goes down, up and during ramdump
+ *collection. It handles modem shutdown and power up events.
+ */
+static int ssr_modem_cb(struct notifier_block *this,
+		unsigned long opcode, void *data)
+{
+	struct bg_event modeme;
+	int ret;
+
+	switch (opcode) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		modeme.e_type = MODEM_BEFORE_POWER_DOWN;
+		reinit_completion(&bg_modem_down_wait);
+		send_uevent(&modeme);
+		ret = wait_for_completion_timeout(&bg_modem_down_wait,
+			msecs_to_jiffies(MPPS_DOWN_EVENT_TO_BG_TIMEOUT));
+		if (!ret)
+			pr_err("Time out on modem down event\n");
+		break;
+	case SUBSYS_AFTER_POWERUP:
+		modeme.e_type = MODEM_AFTER_POWER_UP;
+		send_uevent(&modeme);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block ssr_modem_nb = {
+	.notifier_call = ssr_modem_cb,
+	.priority = 0,
+};
+
+static struct notifier_block ssr_bg_nb = {
+	.notifier_call = ssr_bg_cb,
+	.priority = 0,
+};
+
+static struct service_info service_data[2] = {
+	{
+		.name = "SSR_BG",
+		.domain_id = SSR_DOMAIN_BG,
+		.nb = &ssr_bg_nb,
+		.handle = NULL,
+	},
+	{
+		.name = "SSR_MODEM",
+		.domain_id = SSR_DOMAIN_MODEM,
+		.nb = &ssr_modem_nb,
+		.handle = NULL,
+	},
+};
+
+/**
+ * ssr_register checks that domain id should be in range and register
+ * SSR framework for value at domain id.
+ */
+static int __init ssr_register(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(service_data); i++) {
+		if ((service_data[i].domain_id < 0) ||
+				(service_data[i].domain_id >= SSR_DOMAIN_MAX)) {
+			pr_err("Invalid service ID = %d\n",
+					service_data[i].domain_id);
+		} else {
+			service_data[i].handle =
+					subsys_notif_register_notifier(
+					ssr_domains[service_data[i].domain_id],
+					service_data[i].nb);
+			if (IS_ERR_OR_NULL(service_data[i].handle)) {
+				pr_err("subsys register failed for id = %d",
+						service_data[i].domain_id);
+				service_data[i].handle = NULL;
+			}
+		}
+	}
+	return 0;
+}
+
+module_init(init_bg_com_dev);
+late_initcall(ssr_register);
+module_exit(exit_bg_com_dev);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/bgcom_interface.h b/drivers/soc/qcom/bgcom_interface.h
new file mode 100644
index 0000000..500ca6d
--- /dev/null
+++ b/drivers/soc/qcom/bgcom_interface.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 BGCOM_INTERFACE_H
+#define BGCOM_INTERFACE_H
+
+/**
+ * bg_soft_reset() - soft reset Blackghost
+ * Return 0 on success or -Ve on error
+ */
+int bg_soft_reset(void);
+
+#endif /* BGCOM_INTERFACE_H */
diff --git a/drivers/soc/qcom/bgcom_spi.c b/drivers/soc/qcom/bgcom_spi.c
new file mode 100644
index 0000000..b8e3b84
--- /dev/null
+++ b/drivers/soc/qcom/bgcom_spi.c
@@ -0,0 +1,1010 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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(msg) "bgcom: %s: " msg, __func__
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/ratelimit.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/kthread.h>
+#include "bgcom.h"
+#include "bgrsb.h"
+#include "bgcom_interface.h"
+
+#define BG_SPI_WORD_SIZE (0x04)
+#define BG_SPI_READ_LEN (0x04)
+#define BG_SPI_WRITE_CMND_LEN (0x01)
+#define BG_SPI_FIFO_READ_CMD (0x41)
+#define BG_SPI_FIFO_WRITE_CMD (0x40)
+#define BG_SPI_AHB_READ_CMD (0x43)
+#define BG_SPI_AHB_WRITE_CMD (0x42)
+#define BG_SPI_AHB_CMD_LEN (0x05)
+#define BG_SPI_AHB_READ_CMD_LEN (0x08)
+#define BG_STATUS_REG (0x05)
+#define BG_CMND_REG (0x14)
+
+#define BG_SPI_MAX_WORDS (0x3FFFFFFD)
+#define BG_SPI_MAX_REGS (0x0A)
+#define SLEEP_IN_STATE_CHNG 2000
+#define HED_EVENT_ID_LEN (0x02)
+#define HED_EVENT_SIZE_LEN (0x02)
+#define HED_EVENT_DATA_STRT_LEN (0x05)
+
+#define MAX_RETRY 200
+
+enum bgcom_state {
+	/*BGCOM Staus ready*/
+	BGCOM_PROB_SUCCESS = 0,
+	BGCOM_PROB_WAIT = 1,
+	BGCOM_STATE_SUSPEND = 2,
+	BGCOM_STATE_ACTIVE = 3
+};
+
+enum bgcom_req_type {
+	/*BGCOM local requests*/
+	BGCOM_READ_REG = 0,
+	BGCOM_READ_FIFO = 1,
+	BGCOM_READ_AHB = 2,
+};
+
+struct bg_spi_priv {
+	struct spi_device *spi;
+	/* Transaction related */
+	struct mutex xfer_mutex;
+	void *lhandle;
+	/* Message for single transfer */
+	struct spi_message msg1;
+	struct spi_transfer xfer1;
+	int irq_lock;
+
+	enum bgcom_state bg_state;
+};
+
+struct cb_data {
+	void *priv;
+	void *handle;
+	void (*bgcom_notification_cb)(void *handle, void *priv,
+		enum bgcom_event_type event,
+		union bgcom_event_data_type *event_data);
+	struct list_head list;
+};
+
+struct bg_context {
+	struct bg_spi_priv *bg_spi;
+	enum bgcom_state state;
+	struct cb_data *cb;
+};
+
+struct event_list {
+	struct event *evnt;
+	struct list_head list;
+};
+static void *bg_com_drv;
+static uint32_t g_slav_status_reg;
+
+/* BGCOM client callbacks set-up */
+static void send_input_events(struct work_struct *work);
+static struct list_head cb_head = LIST_HEAD_INIT(cb_head);
+static struct list_head pr_lst_hd = LIST_HEAD_INIT(pr_lst_hd);
+static enum bgcom_spi_state spi_state;
+
+
+static struct workqueue_struct *wq;
+static DECLARE_WORK(input_work, send_input_events);
+
+static struct mutex bg_resume_mutex;
+
+static void augmnt_fifo(uint8_t *data, int pos)
+{
+	data[pos] = '\0';
+}
+
+static void send_input_events(struct work_struct *work)
+{
+	struct list_head *temp;
+	struct list_head *pos;
+	struct event_list *node;
+	struct event *evnt;
+
+	if (list_empty(&pr_lst_hd))
+		return;
+
+	list_for_each_safe(pos, temp, &pr_lst_hd) {
+		node = list_entry(pos, struct event_list, list);
+		evnt = node->evnt;
+		bgrsb_send_input(evnt);
+		kfree(evnt);
+		list_del(&node->list);
+		kfree(node);
+	}
+}
+
+int bgcom_set_spi_state(enum bgcom_spi_state state)
+{
+	struct bg_spi_priv *bg_spi = container_of(bg_com_drv,
+						struct bg_spi_priv, lhandle);
+	if (state < 0 || state > 1)
+		return -EINVAL;
+
+	if (state == spi_state)
+		return 0;
+
+	mutex_lock(&bg_spi->xfer_mutex);
+	spi_state = state;
+	if (spi_state == BGCOM_SPI_BUSY)
+		msleep(SLEEP_IN_STATE_CHNG);
+	mutex_unlock(&bg_spi->xfer_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(bgcom_set_spi_state);
+
+static inline
+void add_to_irq_list(struct  cb_data *data)
+{
+	list_add_tail(&data->list, &cb_head);
+}
+
+static bool is_bgcom_ready(void)
+{
+	return (bg_com_drv != NULL ? true : false);
+}
+
+static void bg_spi_reinit_xfer(struct spi_transfer *xfer)
+{
+	xfer->tx_buf = NULL;
+	xfer->rx_buf = NULL;
+	xfer->delay_usecs = 0;
+	xfer->len = 0;
+}
+
+static int read_bg_locl(enum bgcom_req_type req_type,
+	uint32_t no_of_words, void *buf)
+{
+
+	struct bg_context clnt_handle;
+	struct bg_spi_priv *spi =
+			container_of(bg_com_drv, struct bg_spi_priv, lhandle);
+	int ret = 0;
+
+	if (!buf)
+		return -EINVAL;
+
+	clnt_handle.bg_spi = spi;
+
+	switch (req_type) {
+	case BGCOM_READ_REG:
+		ret = bgcom_reg_read(&clnt_handle,
+			BG_STATUS_REG, no_of_words, buf);
+		break;
+	case BGCOM_READ_FIFO:
+		ret = bgcom_fifo_read(&clnt_handle, no_of_words, buf);
+		break;
+	case BGCOM_READ_AHB:
+		break;
+	}
+	return ret;
+}
+
+static int bgcom_transfer(void *handle, uint8_t *tx_buf,
+	uint8_t *rx_buf, uint32_t txn_len)
+{
+	struct spi_transfer *tx_xfer;
+	struct bg_spi_priv *bg_spi;
+	struct bg_context *cntx;
+	struct spi_device *spi;
+	int ret;
+
+	if (!handle || !tx_buf)
+		return -EINVAL;
+
+	cntx = (struct bg_context *)handle;
+
+	if (cntx->state == BGCOM_PROB_WAIT) {
+		if (!is_bgcom_ready())
+			return -ENODEV;
+		cntx->bg_spi = container_of(bg_com_drv,
+						struct bg_spi_priv, lhandle);
+		cntx->state = BGCOM_PROB_SUCCESS;
+	}
+	bg_spi = cntx->bg_spi;
+
+	if (!bg_spi)
+		return -ENODEV;
+
+	tx_xfer = &bg_spi->xfer1;
+	spi = bg_spi->spi;
+
+	mutex_lock(&bg_spi->xfer_mutex);
+	bg_spi_reinit_xfer(tx_xfer);
+	tx_xfer->tx_buf = tx_buf;
+	if (rx_buf)
+		tx_xfer->rx_buf = rx_buf;
+
+	tx_xfer->len = txn_len;
+	ret = spi_sync(spi, &bg_spi->msg1);
+	mutex_unlock(&bg_spi->xfer_mutex);
+
+	if (ret)
+		pr_err("SPI transaction failed: %d\n", ret);
+	return ret;
+}
+
+/* BG-COM Interrupt handling */
+static inline
+void send_event(enum bgcom_event_type event,
+	void *data)
+{
+	struct list_head *pos;
+	struct cb_data *cb;
+
+	/* send interrupt notification for each
+	 * registered call-back
+	 */
+	list_for_each(pos, &cb_head) {
+		cb = list_entry(pos, struct cb_data, list);
+		cb->bgcom_notification_cb(cb->handle,
+		cb->priv,  event, data);
+	}
+}
+
+void bgcom_bgdown_handler(void)
+{
+	send_event(BGCOM_EVENT_RESET_OCCURRED, NULL);
+	g_slav_status_reg = 0;
+}
+EXPORT_SYMBOL(bgcom_bgdown_handler);
+
+static void parse_fifo(uint8_t *data, union bgcom_event_data_type *event_data)
+{
+	uint16_t p_len;
+	uint8_t sub_id;
+	uint32_t evnt_tm;
+	uint16_t event_id;
+	void *evnt_data;
+	struct event *evnt;
+	struct event_list *data_list;
+
+	while (*data != '\0') {
+
+		event_id = *((uint16_t *) data);
+		data = data + HED_EVENT_ID_LEN;
+		p_len = *((uint16_t *) data);
+		data = data + HED_EVENT_SIZE_LEN;
+
+		if (event_id == 0xFFFE) {
+
+			sub_id = *data;
+			evnt_tm = *((uint32_t *)(data+1));
+
+			evnt = kmalloc(sizeof(*evnt), GFP_KERNEL);
+			evnt->sub_id = sub_id;
+			evnt->evnt_tm = evnt_tm;
+			evnt->evnt_data =
+				*(int16_t *)(data + HED_EVENT_DATA_STRT_LEN);
+
+			data_list = kmalloc(sizeof(*data_list), GFP_KERNEL);
+			data_list->evnt = evnt;
+			list_add_tail(&data_list->list, &pr_lst_hd);
+
+		} else if (event_id == 0x0001) {
+			evnt_data = kmalloc(p_len, GFP_KERNEL);
+			if (evnt_data != NULL) {
+				memcpy(evnt_data, data, p_len);
+				event_data->fifo_data.to_master_fifo_used =
+						p_len/BG_SPI_WORD_SIZE;
+				event_data->fifo_data.data = evnt_data;
+				send_event(BGCOM_EVENT_TO_MASTER_FIFO_USED,
+						event_data);
+			}
+		}
+		data = data + p_len;
+	}
+	if (!list_empty(&pr_lst_hd))
+		queue_work(wq, &input_work);
+}
+
+static void send_back_notification(uint32_t slav_status_reg,
+	uint32_t slav_status_auto_clear_reg,
+	uint32_t fifo_fill_reg, uint32_t fifo_size_reg)
+{
+	uint16_t master_fifo_used;
+	uint16_t slave_fifo_free;
+	uint32_t *ptr;
+	int ret;
+	union bgcom_event_data_type event_data = { .fifo_data = {0} };
+
+	master_fifo_used = (uint16_t)fifo_fill_reg;
+	slave_fifo_free = (uint16_t)(fifo_fill_reg >> 16);
+
+	if (slav_status_auto_clear_reg & BIT(31))
+		send_event(BGCOM_EVENT_RESET_OCCURRED, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(30))
+		send_event(BGCOM_EVENT_ERROR_WRITE_FIFO_OVERRUN, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(29))
+		send_event(BGCOM_EVENT_ERROR_WRITE_FIFO_BUS_ERR, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(28))
+		send_event(BGCOM_EVENT_ERROR_WRITE_FIFO_ACCESS, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(27))
+		send_event(BGCOM_EVENT_ERROR_READ_FIFO_UNDERRUN, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(26))
+		send_event(BGCOM_EVENT_ERROR_READ_FIFO_BUS_ERR, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(25))
+		send_event(BGCOM_EVENT_ERROR_READ_FIFO_ACCESS, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(24))
+		send_event(BGCOM_EVENT_ERROR_TRUNCATED_READ, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(23))
+		send_event(BGCOM_EVENT_ERROR_TRUNCATED_WRITE, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(22))
+		send_event(BGCOM_EVENT_ERROR_AHB_ILLEGAL_ADDRESS, NULL);
+
+	if (slav_status_auto_clear_reg & BIT(21))
+		send_event(BGCOM_EVENT_ERROR_AHB_BUS_ERR, NULL);
+
+	/* check if BG status is changed */
+	if (g_slav_status_reg ^ slav_status_reg) {
+		if (slav_status_reg & BIT(30)) {
+			event_data.application_running = true;
+			send_event(BGCOM_EVENT_APPLICATION_RUNNING,
+				&event_data);
+		}
+
+		if (slav_status_reg & BIT(29)) {
+			event_data.to_slave_fifo_ready = true;
+			send_event(BGCOM_EVENT_TO_SLAVE_FIFO_READY,
+				&event_data);
+		}
+
+		if (slav_status_reg & BIT(28)) {
+			event_data.to_master_fifo_ready = true;
+			send_event(BGCOM_EVENT_TO_MASTER_FIFO_READY,
+				&event_data);
+		}
+
+		if (slav_status_reg & BIT(27)) {
+			event_data.ahb_ready = true;
+			send_event(BGCOM_EVENT_AHB_READY,
+				&event_data);
+		}
+	}
+
+	if (master_fifo_used > 0) {
+		ptr = kzalloc(master_fifo_used*BG_SPI_WORD_SIZE + 1,
+			GFP_KERNEL | GFP_ATOMIC);
+		if (ptr != NULL) {
+			ret = read_bg_locl(BGCOM_READ_FIFO,
+				master_fifo_used,  ptr);
+			if (!ret) {
+				augmnt_fifo((uint8_t *)ptr,
+					master_fifo_used*BG_SPI_WORD_SIZE);
+				parse_fifo((uint8_t *)ptr, &event_data);
+			}
+			kfree(ptr);
+		}
+	}
+
+	event_data.to_slave_fifo_free = slave_fifo_free;
+	send_event(BGCOM_EVENT_TO_SLAVE_FIFO_FREE, &event_data);
+}
+
+static void bg_irq_tasklet_hndlr_l(void)
+{
+	uint32_t slave_status_reg;
+	uint32_t glink_isr_reg;
+	uint32_t slav_status_auto_clear_reg;
+	uint32_t fifo_fill_reg;
+	uint32_t fifo_size_reg;
+	int ret =  0;
+	uint32_t irq_buf[5] = {0};
+
+	ret = read_bg_locl(BGCOM_READ_REG, 5, &irq_buf[0]);
+	if (ret)
+		return;
+
+	/* save current state */
+	slave_status_reg = irq_buf[0];
+	glink_isr_reg = irq_buf[1];
+	slav_status_auto_clear_reg = irq_buf[2];
+	fifo_fill_reg = irq_buf[3];
+	fifo_size_reg = irq_buf[4];
+
+	send_back_notification(slave_status_reg,
+		slav_status_auto_clear_reg, fifo_fill_reg, fifo_size_reg);
+
+	g_slav_status_reg = slave_status_reg;
+}
+
+int bgcom_ahb_read(void *handle, uint32_t ahb_start_addr,
+	uint32_t num_words, void *read_buf)
+{
+	uint32_t txn_len;
+	uint8_t *tx_buf;
+	uint8_t *rx_buf;
+	uint32_t size;
+	int ret;
+	uint8_t cmnd = 0;
+	uint32_t ahb_addr = 0;
+
+	if (!handle || !read_buf || num_words == 0
+		|| num_words > BG_SPI_MAX_WORDS) {
+		pr_err("Invalid param\n");
+		return -EINVAL;
+	}
+	if (!is_bgcom_ready())
+		return -ENODEV;
+
+	if (spi_state == BGCOM_SPI_BUSY) {
+		pr_err("Device busy\n");
+		return -EBUSY;
+	}
+
+	if (bgcom_resume(handle)) {
+		pr_err("Failed to resume\n");
+		return -EBUSY;
+	}
+
+	size = num_words*BG_SPI_WORD_SIZE;
+	txn_len = BG_SPI_AHB_READ_CMD_LEN + size;
+
+	tx_buf = kzalloc(txn_len, GFP_KERNEL);
+
+	if (!tx_buf)
+		return -ENOMEM;
+
+	rx_buf = kzalloc(txn_len, GFP_KERNEL);
+
+	if (!rx_buf) {
+		kfree(tx_buf);
+		return -ENOMEM;
+	}
+
+	cmnd |= BG_SPI_AHB_READ_CMD;
+	ahb_addr |= ahb_start_addr;
+
+	memcpy(tx_buf, &cmnd, sizeof(cmnd));
+	memcpy(tx_buf+sizeof(cmnd), &ahb_addr, sizeof(ahb_addr));
+
+	ret = bgcom_transfer(handle, tx_buf, rx_buf, txn_len);
+
+	if (!ret)
+		memcpy(read_buf, rx_buf+BG_SPI_AHB_READ_CMD_LEN, size);
+
+	kfree(tx_buf);
+	kfree(rx_buf);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_ahb_read);
+
+int bgcom_ahb_write(void *handle, uint32_t ahb_start_addr,
+	uint32_t num_words, void *write_buf)
+{
+	uint32_t txn_len;
+	uint8_t *tx_buf;
+	uint32_t size;
+	int ret;
+	uint8_t cmnd = 0;
+	uint32_t ahb_addr = 0;
+
+	if (!handle || !write_buf || num_words == 0
+		|| num_words > BG_SPI_MAX_WORDS) {
+		pr_err("Invalid param\n");
+		return -EINVAL;
+	}
+
+	if (!is_bgcom_ready())
+		return -ENODEV;
+
+	if (spi_state == BGCOM_SPI_BUSY) {
+		pr_err("Device busy\n");
+		return -EBUSY;
+	}
+
+	if (bgcom_resume(handle)) {
+		pr_err("Failed to resume\n");
+		return -EBUSY;
+	}
+
+	size = num_words*BG_SPI_WORD_SIZE;
+	txn_len = BG_SPI_AHB_CMD_LEN + size;
+
+	tx_buf = kzalloc(txn_len, GFP_KERNEL);
+
+	if (!tx_buf)
+		return -ENOMEM;
+
+	cmnd |= BG_SPI_AHB_WRITE_CMD;
+	ahb_addr |= ahb_start_addr;
+
+	memcpy(tx_buf, &cmnd, sizeof(cmnd));
+	memcpy(tx_buf+sizeof(cmnd), &ahb_addr, sizeof(ahb_addr));
+	memcpy(tx_buf+BG_SPI_AHB_CMD_LEN, write_buf, size);
+
+	ret = bgcom_transfer(handle, tx_buf, NULL, txn_len);
+	kfree(tx_buf);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_ahb_write);
+
+int bgcom_fifo_write(void *handle, uint32_t num_words,
+	void  *write_buf)
+{
+	uint32_t txn_len;
+	uint8_t *tx_buf;
+	uint32_t size;
+	int ret;
+	uint8_t cmnd = 0;
+
+	if (!handle || !write_buf || num_words == 0
+		|| num_words > BG_SPI_MAX_WORDS) {
+		pr_err("Invalid param\n");
+		return -EINVAL;
+	}
+
+	if (!is_bgcom_ready())
+		return -ENODEV;
+
+	if (spi_state == BGCOM_SPI_BUSY) {
+		pr_err("Device busy\n");
+		return -EBUSY;
+	}
+
+	if (bgcom_resume(handle)) {
+		pr_err("Failed to resume\n");
+		return -EBUSY;
+	}
+
+	size = num_words*BG_SPI_WORD_SIZE;
+	txn_len = BG_SPI_WRITE_CMND_LEN + size;
+
+	tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
+
+	if (!tx_buf)
+		return -ENOMEM;
+
+	cmnd |= BG_SPI_FIFO_WRITE_CMD;
+	memcpy(tx_buf, &cmnd, sizeof(cmnd));
+	memcpy(tx_buf+sizeof(cmnd), write_buf, size);
+
+	ret = bgcom_transfer(handle, tx_buf, NULL, txn_len);
+	kfree(tx_buf);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_fifo_write);
+
+int bgcom_fifo_read(void *handle, uint32_t num_words,
+	void *read_buf)
+{
+	uint32_t txn_len;
+	uint8_t *tx_buf;
+	uint8_t *rx_buf;
+	uint32_t size;
+	uint8_t cmnd = 0;
+	int ret =  0;
+
+	if (!handle || !read_buf || num_words == 0
+		|| num_words > BG_SPI_MAX_WORDS) {
+		pr_err("Invalid param\n");
+		return -EINVAL;
+	}
+
+	if (!is_bgcom_ready())
+		return -ENODEV;
+
+	if (spi_state == BGCOM_SPI_BUSY) {
+		pr_err("Device busy\n");
+		return -EBUSY;
+	}
+
+	size = num_words*BG_SPI_WORD_SIZE;
+	txn_len = BG_SPI_READ_LEN + size;
+	tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
+
+	if (!tx_buf)
+		return -ENOMEM;
+
+	rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
+
+	if (!rx_buf) {
+		kfree(tx_buf);
+		return -ENOMEM;
+	}
+
+	cmnd |= BG_SPI_FIFO_READ_CMD;
+	memcpy(tx_buf, &cmnd, sizeof(cmnd));
+
+	ret = bgcom_transfer(handle, tx_buf, rx_buf, txn_len);
+
+	if (!ret)
+		memcpy(read_buf, rx_buf+BG_SPI_READ_LEN, size);
+	kfree(tx_buf);
+	kfree(rx_buf);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_fifo_read);
+
+int bgcom_reg_write(void *handle, uint8_t reg_start_addr,
+	uint8_t num_regs, void *write_buf)
+{
+	uint32_t txn_len;
+	uint8_t *tx_buf;
+	uint32_t size;
+	uint8_t cmnd = 0;
+	int ret =  0;
+
+	if (!handle || !write_buf || num_regs == 0
+		|| num_regs > BG_SPI_MAX_REGS) {
+		pr_err("Invalid param\n");
+		return -EINVAL;
+	}
+
+	if (!is_bgcom_ready())
+		return -ENODEV;
+
+	if (spi_state == BGCOM_SPI_BUSY) {
+		pr_err("Device busy\n");
+		return -EBUSY;
+	}
+
+	size = num_regs*BG_SPI_WORD_SIZE;
+	txn_len = BG_SPI_WRITE_CMND_LEN + size;
+
+	tx_buf = kzalloc(txn_len, GFP_KERNEL);
+
+	if (!tx_buf)
+		return -ENOMEM;
+
+	cmnd |= reg_start_addr;
+	memcpy(tx_buf, &cmnd, sizeof(cmnd));
+	memcpy(tx_buf+sizeof(cmnd), write_buf, size);
+
+	ret = bgcom_transfer(handle, tx_buf, NULL, txn_len);
+	kfree(tx_buf);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_reg_write);
+
+int bgcom_reg_read(void *handle, uint8_t reg_start_addr,
+	uint32_t num_regs, void *read_buf)
+{
+	uint32_t txn_len;
+	uint8_t *tx_buf;
+	uint8_t *rx_buf;
+	uint32_t size;
+	int ret;
+	uint8_t cmnd = 0;
+
+	if (!handle || !read_buf || num_regs == 0
+		|| num_regs > BG_SPI_MAX_REGS) {
+		pr_err("Invalid param\n");
+		return -EINVAL;
+	}
+
+	if (!is_bgcom_ready())
+		return -ENODEV;
+
+	if (spi_state == BGCOM_SPI_BUSY) {
+		pr_err("Device busy\n");
+		return -EBUSY;
+	}
+
+	size = num_regs*BG_SPI_WORD_SIZE;
+	txn_len = BG_SPI_READ_LEN + size;
+
+	tx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
+
+	if (!tx_buf)
+		return -ENOMEM;
+
+	rx_buf = kzalloc(txn_len, GFP_KERNEL | GFP_ATOMIC);
+
+	if (!rx_buf) {
+		kfree(tx_buf);
+		return -ENOMEM;
+	}
+
+	cmnd |= reg_start_addr;
+	memcpy(tx_buf, &cmnd, sizeof(cmnd));
+
+	ret = bgcom_transfer(handle, tx_buf, rx_buf, txn_len);
+
+	if (!ret)
+		memcpy(read_buf, rx_buf+BG_SPI_READ_LEN, size);
+	kfree(tx_buf);
+	kfree(rx_buf);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_reg_read);
+
+static int is_bg_resume(void *handle)
+{
+	uint32_t txn_len;
+	int ret;
+	uint8_t tx_buf[8] = {0};
+	uint8_t rx_buf[8] = {0};
+	uint32_t cmnd_reg = 0;
+
+	txn_len = 0x08;
+	tx_buf[0] = 0x05;
+	ret = bgcom_transfer(handle, tx_buf, rx_buf, txn_len);
+	if (!ret)
+		memcpy(&cmnd_reg, rx_buf+BG_SPI_READ_LEN, 0x04);
+	return cmnd_reg & BIT(31);
+}
+
+int bgcom_resume(void *handle)
+{
+	struct bg_spi_priv *bg_spi;
+	struct bg_context *cntx;
+	int retry = 0;
+
+	if (handle == NULL)
+		return -EINVAL;
+
+	cntx = (struct bg_context *)handle;
+	bg_spi = cntx->bg_spi;
+
+	mutex_lock(&bg_resume_mutex);
+	if (bg_spi->bg_state == BGCOM_STATE_ACTIVE)
+		goto unlock;
+	do {
+		if (is_bg_resume(handle)) {
+			bg_spi->bg_state = BGCOM_STATE_ACTIVE;
+			break;
+		}
+		udelay(10);
+		++retry;
+	} while (retry < MAX_RETRY);
+
+unlock:
+	mutex_unlock(&bg_resume_mutex);
+	if (retry == MAX_RETRY) {
+		/* BG failed to resume. Trigger BG soft reset. */
+		pr_err("BG failed to resume\n");
+		bg_soft_reset();
+		return -ETIMEDOUT;
+	}
+	pr_info("BG retries for wake up : %d\n", retry);
+	return 0;
+}
+EXPORT_SYMBOL(bgcom_resume);
+
+int bgcom_suspend(void *handle)
+{
+	struct bg_spi_priv *bg_spi;
+	struct bg_context *cntx;
+	uint32_t cmnd_reg = 0;
+	int ret = 0;
+
+	if (handle == NULL)
+		return -EINVAL;
+
+	cntx = (struct bg_context *)handle;
+	bg_spi = cntx->bg_spi;
+	mutex_lock(&bg_resume_mutex);
+	if (bg_spi->bg_state == BGCOM_STATE_SUSPEND)
+		goto unlock;
+
+	cmnd_reg |= BIT(31);
+	ret = bgcom_reg_write(handle, BG_CMND_REG, 1, &cmnd_reg);
+	if (ret == 0)
+		bg_spi->bg_state = BGCOM_STATE_SUSPEND;
+
+unlock:
+	mutex_unlock(&bg_resume_mutex);
+	pr_info("suspended with : %d\n", ret);
+	return ret;
+}
+EXPORT_SYMBOL(bgcom_suspend);
+
+void *bgcom_open(struct bgcom_open_config_type *open_config)
+{
+	struct bg_spi_priv *spi;
+	struct cb_data *irq_notification;
+	struct bg_context  *clnt_handle =
+			kzalloc(sizeof(*clnt_handle), GFP_KERNEL);
+
+	if (!clnt_handle)
+		return NULL;
+
+	/* Client handle Set-up */
+	if (!is_bgcom_ready()) {
+		clnt_handle->bg_spi = NULL;
+		clnt_handle->state = BGCOM_PROB_WAIT;
+	} else {
+		spi = container_of(bg_com_drv, struct bg_spi_priv, lhandle);
+		clnt_handle->bg_spi = spi;
+		clnt_handle->state = BGCOM_PROB_SUCCESS;
+	}
+	clnt_handle->cb = NULL;
+	/* Interrupt callback Set-up */
+	if (open_config && open_config->bgcom_notification_cb) {
+		irq_notification = kzalloc(sizeof(*irq_notification),
+			GFP_KERNEL);
+		if (!irq_notification)
+			goto error_ret;
+
+		/* set irq node */
+		irq_notification->handle = clnt_handle;
+		irq_notification->priv = open_config->priv;
+		irq_notification->bgcom_notification_cb =
+					open_config->bgcom_notification_cb;
+		add_to_irq_list(irq_notification);
+		clnt_handle->cb = irq_notification;
+	}
+	return clnt_handle;
+
+error_ret:
+	kfree(clnt_handle);
+	return NULL;
+}
+EXPORT_SYMBOL(bgcom_open);
+
+int bgcom_close(void **handle)
+{
+	struct bg_context *lhandle;
+	struct cb_data *cb = NULL;
+
+	if (*handle == NULL)
+		return -EINVAL;
+	lhandle = *handle;
+	cb = lhandle->cb;
+	if (cb)
+		list_del(&cb->list);
+
+	kfree(*handle);
+	*handle = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(bgcom_close);
+
+static irqreturn_t bg_irq_tasklet_hndlr(int irq, void *device)
+{
+	struct bg_spi_priv *bg_spi = device;
+	/* check if call-back exists */
+	if (list_empty(&cb_head)) {
+		pr_debug("No callback registered\n");
+		return IRQ_HANDLED;
+	} else if (spi_state == BGCOM_SPI_BUSY) {
+		return IRQ_HANDLED;
+	} else if (!bg_spi->irq_lock) {
+		bg_spi->irq_lock = 1;
+		bg_irq_tasklet_hndlr_l();
+		bg_spi->irq_lock = 0;
+	}
+	return IRQ_HANDLED;
+}
+
+static void bg_spi_init(struct bg_spi_priv *bg_spi)
+{
+	if (!bg_spi) {
+		pr_err("device not found\n");
+		return;
+	}
+
+	/* BGCOM SPI set-up */
+	mutex_init(&bg_spi->xfer_mutex);
+	spi_message_init(&bg_spi->msg1);
+	spi_message_add_tail(&bg_spi->xfer1, &bg_spi->msg1);
+
+	/* BGCOM IRQ set-up */
+	bg_spi->irq_lock = 0;
+
+	spi_state = BGCOM_SPI_FREE;
+
+	wq = create_singlethread_workqueue("input_wq");
+
+	bg_spi->bg_state = BGCOM_STATE_ACTIVE;
+
+	bg_com_drv = &bg_spi->lhandle;
+
+	mutex_init(&bg_resume_mutex);
+}
+
+static int bg_spi_probe(struct spi_device *spi)
+{
+	struct bg_spi_priv *bg_spi;
+	struct device_node *node;
+	int irq_gpio = 0;
+	int bg_irq = 0;
+	int ret;
+
+	bg_spi = devm_kzalloc(&spi->dev, sizeof(*bg_spi),
+				   GFP_KERNEL | GFP_ATOMIC);
+	if (!bg_spi)
+		return -ENOMEM;
+	bg_spi->spi = spi;
+	spi_set_drvdata(spi, bg_spi);
+	bg_spi_init(bg_spi);
+
+	/* BGCOM Interrupt probe */
+	node = spi->dev.of_node;
+	irq_gpio = of_get_named_gpio(node, "qcom,irq-gpio", 0);
+	if (!gpio_is_valid(irq_gpio)) {
+		pr_err("gpio %d found is not valid\n", irq_gpio);
+		goto err_ret;
+	}
+
+	ret = gpio_request(irq_gpio, "bgcom_gpio");
+	if (ret) {
+		pr_err("gpio %d request failed\n", irq_gpio);
+		goto err_ret;
+	}
+
+	ret = gpio_direction_input(irq_gpio);
+	if (ret) {
+		pr_err("gpio_direction_input not set: %d\n", ret);
+		goto err_ret;
+	}
+
+	bg_irq = gpio_to_irq(irq_gpio);
+	ret = request_threaded_irq(bg_irq, NULL, bg_irq_tasklet_hndlr,
+		IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "qcom,bg_spi", bg_spi);
+
+	if (ret)
+		goto err_ret;
+
+	pr_info("Bgcom Probed successfully\n");
+	return ret;
+
+err_ret:
+	bg_com_drv = NULL;
+	mutex_destroy(&bg_spi->xfer_mutex);
+	spi_set_drvdata(spi, NULL);
+	return -ENODEV;
+}
+
+static int bg_spi_remove(struct spi_device *spi)
+{
+	struct bg_spi_priv *bg_spi = spi_get_drvdata(spi);
+
+	mutex_destroy(&bg_spi->xfer_mutex);
+	devm_kfree(&spi->dev, bg_spi);
+	spi_set_drvdata(spi, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id bg_spi_of_match[] = {
+	{ .compatible = "qcom,bg-spi", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bg_spi_of_match);
+
+static struct spi_driver bg_spi_driver = {
+	.driver = {
+		.name = "bg-spi",
+		.of_match_table = bg_spi_of_match,
+	},
+	.probe = bg_spi_probe,
+	.remove = bg_spi_remove,
+};
+
+module_spi_driver(bg_spi_driver);
+MODULE_DESCRIPTION("bg SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/bgrsb.h b/drivers/soc/qcom/bgrsb.h
new file mode 100644
index 0000000..1ad23b7
--- /dev/null
+++ b/drivers/soc/qcom/bgrsb.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 BGRSB_H
+#define BGRSB_H
+
+struct event {
+	uint8_t sub_id;
+	int16_t evnt_data;
+	uint32_t evnt_tm;
+};
+
+
+struct bg_glink_chnl {
+	char *chnl_name;
+	char *chnl_edge;
+	char *chnl_trnsprt;
+};
+
+/**
+ * bgrsb_send_input() - send the recived input to input framework
+ * @evnt: pointer to the event structure
+ */
+int bgrsb_send_input(struct event *evnt);
+
+#endif /* BGCOM_H */
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 3da1656..b8deec1 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -3828,6 +3828,10 @@
  */
 int glink_xprt_name_to_id(const char *name, uint16_t *id)
 {
+	if (!strcmp(name, "bgcom")) {
+		*id = SPIV2_XPRT_ID;
+		return 0;
+	}
 	if (!strcmp(name, "smem")) {
 		*id = SMEM_XPRT_ID;
 		return 0;
diff --git a/drivers/soc/qcom/glink_bgcom_xprt.c b/drivers/soc/qcom/glink_bgcom_xprt.c
new file mode 100644
index 0000000..3c3923c
--- /dev/null
+++ b/drivers/soc/qcom/glink_bgcom_xprt.c
@@ -0,0 +1,1754 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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/delay.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/srcu.h>
+#include <linux/wait.h>
+#include <linux/component.h>
+#include <soc/qcom/tracer_pkt.h>
+
+#include "glink_core_if.h"
+#include "glink_private.h"
+#include "glink_xprt_if.h"
+#include "bgcom.h"
+
+#define XPRT_NAME "bgcom"
+#define FIFO_ALIGNMENT 16
+#define TRACER_PKT_FEATURE BIT(2)
+
+#define ACTIVE_TX BIT(0)
+#define ACTIVE_RX BIT(1)
+
+#define ID_MASK 0xFFFFFF
+
+#define BGCOM_RESET 0x00000000
+#define BGCOM_APPLICATION_RUNNING 0x00000001
+#define BGCOM_TO_SLAVE_FIFO_READY 0x00000002
+#define BGCOM_TO_MASTER_FIFO_READY 0x00000004
+#define BGCOM_AHB_READY 0x00000008
+#define WORD_SIZE 4
+#define TX_BLOCKED_CMD_RESERVE 16
+#define FIFO_FULL_RESERVE (TX_BLOCKED_CMD_RESERVE/WORD_SIZE)
+#define BGCOM_LINKUP (BGCOM_APPLICATION_RUNNING \
+			| BGCOM_TO_SLAVE_FIFO_READY \
+			| BGCOM_TO_MASTER_FIFO_READY \
+			| BGCOM_AHB_READY)
+/**
+ * enum command_types - definition of the types of commands sent/received
+ * @VERSION_CMD:		Version and feature set supported
+ * @VERSION_ACK_CMD:		Response for @VERSION_CMD
+ * @OPEN_CMD:			Open a channel
+ * @CLOSE_CMD:			Close a channel
+ * @OPEN_ACK_CMD:		Response to @OPEN_CMD
+ * @CLOSE_ACK_CMD:		Response for @CLOSE_CMD
+ * @RX_INTENT_CMD:		RX intent for a channel is queued
+ * @RX_DONE_CMD:		Use of RX intent for a channel is complete
+ * @RX_DONE_W_REUSE_CMD:	Same as @RX_DONE but also reuse the used intent
+ * @RX_INTENT_REQ_CMD:		Request to have RX intent queued
+ * @RX_INTENT_REQ_ACK_CMD:	Response for @RX_INTENT_REQ_CMD
+ * @TX_DATA_CMD:		Start of a data transfer
+ * @TX_DATA_CONT_CMD:		Continuation or end of a data transfer
+ * @READ_NOTIF_CMD:		Request for a notification when this cmd is read
+ * @SIGNALS_CMD:		Sideband signals
+ * @TRACER_PKT_CMD:		Start of a Tracer Packet Command
+ * @TRACER_PKT_CONT_CMD:	Continuation or end of a Tracer Packet Command
+ */
+enum command_types {
+	VERSION_CMD,
+	VERSION_ACK_CMD,
+	OPEN_CMD,
+	CLOSE_CMD,
+	OPEN_ACK_CMD,
+	CLOSE_ACK_CMD,
+	RX_INTENT_CMD,
+	RX_DONE_CMD,
+	RX_DONE_W_REUSE_CMD,
+	RX_INTENT_REQ_CMD,
+	RX_INTENT_REQ_ACK_CMD,
+	TX_DATA_CMD,
+	TX_DATA_CONT_CMD,
+	READ_NOTIF_CMD,
+	SIGNALS_CMD,
+	TRACER_PKT_CMD,
+	TRACER_PKT_CONT_CMD,
+};
+
+struct bgcom_fifo_size {
+	uint32_t to_master:16;
+	uint32_t to_slave:16;
+};
+
+struct bgcom_fifo_fill {
+	uint32_t rx_avail:16;
+	uint32_t tx_avail:16;
+};
+
+/**
+ * struct edge_info - local information for managing a single complete edge
+ * @list:			List item to traverse in edge_info list
+ * @xprt_if:			The transport interface registered with the
+ *				glink core associated with this edge.
+ * @xprt_cfg:			The transport configuration for the glink core
+ *				assocaited with this edge.
+ * @subsys_name:		Name of the remote subsystem in the edge.
+ * @bgcom_dev:			Pointer to the connectingSPI Device.
+ * @fifo_size:			Size of the FIFO at the remote end.
+ * @fifo_fill:			Current available fifo size.
+ * @tx_avail_lock:		Lock to serialize access to tx_avail.
+ * @kwork:			Work to be executed when receiving data.
+ * @kworker:			Handle to the entity processing @kwork.
+ * @task:			Handle to the task context that runs @kworker.
+ * @use_ref:			Active users of this transport grab a
+ *				reference. Used for SSR synchronization.
+ * @in_ssr:			Signals if this transport is in ssr.
+ * @water_mark_reached		Signals if tx_avail need to read from fifo.
+ * @write_lock:			Lock to serialize write/tx operation.
+ * @tx_blocked_queue:		Queue of entities waiting for the remote side to
+ *				signal the resumption of TX.
+ * @tx_resume_needed:		A tx resume signal needs to be sent to the glink
+ *				core.
+ * @tx_blocked_signal_sent:	Flag to indicate the flush signal has already
+ *				been sent, and a response is pending from the
+ *				remote side.  Protected by @write_lock.
+ * @num_pw_states:		Size of @ramp_time_us.
+ * @ramp_time_us:		Array of ramp times in microseconds where array
+ *				index position represents a power state.
+ * @bgcom_status		Maintains bgcom status based on events.
+ * @wakeup_work	:		Work item for waking up tx_thread
+ * @activity_flag:		Flag indicating active TX and RX.
+ * @activity_lock:		Lock to synchronize access to activity flag.
+ * @bgcom_config:		Config to be given to bgcom driver.
+ * @bgcom_handle:		Handle to use bgcom driver apis.
+ */
+struct edge_info {
+	struct list_head list;
+	struct glink_transport_if xprt_if;
+	struct glink_core_transport_cfg xprt_cfg;
+	char subsys_name[GLINK_NAME_SIZE];
+	struct bgcom_device *bgcom_dev;
+
+	struct bgcom_fifo_size fifo_size;
+	struct bgcom_fifo_fill fifo_fill;
+	struct mutex tx_avail_lock;
+
+	struct kthread_worker kworker;
+	struct task_struct *task;
+	struct srcu_struct use_ref;
+	bool in_ssr;
+	bool water_mark_reached;
+	struct mutex write_lock;
+	wait_queue_head_t tx_blocked_queue;
+	bool tx_resume_needed;
+	bool tx_blocked_signal_sent;
+
+	uint32_t num_pw_states;
+	unsigned long *ramp_time_us;
+	uint32_t bgcom_status;
+	struct work_struct wakeup_work;
+	uint32_t activity_flag;
+	spinlock_t activity_lock;
+	struct bgcom_open_config_type bgcom_config;
+	void *bgcom_handle;
+};
+
+struct rx_pkt {
+	void *rx_buf;
+	uint32_t rx_len;
+	struct edge_info *einfo;
+	struct kthread_work kwork;
+};
+
+
+static uint32_t negotiate_features_v1(struct glink_transport_if *if_ptr,
+				      const struct glink_core_version *version,
+				      uint32_t features);
+static DEFINE_SPINLOCK(edge_infos_lock);
+static LIST_HEAD(edge_infos);
+static struct glink_core_version versions[] = {
+	{1, TRACER_PKT_FEATURE, negotiate_features_v1},
+};
+
+/**
+ * negotiate_features_v1() - determine what features of a version can be used
+ * @if_ptr:	The transport for which features are negotiated for.
+ * @version:	The version negotiated.
+ * @features:	The set of requested features.
+ *
+ * Return: What set of the requested features can be supported.
+ */
+static uint32_t negotiate_features_v1(struct glink_transport_if *if_ptr,
+				      const struct glink_core_version *version,
+				      uint32_t features)
+{
+	return features & version->features;
+}
+
+/**
+ * glink_bgcom_get_tx_avail() - Available Write Space in the remote side
+ * @einfo:	Edge information corresponding to the remote side.
+ *
+ * Return: 0 on error, available write space on success.
+ */
+static int glink_bgcom_get_tx_avail(struct edge_info *einfo)
+{
+	uint32_t tx_avail;
+
+	mutex_lock(&einfo->tx_avail_lock);
+	tx_avail = einfo->fifo_fill.tx_avail;
+	if (tx_avail < FIFO_FULL_RESERVE)
+		tx_avail = 0;
+	else
+		tx_avail -= FIFO_FULL_RESERVE;
+
+	mutex_unlock(&einfo->tx_avail_lock);
+	return tx_avail;
+}
+
+
+/**
+ * glink_bgcom_update_tx_avail() - update available Write Space in fifo
+ * @einfo:	Edge information corresponding to the remote side.
+ * @size:	size to update.
+ *
+ * Return: 0 on error, available write space on success.
+ */
+static void glink_bgcom_update_tx_avail(struct edge_info *einfo, uint32_t size)
+{
+	mutex_lock(&einfo->tx_avail_lock);
+	einfo->fifo_fill.tx_avail -= size;
+	if (einfo->fifo_fill.tx_avail < einfo->fifo_size.to_slave/2)
+		einfo->water_mark_reached = true;
+	mutex_unlock(&einfo->tx_avail_lock);
+}
+
+/**
+ * glink_bgcom_xprt_tx_cmd_safe() - Transmit G-Link commands
+ * @einfo:	Edge information corresponding to the remote subsystem.
+ * @src:	Source buffer containing the G-Link command.
+ * @size:	Size of the command to transmit.
+ *
+ * This function is used to transmit the G-Link commands. This function
+ * must be called with einfo->write_lock locked.
+ *
+ * Return: 0 on success, standard Linux error codes on error.
+ */
+static int glink_bgcom_xprt_tx_cmd_safe(struct edge_info *einfo, void *src,
+				      uint32_t size)
+{
+	uint32_t tx_avail = glink_bgcom_get_tx_avail(einfo);
+	int ret;
+	uint32_t size_in_words = size/WORD_SIZE;
+
+	if (size_in_words > tx_avail) {
+		GLINK_ERR("%s: No Space in Fifo\n", __func__);
+		return -ENOSPC;
+	}
+
+	ret = bgcom_fifo_write(einfo->bgcom_handle, size_in_words, src);
+	if (ret < 0) {
+		GLINK_ERR("%s: Error %d writing data\n", __func__, ret);
+		return ret;
+	}
+	glink_bgcom_update_tx_avail(einfo, size_in_words);
+	return ret;
+}
+
+/**
+ * send_tx_blocked_signal() - Send flow control request message
+ * @einfo:	Edge information corresponding to the remote subsystem.
+ *
+ * This function is used to send a message to the remote subsystem indicating
+ * that the local subsystem is waiting for the write space. The remote
+ * subsystem on receiving this message will send a resume tx message.
+ */
+static void send_tx_blocked_signal(struct edge_info *einfo)
+{
+	struct read_notif_request {
+		uint16_t cmd;
+		uint16_t reserved;
+		uint32_t reserved2;
+		uint64_t reserved3;
+	};
+	struct read_notif_request read_notif_req = {0};
+	int size_in_word = sizeof(read_notif_req)/WORD_SIZE;
+	void *src = &read_notif_req;
+	int ret;
+
+	read_notif_req.cmd = READ_NOTIF_CMD;
+	if (!einfo->tx_blocked_signal_sent) {
+		einfo->tx_blocked_signal_sent = true;
+		ret = bgcom_fifo_write(einfo->bgcom_handle, size_in_word, src);
+		if (ret < 0) {
+			GLINK_ERR("%s: Err %d send blocked\n", __func__, ret);
+			return;
+		}
+		glink_bgcom_update_tx_avail(einfo, size_in_word);
+	}
+}
+
+/**
+ * glink_bgcom_xprt_tx_cmd() - Transmit G-Link commands
+ * @einfo:	Edge information corresponding to the remote subsystem.
+ * @src:	Source buffer containing the G-Link command.
+ * @size:	Size of the command to transmit.
+ *
+ * This function is used to transmit the G-Link commands. This function
+ * might sleep if the space is not available to transmit the command.
+ *
+ * Return: 0 on success, standard Linux error codes on error.
+ */
+static int glink_bgcom_xprt_tx_cmd(struct edge_info *einfo, void *src,
+				 uint32_t size)
+{
+	int ret;
+	DEFINE_WAIT(wait);
+
+	mutex_lock(&einfo->write_lock);
+	while (glink_bgcom_get_tx_avail(einfo) < (size/WORD_SIZE)) {
+		send_tx_blocked_signal(einfo);
+		prepare_to_wait(&einfo->tx_blocked_queue, &wait,
+				TASK_UNINTERRUPTIBLE);
+		if (glink_bgcom_get_tx_avail(einfo) < (size/WORD_SIZE)
+							&& !einfo->in_ssr) {
+			mutex_unlock(&einfo->write_lock);
+			schedule();
+			mutex_lock(&einfo->write_lock);
+		}
+		finish_wait(&einfo->tx_blocked_queue, &wait);
+		if (einfo->in_ssr) {
+			mutex_unlock(&einfo->write_lock);
+			return -EFAULT;
+		}
+	}
+	ret = glink_bgcom_xprt_tx_cmd_safe(einfo, src, size);
+	mutex_unlock(&einfo->write_lock);
+	return ret;
+}
+
+/**
+ * process_rx_data() - process received data from an edge
+ * @einfo:		The edge the data is received on.
+ * @cmd_id:		ID to specify the type of data.
+ * @rcid:		The remote channel id associated with the data.
+ * @intend_id:		The intent the data should be put in.
+ * @src:		Address of the source buffer from which the data
+ *			is read.
+ * @frag_size:		Size of the data fragment to read.
+ * @size_remaining:	Size of data left to be read in this packet.
+ */
+static void process_rx_data(struct edge_info *einfo, uint16_t cmd_id,
+			    uint32_t rcid, uint32_t intent_id, void *src,
+			    uint32_t frag_size, uint32_t size_remaining)
+{
+	struct glink_core_rx_intent *intent;
+	int rc = 0;
+
+	intent = einfo->xprt_if.glink_core_if_ptr->rx_get_pkt_ctx(
+				&einfo->xprt_if, rcid, intent_id);
+	if (intent == NULL) {
+		GLINK_ERR("%s: no intent for ch %d liid %d\n", __func__, rcid,
+			  intent_id);
+		return;
+	} else if (intent->data == NULL) {
+		GLINK_ERR("%s: intent for ch %d liid %d has no data buff\n",
+			  __func__, rcid, intent_id);
+		return;
+	} else if (intent->intent_size - intent->write_offset < frag_size ||
+		 intent->write_offset + size_remaining > intent->intent_size) {
+		GLINK_ERR("%s: rx data size:%d and remaining:%d %s %d %s:%d\n",
+			  __func__, frag_size, size_remaining,
+			  "will overflow ch", rcid, "intent", intent_id);
+		return;
+	}
+
+	rc = bgcom_ahb_read(einfo->bgcom_handle, (uint32_t)(size_t)src,
+				ALIGN(frag_size, WORD_SIZE)/WORD_SIZE,
+				intent->data + intent->write_offset);
+
+	if (rc < 0) {
+		GLINK_ERR("%s: Error %d receiving data %d:%d:%d:%d\n",
+			  __func__, rc, rcid, intent_id, frag_size,
+			  size_remaining);
+		size_remaining += frag_size;
+	} else {
+		intent->write_offset += frag_size;
+		intent->pkt_size += frag_size;
+
+		if (unlikely((cmd_id == TRACER_PKT_CMD ||
+			cmd_id == TRACER_PKT_CONT_CMD) && !size_remaining)) {
+			tracer_pkt_log_event(intent->data, GLINK_XPRT_RX);
+			intent->tracer_pkt = true;
+		}
+	}
+	einfo->xprt_if.glink_core_if_ptr->rx_put_pkt_ctx(&einfo->xprt_if,
+				rcid, intent, size_remaining ? false : true);
+}
+
+/**
+ * process_rx_cmd() - Process incoming G-Link commands
+ * @einfo:	Edge information corresponding to the remote subsystem.
+ * @rx_data:	Buffer which contains the G-Link commands to be processed.
+ * @rx_size:	Size of the buffer containing the series of G-Link commands.
+ *
+ * This function is used to parse and process a series of G-Link commands
+ * received in a buffer.
+ */
+static void process_rx_cmd(struct edge_info *einfo,
+			   void *rx_data, int rx_size)
+{
+	struct command {
+		uint16_t id;
+		uint16_t param1;
+		uint32_t param2;
+		uint32_t param3;
+		uint32_t param4;
+	};
+	struct intent_desc {
+		uint32_t size;
+		uint32_t id;
+		uint64_t addr;
+	};
+	struct rx_desc {
+		uint32_t size;
+		uint32_t size_left;
+		uint64_t addr;
+	};
+	struct command *cmd;
+	struct intent_desc *intents;
+	struct rx_desc *rx_descp;
+	int offset = 0;
+	int rcu_id;
+	uint16_t rcid;
+	uint16_t name_len;
+	uint16_t prio;
+	char *name;
+	bool granted;
+	int i;
+
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+
+	while (offset < rx_size) {
+		cmd = (struct command *)(rx_data + offset);
+		offset += sizeof(*cmd);
+		switch (cmd->id) {
+		case VERSION_CMD:
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_version(
+				&einfo->xprt_if, cmd->param1, cmd->param2);
+			break;
+
+		case VERSION_ACK_CMD:
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_version_ack(
+				&einfo->xprt_if, cmd->param1, cmd->param2);
+			break;
+
+		case OPEN_CMD:
+			rcid = cmd->param1;
+			name_len = (uint16_t)(cmd->param2 & 0xFFFF);
+			prio = (uint16_t)((cmd->param2 & 0xFFFF0000) >> 16);
+			name = (char *)(rx_data + offset);
+			offset += ALIGN(name_len, FIFO_ALIGNMENT);
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_ch_remote_open(
+				&einfo->xprt_if, rcid, name, prio);
+			break;
+
+		case CLOSE_CMD:
+			einfo->xprt_if.glink_core_if_ptr->
+					rx_cmd_ch_remote_close(
+						&einfo->xprt_if, cmd->param1);
+			break;
+
+		case OPEN_ACK_CMD:
+			prio = (uint16_t)(cmd->param2 & 0xFFFF);
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_ch_open_ack(
+				&einfo->xprt_if, cmd->param1, prio);
+			break;
+
+		case CLOSE_ACK_CMD:
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_ch_close_ack(
+					&einfo->xprt_if, cmd->param1);
+			break;
+
+		case RX_INTENT_CMD:
+			for (i = 0; i < cmd->param2; i++) {
+				intents = (struct intent_desc *)
+						(rx_data + offset);
+				offset += sizeof(*intents);
+				einfo->xprt_if.glink_core_if_ptr->
+					rx_cmd_remote_rx_intent_put_cookie(
+					&einfo->xprt_if, cmd->param1,
+					intents->id, intents->size,
+					(void *)(uintptr_t)(intents->addr));
+			}
+			break;
+
+		case RX_DONE_CMD:
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_tx_done(
+				&einfo->xprt_if, cmd->param1, cmd->param2,
+				false);
+			break;
+
+		case RX_INTENT_REQ_CMD:
+			einfo->xprt_if.glink_core_if_ptr->
+				rx_cmd_remote_rx_intent_req(
+					&einfo->xprt_if, cmd->param1,
+					cmd->param2);
+			break;
+
+		case RX_INTENT_REQ_ACK_CMD:
+			granted = cmd->param2 == 1 ? true : false;
+			einfo->xprt_if.glink_core_if_ptr->
+				rx_cmd_rx_intent_req_ack(&einfo->xprt_if,
+						cmd->param1, granted);
+			break;
+
+		case TX_DATA_CMD:
+		case TX_DATA_CONT_CMD:
+		case TRACER_PKT_CMD:
+		case TRACER_PKT_CONT_CMD:
+			rx_descp = (struct rx_desc *)(rx_data + offset);
+			offset += sizeof(*rx_descp);
+			process_rx_data(einfo, cmd->id, cmd->param1,
+					cmd->param2,
+					(void *)(uintptr_t)(rx_descp->addr),
+					rx_descp->size, rx_descp->size_left);
+			break;
+
+		case READ_NOTIF_CMD:
+			break;
+
+		case SIGNALS_CMD:
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_remote_sigs(
+				&einfo->xprt_if, cmd->param1, cmd->param2);
+			break;
+
+		case RX_DONE_W_REUSE_CMD:
+			einfo->xprt_if.glink_core_if_ptr->rx_cmd_tx_done(
+				&einfo->xprt_if, cmd->param1,
+				cmd->param2, true);
+			break;
+
+		default:
+			GLINK_ERR("Unrecognized command: %d\n", cmd->id);
+			break;
+		}
+	}
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+
+/**
+ * tx_wakeup_worker() - worker function to wakeup tx blocked thread
+ * @work:	kwork associated with the edge to process commands on.
+ */
+static void tx_wakeup_worker(struct edge_info *einfo)
+{
+	int rcu_id;
+	struct bgcom_fifo_fill fifo_fill;
+
+	mutex_lock(&einfo->tx_avail_lock);
+	bgcom_reg_read(einfo->bgcom_handle, BGCOM_REG_FIFO_FILL, 1,
+						&fifo_fill);
+	einfo->fifo_fill.tx_avail = fifo_fill.tx_avail;
+	if (einfo->fifo_fill.tx_avail > einfo->fifo_size.to_slave/2)
+		einfo->water_mark_reached = false;
+	mutex_unlock(&einfo->tx_avail_lock);
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+	if (einfo->tx_resume_needed &&
+				glink_bgcom_get_tx_avail(einfo)) {
+		einfo->tx_resume_needed = false;
+		einfo->xprt_if.glink_core_if_ptr->tx_resume(
+						&einfo->xprt_if);
+	}
+	mutex_lock(&einfo->write_lock);
+	if (einfo->tx_blocked_signal_sent) {
+		wake_up_all(&einfo->tx_blocked_queue);
+		einfo->tx_blocked_signal_sent = false;
+	}
+	mutex_unlock(&einfo->write_lock);
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * __rx_worker() - Receive commands on a specific edge
+ * @einfo:      Edge to process commands on.
+ *
+ * This function checks the size of data to be received, allocates the
+ * buffer for that data and reads the data from the remote subsytem
+ * into that buffer. This function then calls the process_rx_cmd() to
+ * parse the received G-Link command sequence. This function will also
+ * poll for the data for a predefined duration for performance reasons.
+ */
+static void __rx_worker(struct rx_pkt *rx_pkt_info)
+{
+	int rcu_id;
+	struct edge_info *einfo = rx_pkt_info->einfo;
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+	process_rx_cmd(einfo, rx_pkt_info->rx_buf,
+				rx_pkt_info->rx_len*WORD_SIZE);
+	kfree(rx_pkt_info->rx_buf);
+	kfree(rx_pkt_info);
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * rx_worker() - Worker function to process received commands
+ * @work:       kwork associated with the edge to process commands on.
+ */
+static void rx_worker(struct kthread_work *work)
+{
+	struct rx_pkt *rx_pkt_info;
+
+	rx_pkt_info = container_of(work, struct rx_pkt, kwork);
+	__rx_worker(rx_pkt_info);
+};
+
+/**
+ * tx_cmd_version() - Convert a version cmd to wire format and transmit
+ * @if_ptr:     The transport to transmit on.
+ * @version:    The version number to encode.
+ * @features:   The features information to encode.
+ */
+static void tx_cmd_version(struct glink_transport_if *if_ptr, uint32_t version,
+			   uint32_t features)
+{
+	struct command {
+		uint16_t id;
+		uint16_t version;
+		uint32_t features;
+		uint32_t fifo_size;
+		uint32_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+
+	cmd.id = VERSION_CMD;
+	cmd.version = version;
+	cmd.features = features;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * tx_cmd_version_ack() - Convert a version ack cmd to wire format and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @version:	The version number to encode.
+ * @features:	The features information to encode.
+ */
+static void tx_cmd_version_ack(struct glink_transport_if *if_ptr,
+			       uint32_t version,
+			       uint32_t features)
+{
+	struct command {
+		uint16_t id;
+		uint16_t version;
+		uint32_t features;
+		uint32_t fifo_size;
+		uint32_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+
+	cmd.id = VERSION_ACK_CMD;
+	cmd.version = version;
+	cmd.features = features;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * set_version() - Activate a negotiated version and feature set
+ * @if_ptr:	The transport to configure.
+ * @version:	The version to use.
+ * @features:	The features to use.
+ *
+ * Return: The supported capabilities of the transport.
+ */
+static uint32_t set_version(struct glink_transport_if *if_ptr, uint32_t version,
+			uint32_t features)
+{
+	struct edge_info *einfo;
+	uint32_t ret;
+	int rcu_id;
+
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return 0;
+	}
+
+	ret = GCAP_SIGNALS;
+	if (features & TRACER_PKT_FEATURE)
+		ret |= GCAP_TRACER_PKT;
+
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return ret;
+}
+
+/**
+ * tx_cmd_ch_open() - Convert a channel open cmd to wire format and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ * @name:	The channel name to encode.
+ * @req_xprt:	The transport the core would like to migrate this channel to.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_ch_open(struct glink_transport_if *if_ptr, uint32_t lcid,
+			  const char *name, uint16_t req_xprt)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint16_t length;
+		uint16_t req_xprt;
+		uint64_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	uint32_t buf_size;
+	void *buf;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	cmd.id = OPEN_CMD;
+	cmd.lcid = lcid;
+	cmd.length = (uint16_t)(strlen(name) + 1);
+	cmd.req_xprt = req_xprt;
+
+	buf_size = ALIGN(sizeof(cmd) + cmd.length, FIFO_ALIGNMENT);
+
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -ENOMEM;
+	}
+
+	memcpy(buf, &cmd, sizeof(cmd));
+	memcpy(buf + sizeof(cmd), name, cmd.length);
+
+	glink_bgcom_xprt_tx_cmd(einfo, buf, buf_size);
+
+	kfree(buf);
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return 0;
+}
+
+/**
+ * tx_cmd_ch_close() - Convert a channel close cmd to wire format and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_ch_close(struct glink_transport_if *if_ptr, uint32_t lcid)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t reserved1;
+		uint64_t reserved2;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	cmd.id = CLOSE_CMD;
+	cmd.lcid = lcid;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return 0;
+}
+
+/**
+ * tx_cmd_ch_remote_open_ack() - Convert a channel open ack cmd to wire format
+ *				 and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @rcid:	The remote channel id to encode.
+ * @xprt_resp:	The response to a transport migration request.
+ */
+static void tx_cmd_ch_remote_open_ack(struct glink_transport_if *if_ptr,
+				     uint32_t rcid, uint16_t xprt_resp)
+{
+	struct command {
+		uint16_t id;
+		uint16_t rcid;
+		uint16_t reserved1;
+		uint16_t xprt_resp;
+		uint64_t reserved2;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+
+	cmd.id = OPEN_ACK_CMD;
+	cmd.rcid = rcid;
+	cmd.xprt_resp = xprt_resp;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * tx_cmd_ch_remote_close_ack() - Convert a channel close ack cmd to wire format
+ *				  and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @rcid:	The remote channel id to encode.
+ */
+static void tx_cmd_ch_remote_close_ack(struct glink_transport_if *if_ptr,
+				       uint32_t rcid)
+{
+	struct command {
+		uint16_t id;
+		uint16_t rcid;
+		uint32_t reserved1;
+		uint64_t reserved2;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+
+	cmd.id = CLOSE_ACK_CMD;
+	cmd.rcid = rcid;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * ssr() - Process a subsystem restart notification of a transport
+ * @if_ptr:	The transport to restart
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int ssr(struct glink_transport_if *if_ptr)
+{
+	struct edge_info *einfo;
+
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	einfo->in_ssr = true;
+	wake_up_all(&einfo->tx_blocked_queue);
+
+	synchronize_srcu(&einfo->use_ref);
+	einfo->tx_resume_needed = false;
+	einfo->tx_blocked_signal_sent = false;
+	einfo->xprt_if.glink_core_if_ptr->link_down(&einfo->xprt_if);
+
+	return 0;
+}
+
+/**
+ * allocate_rx_intent() - Allocate/reserve space for RX Intent
+ * @if_ptr:	The transport the intent is associated with.
+ * @size:	size of intent.
+ * @intent:	Pointer to the intent structure.
+ *
+ * Assign "data" with the buffer created, since the transport creates
+ * a linear buffer and "iovec" with the "intent" itself, so that
+ * the data can be passed to a client that receives only vector buffer.
+ * Note that returning NULL for the pointer is valid (it means that space has
+ * been reserved, but the actual pointer will be provided later).
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int allocate_rx_intent(struct glink_transport_if *if_ptr, size_t size,
+			      struct glink_core_rx_intent *intent)
+{
+	void *t;
+
+	t = kzalloc(ALIGN(size, WORD_SIZE), GFP_KERNEL);
+	if (!t)
+		return -ENOMEM;
+
+	intent->data = t;
+	intent->iovec = (void *)intent;
+	intent->vprovider = rx_linear_vbuf_provider;
+	intent->pprovider = NULL;
+	return 0;
+}
+
+/**
+ * deallocate_rx_intent() - Deallocate space created for RX Intent
+ * @if_ptr:	The transport the intent is associated with.
+ * @intent:	Pointer to the intent structure.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int deallocate_rx_intent(struct glink_transport_if *if_ptr,
+				struct glink_core_rx_intent *intent)
+{
+	if (!intent || !intent->data)
+		return -EINVAL;
+
+	kfree(intent->data);
+	intent->data = NULL;
+	intent->iovec = NULL;
+	intent->vprovider = NULL;
+	return 0;
+}
+
+/**
+ * tx_cmd_local_rx_intent() - Convert an rx intent cmd to wire format and
+ *			      transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ * @size:	The intent size to encode.
+ * @liid:	The local intent id to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_local_rx_intent(struct glink_transport_if *if_ptr,
+				  uint32_t lcid, size_t size, uint32_t liid)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t count;
+		uint64_t reserved;
+		uint32_t size;
+		uint32_t liid;
+		uint64_t addr;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	if (size > UINT_MAX) {
+		GLINK_ERR("%s: size %zu is too large to encode\n",
+							__func__, size);
+		return -EMSGSIZE;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	cmd.id = RX_INTENT_CMD;
+	cmd.lcid = lcid;
+	cmd.count = 1;
+	cmd.size = size;
+	cmd.liid = liid;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return 0;
+}
+
+/**
+ * tx_cmd_local_rx_done() - Convert an rx done cmd to wire format and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ * @liid:	The local intent id to encode.
+ * @reuse:	Reuse the consumed intent.
+ */
+static void tx_cmd_local_rx_done(struct glink_transport_if *if_ptr,
+				 uint32_t lcid, uint32_t liid, bool reuse)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t liid;
+		uint64_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return;
+	}
+
+	cmd.id = reuse ? RX_DONE_W_REUSE_CMD : RX_DONE_CMD;
+	cmd.lcid = lcid;
+	cmd.liid = liid;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
+ * tx_cmd_rx_intent_req() - Convert an rx intent request cmd to wire format and
+ *			    transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ * @size:	The requested intent size to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_rx_intent_req(struct glink_transport_if *if_ptr,
+				uint32_t lcid, size_t size)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t size;
+		uint64_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	if (size > UINT_MAX) {
+		GLINK_ERR("%s: size %zu is too large to encode\n",
+							__func__, size);
+		return -EMSGSIZE;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	cmd.id = RX_INTENT_REQ_CMD,
+	cmd.lcid = lcid;
+	cmd.size = ALIGN(size, WORD_SIZE);
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return 0;
+}
+
+/**
+ * tx_cmd_rx_intent_req_ack() - Convert an rx intent request ack cmd to wire
+ *				format and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ * @granted:	The request response to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_remote_rx_intent_req_ack(struct glink_transport_if *if_ptr,
+					   uint32_t lcid, bool granted)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t response;
+		uint64_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	cmd.id = RX_INTENT_REQ_ACK_CMD,
+	cmd.lcid = lcid;
+	if (granted)
+		cmd.response = 1;
+	else
+		cmd.response = 0;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return 0;
+}
+
+/**
+ * tx_cmd_set_sigs() - Convert a signals ack cmd to wire format and transmit
+ * @if_ptr:	The transport to transmit on.
+ * @lcid:	The local channel id to encode.
+ * @sigs:	The signals to encode.
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int tx_cmd_set_sigs(struct glink_transport_if *if_ptr, uint32_t lcid,
+			   uint32_t sigs)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t sigs;
+		uint64_t reserved;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	int rcu_id;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	cmd.id = SIGNALS_CMD,
+	cmd.lcid = lcid;
+	cmd.sigs = sigs;
+
+	glink_bgcom_xprt_tx_cmd(einfo, &cmd, sizeof(cmd));
+
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return 0;
+}
+
+/**
+ * tx_data() - convert a data/tracer_pkt to wire format and transmit
+ * @if_ptr:     The transport to transmit on.
+ * @cmd_id:     The command ID to transmit.
+ * @lcid:       The local channel id to encode.
+ * @pctx:       The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx_data(struct glink_transport_if *if_ptr, uint16_t cmd_id,
+		   uint32_t lcid, struct glink_core_tx_pkt *pctx)
+{
+	struct command {
+		uint16_t id;
+		uint16_t lcid;
+		uint32_t riid;
+		uint64_t reserved;
+		uint32_t size;
+		uint32_t size_left;
+		uint64_t addr;
+	};
+	struct command cmd;
+	struct edge_info *einfo;
+	void *data_start, *dst = NULL;
+	size_t tx_size = 0;
+	int rcu_id;
+
+	if (pctx->size < pctx->size_remaining) {
+		GLINK_ERR("%s: size remaining exceeds size.  Resetting.\n",
+			  __func__);
+		pctx->size_remaining = pctx->size;
+	}
+	if (!pctx->size_remaining)
+		return 0;
+
+	memset(&cmd, 0, sizeof(cmd));
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+
+	rcu_id = srcu_read_lock(&einfo->use_ref);
+	if (einfo->in_ssr) {
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EFAULT;
+	}
+
+	if (cmd_id == TX_DATA_CMD) {
+		if (pctx->size_remaining == pctx->size)
+			cmd.id = TX_DATA_CMD;
+		else
+			cmd.id = TX_DATA_CONT_CMD;
+	} else {
+		if (pctx->size_remaining == pctx->size)
+			cmd.id = TRACER_PKT_CMD;
+		else
+			cmd.id = TRACER_PKT_CONT_CMD;
+	}
+	cmd.lcid = lcid;
+	cmd.riid = pctx->riid;
+	data_start = get_tx_vaddr(pctx, pctx->size - pctx->size_remaining,
+				  &tx_size);
+	if (unlikely(!data_start)) {
+		GLINK_ERR("%s: invalid data_start\n", __func__);
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		return -EINVAL;
+	}
+	if (likely(pctx->cookie))
+		dst = pctx->cookie + (pctx->size - pctx->size_remaining);
+
+	mutex_lock(&einfo->write_lock);
+	/* Need enough space to write the command */
+	if (glink_bgcom_get_tx_avail(einfo) <= sizeof(cmd)/WORD_SIZE) {
+		einfo->tx_resume_needed = true;
+		send_tx_blocked_signal(einfo);
+		mutex_unlock(&einfo->write_lock);
+		srcu_read_unlock(&einfo->use_ref, rcu_id);
+		GLINK_ERR("%s: No Space in Fifo\n", __func__);
+		return -EAGAIN;
+	}
+	cmd.addr = 0;
+	cmd.size = tx_size;
+	pctx->size_remaining -= tx_size;
+	cmd.size_left = pctx->size_remaining;
+	if (cmd.id == TRACER_PKT_CMD)
+		tracer_pkt_log_event((void *)(pctx->data), GLINK_XPRT_TX);
+
+	bgcom_resume(einfo->bgcom_handle);
+	bgcom_ahb_write(einfo->bgcom_handle, (uint32_t)(size_t)dst,
+				ALIGN(tx_size, WORD_SIZE)/WORD_SIZE,
+				data_start);
+	glink_bgcom_xprt_tx_cmd_safe(einfo, &cmd, sizeof(cmd));
+	GLINK_DBG("%s %s: lcid[%u] riid[%u] cmd %d, size[%d], size_left[%d]\n",
+		  "<BGCOM>", __func__, cmd.lcid, cmd.riid, cmd.id, cmd.size,
+		  cmd.size_left);
+	mutex_unlock(&einfo->write_lock);
+	srcu_read_unlock(&einfo->use_ref, rcu_id);
+	return cmd.size;
+}
+
+/**
+ * tx() - convert a data transmit cmd to wire format and transmit
+ * @if_ptr:     The transport to transmit on.
+ * @lcid:       The local channel id to encode.
+ * @pctx:       The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx(struct glink_transport_if *if_ptr, uint32_t lcid,
+	      struct glink_core_tx_pkt *pctx)
+{
+	return tx_data(if_ptr, TX_DATA_CMD, lcid, pctx);
+}
+
+/**
+ * tx_cmd_tracer_pkt() - convert a tracer packet cmd to wire format and transmit
+ * @if_ptr:     The transport to transmit on.
+ * @lcid:       The local channel id to encode.
+ * @pctx:       The data to encode.
+ *
+ * Return: Number of bytes written or standard Linux error code.
+ */
+static int tx_cmd_tracer_pkt(struct glink_transport_if *if_ptr, uint32_t lcid,
+			     struct glink_core_tx_pkt *pctx)
+{
+	return tx_data(if_ptr, TRACER_PKT_CMD, lcid, pctx);
+}
+
+/**
+ * int wait_link_down() - Check status of read/write indices
+ * @if_ptr:     The transport to check
+ *
+ * Return: 1 if indices are all zero, 0 otherwise
+ */
+static int wait_link_down(struct glink_transport_if *if_ptr)
+{
+	return 0;
+}
+
+/**
+ * get_power_vote_ramp_time() - Get the ramp time required for the power
+ *                              votes to be applied
+ * @if_ptr:     The transport interface on which power voting is requested.
+ * @state:      The power state for which ramp time is required.
+ *
+ * Return: The ramp time specific to the power state, standard error otherwise.
+ */
+static unsigned long get_power_vote_ramp_time(
+		struct glink_transport_if *if_ptr, uint32_t state)
+{
+	return 0;
+}
+
+/**
+ * power_vote() - Update the power votes to meet qos requirement
+ * @if_ptr:     The transport interface on which power voting is requested.
+ * @state:      The power state for which the voting should be done.
+ *
+ * Return: 0 on Success, standard error otherwise.
+ */
+static int power_vote(struct glink_transport_if *if_ptr, uint32_t state)
+{
+	unsigned long flags;
+	struct edge_info *einfo;
+
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+	spin_lock_irqsave(&einfo->activity_lock, flags);
+	einfo->activity_flag |= ACTIVE_TX;
+	spin_unlock_irqrestore(&einfo->activity_lock, flags);
+	return 0;
+}
+
+/**
+ * power_unvote() - Remove the all the power votes
+ * @if_ptr:     The transport interface on which power voting is requested.
+ *
+ * Return: 0 on Success, standard error otherwise.
+ */
+static int power_unvote(struct glink_transport_if *if_ptr)
+{
+	unsigned long flags;
+	struct edge_info *einfo;
+
+	einfo = container_of(if_ptr, struct edge_info, xprt_if);
+	spin_lock_irqsave(&einfo->activity_lock, flags);
+	einfo->activity_flag &= ~ACTIVE_TX;
+	spin_unlock_irqrestore(&einfo->activity_lock, flags);
+	return 0;
+}
+
+static void glink_bgcom_linkup(struct edge_info *einfo)
+{
+	if (einfo->bgcom_status != BGCOM_LINKUP)
+		return;
+	einfo->in_ssr = false;
+	synchronize_srcu(&einfo->use_ref);
+	bgcom_reg_read(einfo->bgcom_handle, BGCOM_REG_FIFO_SIZE, 1,
+				&einfo->fifo_size);
+	mutex_lock(&einfo->tx_avail_lock);
+	einfo->fifo_fill.tx_avail = einfo->fifo_size.to_master;
+	mutex_unlock(&einfo->tx_avail_lock);
+	einfo->xprt_if.glink_core_if_ptr->link_up(&einfo->xprt_if);
+}
+
+static void glink_bgcom_event_handler(void *handle,
+		void *priv_data, enum bgcom_event_type event,
+		union bgcom_event_data_type *data)
+{
+	struct edge_info *einfo = (struct edge_info *)priv_data;
+	struct rx_pkt *rx_pkt_info;
+
+	switch (event) {
+	case BGCOM_EVENT_APPLICATION_RUNNING:
+		if (data->application_running &&
+				einfo->bgcom_status != BGCOM_LINKUP) {
+			einfo->bgcom_status |= BGCOM_APPLICATION_RUNNING;
+			glink_bgcom_linkup(einfo);
+		}
+		break;
+	case BGCOM_EVENT_TO_SLAVE_FIFO_READY:
+		if (data->to_slave_fifo_ready &&
+				einfo->bgcom_status != BGCOM_LINKUP) {
+			einfo->bgcom_status |= BGCOM_TO_SLAVE_FIFO_READY;
+			glink_bgcom_linkup(einfo);
+		}
+		break;
+	case BGCOM_EVENT_TO_MASTER_FIFO_READY:
+		if (data->to_master_fifo_ready &&
+				einfo->bgcom_status != BGCOM_LINKUP) {
+			einfo->bgcom_status |= BGCOM_TO_MASTER_FIFO_READY;
+			glink_bgcom_linkup(einfo);
+		}
+		break;
+	case BGCOM_EVENT_AHB_READY:
+		if (data->ahb_ready &&
+				einfo->bgcom_status != BGCOM_LINKUP) {
+			einfo->bgcom_status |= BGCOM_AHB_READY;
+			glink_bgcom_linkup(einfo);
+		}
+		break;
+	case BGCOM_EVENT_TO_MASTER_FIFO_USED:
+		rx_pkt_info = kzalloc(sizeof(struct rx_pkt), GFP_KERNEL);
+		rx_pkt_info->rx_buf = data->fifo_data.data;
+		rx_pkt_info->rx_len = data->fifo_data.to_master_fifo_used;
+		rx_pkt_info->einfo = einfo;
+		kthread_init_work(&rx_pkt_info->kwork, rx_worker);
+		kthread_queue_work(&einfo->kworker, &rx_pkt_info->kwork);
+		break;
+	case BGCOM_EVENT_TO_SLAVE_FIFO_FREE:
+		if (einfo->water_mark_reached)
+			tx_wakeup_worker(einfo);
+		break;
+	case BGCOM_EVENT_RESET_OCCURRED:
+		einfo->bgcom_status = BGCOM_RESET;
+		ssr(&einfo->xprt_if);
+		break;
+	case BGCOM_EVENT_ERROR_WRITE_FIFO_OVERRUN:
+	case BGCOM_EVENT_ERROR_WRITE_FIFO_BUS_ERR:
+	case BGCOM_EVENT_ERROR_WRITE_FIFO_ACCESS:
+	case BGCOM_EVENT_ERROR_READ_FIFO_UNDERRUN:
+	case BGCOM_EVENT_ERROR_READ_FIFO_BUS_ERR:
+	case BGCOM_EVENT_ERROR_READ_FIFO_ACCESS:
+	case BGCOM_EVENT_ERROR_TRUNCATED_READ:
+	case BGCOM_EVENT_ERROR_TRUNCATED_WRITE:
+	case BGCOM_EVENT_ERROR_AHB_ILLEGAL_ADDRESS:
+	case BGCOM_EVENT_ERROR_AHB_BUS_ERR:
+		GLINK_ERR("%s: ERROR %d", __func__, event);
+		break;
+	default:
+		GLINK_ERR("%s: unhandled event %d", __func__, event);
+		break;
+	}
+}
+
+/**
+ * init_xprt_if() - Initialize the xprt_if for an edge
+ * @einfo:	The edge to initialize.
+ */
+static void init_xprt_if(struct edge_info *einfo)
+{
+	einfo->xprt_if.tx_cmd_version = tx_cmd_version;
+	einfo->xprt_if.tx_cmd_version_ack = tx_cmd_version_ack;
+	einfo->xprt_if.set_version = set_version;
+	einfo->xprt_if.tx_cmd_ch_open = tx_cmd_ch_open;
+	einfo->xprt_if.tx_cmd_ch_close = tx_cmd_ch_close;
+	einfo->xprt_if.tx_cmd_ch_remote_open_ack = tx_cmd_ch_remote_open_ack;
+	einfo->xprt_if.tx_cmd_ch_remote_close_ack = tx_cmd_ch_remote_close_ack;
+	einfo->xprt_if.ssr = ssr;
+	einfo->xprt_if.allocate_rx_intent = allocate_rx_intent;
+	einfo->xprt_if.deallocate_rx_intent = deallocate_rx_intent;
+	einfo->xprt_if.tx_cmd_local_rx_intent = tx_cmd_local_rx_intent;
+	einfo->xprt_if.tx_cmd_local_rx_done = tx_cmd_local_rx_done;
+	einfo->xprt_if.tx = tx;
+	einfo->xprt_if.tx_cmd_rx_intent_req = tx_cmd_rx_intent_req;
+	einfo->xprt_if.tx_cmd_remote_rx_intent_req_ack =
+						tx_cmd_remote_rx_intent_req_ack;
+	einfo->xprt_if.tx_cmd_set_sigs = tx_cmd_set_sigs;
+	einfo->xprt_if.wait_link_down = wait_link_down;
+	einfo->xprt_if.tx_cmd_tracer_pkt = tx_cmd_tracer_pkt;
+	einfo->xprt_if.get_power_vote_ramp_time = get_power_vote_ramp_time;
+	einfo->xprt_if.power_vote = power_vote;
+	einfo->xprt_if.power_unvote = power_unvote;
+}
+
+/**
+ * init_xprt_cfg() - Initialize the xprt_cfg for an edge
+ * @einfo:	The edge to initialize.
+ * @name:	The name of the remote side this edge communicates to.
+ */
+static void init_xprt_cfg(struct edge_info *einfo, const char *name)
+{
+	einfo->xprt_cfg.name = XPRT_NAME;
+	einfo->xprt_cfg.edge = name;
+	einfo->xprt_cfg.versions = versions;
+	einfo->xprt_cfg.versions_entries = ARRAY_SIZE(versions);
+	einfo->xprt_cfg.max_cid = SZ_64K;
+	einfo->xprt_cfg.max_iid = SZ_2G;
+}
+
+/**
+ * parse_qos_dt_params() - Parse the power states from DT
+ * @dev:	Reference to the platform device for a specific edge.
+ * @einfo:	Edge information for the edge probe function is called.
+ *
+ * Return: 0 on success, standard error code otherwise.
+ */
+static int parse_qos_dt_params(struct device_node *node,
+				struct edge_info *einfo)
+{
+	int rc;
+	int i;
+	char *key;
+	uint32_t *arr32;
+	uint32_t num_states;
+
+	key = "qcom,ramp-time";
+	if (!of_find_property(node, key, &num_states))
+		return -ENODEV;
+
+	num_states /= sizeof(uint32_t);
+
+	einfo->num_pw_states = num_states;
+
+	arr32 = kmalloc_array(num_states, sizeof(uint32_t), GFP_KERNEL);
+	if (!arr32)
+		return -ENOMEM;
+
+	einfo->ramp_time_us = kmalloc_array(num_states, sizeof(unsigned long),
+					GFP_KERNEL);
+	if (!einfo->ramp_time_us) {
+		rc = -ENOMEM;
+		goto mem_alloc_fail;
+	}
+
+	rc = of_property_read_u32_array(node, key, arr32, num_states);
+	if (rc) {
+		rc = -ENODEV;
+		goto invalid_key;
+	}
+	for (i = 0; i < num_states; i++)
+		einfo->ramp_time_us[i] = arr32[i];
+
+	kfree(arr32);
+	return 0;
+
+invalid_key:
+	kfree(einfo->ramp_time_us);
+mem_alloc_fail:
+	kfree(arr32);
+	return rc;
+}
+
+static int glink_bgcom_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	struct device_node *phandle_node;
+	struct edge_info *einfo;
+	int rc;
+	char *key;
+	const char *subsys_name;
+	unsigned long flags;
+
+	node = pdev->dev.of_node;
+
+	einfo = devm_kzalloc(&pdev->dev, sizeof(*einfo), GFP_KERNEL);
+	if (!einfo) {
+		rc = -ENOMEM;
+		goto edge_info_alloc_fail;
+	}
+
+	key = "label";
+	subsys_name = of_get_property(node, key, NULL);
+	if (!subsys_name) {
+		GLINK_ERR("%s: missing key %s\n", __func__, key);
+		rc = -ENODEV;
+		goto missing_key;
+	}
+	strlcpy(einfo->subsys_name, subsys_name, sizeof(einfo->subsys_name));
+
+	init_xprt_cfg(einfo, subsys_name);
+	init_xprt_if(einfo);
+
+	kthread_init_worker(&einfo->kworker);
+	init_srcu_struct(&einfo->use_ref);
+	mutex_init(&einfo->write_lock);
+	init_waitqueue_head(&einfo->tx_blocked_queue);
+	spin_lock_init(&einfo->activity_lock);
+	mutex_init(&einfo->tx_avail_lock);
+
+	spin_lock_irqsave(&edge_infos_lock, flags);
+	list_add_tail(&einfo->list, &edge_infos);
+	spin_unlock_irqrestore(&edge_infos_lock, flags);
+
+	einfo->task = kthread_run(kthread_worker_fn, &einfo->kworker,
+				  "bgcom_%s", subsys_name);
+	if (IS_ERR(einfo->task)) {
+		rc = PTR_ERR(einfo->task);
+		GLINK_ERR("%s: kthread run failed %d\n", __func__, rc);
+		goto kthread_fail;
+	}
+
+	key = "qcom,qos-config";
+	phandle_node = of_parse_phandle(node, key, 0);
+	if (phandle_node && !(of_get_glink_core_qos_cfg(phandle_node,
+							&einfo->xprt_cfg)))
+		parse_qos_dt_params(node, einfo);
+
+	rc = glink_core_register_transport(&einfo->xprt_if, &einfo->xprt_cfg);
+	if (rc == -EPROBE_DEFER)
+		goto reg_xprt_fail;
+	if (rc) {
+		GLINK_ERR("%s: glink core register transport failed: %d\n",
+			__func__, rc);
+		goto reg_xprt_fail;
+	}
+
+	einfo->bgcom_config.priv = (void *)einfo;
+	einfo->bgcom_config.bgcom_notification_cb = glink_bgcom_event_handler;
+	einfo->bgcom_handle = NULL;
+	dev_set_drvdata(&pdev->dev, einfo);
+	if (!strcmp(einfo->xprt_cfg.edge, "bg")) {
+		einfo->bgcom_handle = bgcom_open(&einfo->bgcom_config);
+		if (!einfo->bgcom_handle) {
+			GLINK_ERR("%s: bgcom open failed\n", __func__);
+			rc = -ENODEV;
+			goto bgcom_open_fail;
+		}
+	}
+	return 0;
+
+bgcom_open_fail:
+	dev_set_drvdata(&pdev->dev, NULL);
+	glink_core_unregister_transport(&einfo->xprt_if);
+reg_xprt_fail:
+	kthread_flush_worker(&einfo->kworker);
+	kthread_stop(einfo->task);
+	einfo->task = NULL;
+kthread_fail:
+	spin_lock_irqsave(&edge_infos_lock, flags);
+	list_del(&einfo->list);
+	spin_unlock_irqrestore(&edge_infos_lock, flags);
+missing_key:
+	kfree(einfo);
+edge_info_alloc_fail:
+	return rc;
+}
+
+static int glink_bgcom_remove(struct platform_device *pdev)
+{
+	struct edge_info *einfo;
+	unsigned long flags;
+
+	einfo = (struct edge_info *)dev_get_drvdata(&pdev->dev);
+	bgcom_close(&einfo->bgcom_handle);
+	glink_core_unregister_transport(&einfo->xprt_if);
+	kthread_flush_worker(&einfo->kworker);
+	kthread_stop(einfo->task);
+	einfo->task = NULL;
+	spin_lock_irqsave(&edge_infos_lock, flags);
+	list_del(&einfo->list);
+	spin_unlock_irqrestore(&edge_infos_lock, flags);
+	return 0;
+}
+
+static int glink_bgcom_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static int glink_bgcom_suspend(struct platform_device *pdev,
+				   pm_message_t state)
+{
+	unsigned long flags;
+	struct edge_info *einfo;
+	bool suspend;
+	int rc = -EBUSY;
+
+	einfo = (struct edge_info *)dev_get_drvdata(&pdev->dev);
+	if (strcmp(einfo->xprt_cfg.edge, "bg"))
+		return 0;
+
+	spin_lock_irqsave(&einfo->activity_lock, flags);
+	suspend = !(einfo->activity_flag);
+	spin_unlock_irqrestore(&einfo->activity_lock, flags);
+	if (suspend)
+		rc = bgcom_suspend(einfo->bgcom_handle);
+	if (rc < 0)
+		GLINK_ERR("%s: Could not suspend activity_flag %d, rc %d\n",
+			__func__, einfo->activity_flag, rc);
+	return rc;
+}
+
+static const struct of_device_id bgcom_match_table[] = {
+	{ .compatible = "qcom,glink-bgcom-xprt" },
+	{},
+};
+
+static struct platform_driver glink_bgcom_driver = {
+	.probe = glink_bgcom_probe,
+	.remove = glink_bgcom_remove,
+	.resume = glink_bgcom_resume,
+	.suspend = glink_bgcom_suspend,
+	.driver = {
+		.name = "msm_glink_bgcom_xprt",
+		.owner = THIS_MODULE,
+		.of_match_table = bgcom_match_table,
+	},
+};
+
+static int __init glink_bgcom_xprt_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&glink_bgcom_driver);
+	if (rc)
+		GLINK_ERR("%s: glink_bgcom register failed %d\n", __func__, rc);
+
+	return rc;
+}
+module_init(glink_bgcom_xprt_init);
+
+static void __exit glink_bgcom_xprt_exit(void)
+{
+	platform_driver_unregister(&glink_bgcom_driver);
+}
+module_exit(glink_bgcom_xprt_exit);
+
+MODULE_DESCRIPTION("MSM G-Link bgcom Transport");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/pil_bg_intf.h b/drivers/soc/qcom/pil_bg_intf.h
new file mode 100644
index 0000000..722024b
--- /dev/null
+++ b/drivers/soc/qcom/pil_bg_intf.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 __BG_INTF_H_
+#define __BG_INTF_H_
+
+#define MAX_APP_NAME_SIZE 100
+#define RESULT_SUCCESS 0
+#define RESULT_FAILURE -1
+
+/* tzapp command list.*/
+enum bg_tz_commands {
+	BGPIL_RAMDUMP,
+	BGPIL_IMAGE_LOAD,
+	BGPIL_AUTH_MDT,
+	BGPIL_DLOAD_CONT,
+};
+
+/* tzapp bg request.*/
+__packed struct tzapp_bg_req {
+	uint8_t tzapp_bg_cmd;
+	phys_addr_t address_fw;
+	size_t size_fw;
+};
+
+/* tzapp bg response.*/
+__packed struct tzapp_bg_rsp {
+	uint32_t tzapp_bg_cmd;
+	uint32_t bg_info_len;
+	uint32_t status;
+	uint32_t bg_info[100];
+};
+
+#endif
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index a82cb43..e02bf84 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -605,6 +605,7 @@
 
 	/* SDM450 ID */
 	[338] = {MSM_CPU_SDM450, "SDM450"},
+	[351] = {MSM_CPU_SDM450, "SDA450"},
 
 	/* SDM632 ID */
 	[349] = {MSM_CPU_SDM632, "SDM632"},
diff --git a/drivers/soc/qcom/subsys-pil-bg.c b/drivers/soc/qcom/subsys-pil-bg.c
new file mode 100644
index 0000000..75c3666
--- /dev/null
+++ b/drivers/soc/qcom/subsys-pil-bg.c
@@ -0,0 +1,771 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/reboot.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/ramdump.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <linux/highmem.h>
+
+#include "peripheral-loader.h"
+#include "../../misc/qseecom_kernel.h"
+#include "pil_bg_intf.h"
+
+#define INVALID_GPIO	-1
+#define NUM_GPIOS	4
+#define SECURE_APP	"bgapp"
+#define desc_to_data(d)	container_of(d, struct pil_bg_data, desc)
+#define subsys_to_data(d) container_of(d, struct pil_bg_data, subsys_desc)
+#define BG_RAMDUMP_SZ	0x00102000
+#define BG_CRASH_IN_TWM	2
+/**
+ * struct pil_bg_data
+ * @qseecom_handle: handle of TZ app
+ * @bg_queue: private queue to schedule worker threads for bottom half
+ * @restart_work: work struct for executing ssr
+ * @reboot_blk: notification block for reboot event
+ * @subsys_desc: subsystem descriptor
+ * @subsys: subsystem device pointer
+ * @gpios: array to hold all gpio handle
+ * @desc: PIL descriptor
+ * @address_fw: address where firmware binaries loaded
+ * @ramdump_dev: ramdump device pointer
+ * @size_fw: size of bg firmware binaries
+ * @errfatal_irq: irq number to indicate bg crash or shutdown
+ * @status_irq: irq to indicate bg status
+ * @app_status: status of tz app loading
+ * @is_ready: Is BG chip up
+ * @err_ready: The error ready signal
+ */
+
+struct pil_bg_data {
+	struct qseecom_handle *qseecom_handle;
+	struct workqueue_struct *bg_queue;
+	struct work_struct restart_work;
+	struct notifier_block reboot_blk;
+	struct subsys_desc subsys_desc;
+	struct subsys_device *subsys;
+	unsigned int gpios[NUM_GPIOS];
+	int errfatal_irq;
+	int status_irq;
+	struct pil_desc desc;
+	phys_addr_t address_fw;
+	void *ramdump_dev;
+	u32 cmd_status;
+	size_t size_fw;
+	int app_status;
+	bool is_ready;
+	struct completion err_ready;
+};
+
+static irqreturn_t bg_status_change(int irq, void *dev_id);
+
+/**
+ * bg_app_shutdown_notify() - Toggle AP2BG err fatal gpio when
+ * called by SSR framework.
+ * @subsys: struct containing private BG data.
+ *
+ * Return: none.
+ */
+static void bg_app_shutdown_notify(const struct subsys_desc *subsys)
+{
+	struct pil_bg_data *bg_data = subsys_to_data(subsys);
+	/* Toggle AP2BG err fatal gpio here to inform apps err fatal event */
+	if (gpio_is_valid(bg_data->gpios[2]))
+		gpio_set_value(bg_data->gpios[2], 1);
+}
+
+/**
+ * bg_app_reboot_notify() - Toggle AP2BG err fatal gpio.
+ * @nb: struct containing private BG data.
+ *
+ * Return: NOTIFY_DONE indicating success.
+ */
+static int bg_app_reboot_notify(struct notifier_block *nb,
+		unsigned long code, void *unused)
+{
+	struct pil_bg_data *bg_data = container_of(nb,
+					struct pil_bg_data, reboot_blk);
+	/* Toggle AP2BG err fatal gpio here to inform apps err fatal event */
+	if (gpio_is_valid(bg_data->gpios[2]))
+		gpio_set_value(bg_data->gpios[2], 1);
+	return NOTIFY_DONE;
+}
+
+/**
+ * get_cmd_rsp_buffers() - Function sets cmd & rsp buffer pointers and
+ *                         aligns buffer lengths
+ * @hdl:	index of qseecom_handle
+ * @cmd:	req buffer - set to qseecom_handle.sbuf
+ * @cmd_len:	ptr to req buffer len
+ * @rsp:	rsp buffer - set to qseecom_handle.sbuf + offset
+ * @rsp_len:	ptr to rsp buffer len
+ *
+ * Return: Success always .
+ */
+static int get_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd,
+			uint32_t *cmd_len, void **rsp, uint32_t *rsp_len)
+{
+	*cmd = handle->sbuf;
+	if (*cmd_len & QSEECOM_ALIGN_MASK)
+		*cmd_len = QSEECOM_ALIGN(*cmd_len);
+
+	*rsp = handle->sbuf + *cmd_len;
+	if (*rsp_len & QSEECOM_ALIGN_MASK)
+		*rsp_len = QSEECOM_ALIGN(*rsp_len);
+
+	return 0;
+}
+
+/**
+ * pil_load_bg_tzapp() - Called to load TZ app.
+ * @pbd: struct containing private BG data.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int pil_load_bg_tzapp(struct pil_bg_data *pbd)
+{
+	int rc;
+
+	/* return success if already loaded */
+	if (pbd->qseecom_handle && !pbd->app_status)
+		return 0;
+	/* Load the APP */
+	rc = qseecom_start_app(&pbd->qseecom_handle, SECURE_APP, SZ_4K);
+	if (rc < 0) {
+		dev_err(pbd->desc.dev, "BG TZ app load failure\n");
+		pbd->app_status = RESULT_FAILURE;
+		return -EIO;
+	}
+	pbd->app_status = RESULT_SUCCESS;
+	return 0;
+}
+
+/**
+ * bgpil_tzapp_comm() - Function called to communicate with TZ APP.
+ * @req:	struct containing command and parameters.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static long bgpil_tzapp_comm(struct pil_bg_data *pbd,
+				struct tzapp_bg_req *req)
+{
+	struct tzapp_bg_req *bg_tz_req;
+	struct tzapp_bg_rsp *bg_tz_rsp;
+	int rc, req_len, rsp_len;
+
+	/* Fill command structure */
+	req_len = sizeof(struct tzapp_bg_req);
+	rsp_len = sizeof(struct tzapp_bg_rsp);
+	rc = get_cmd_rsp_buffers(pbd->qseecom_handle,
+		(void **)&bg_tz_req, &req_len,
+		(void **)&bg_tz_rsp, &rsp_len);
+	if (rc)
+		goto end;
+
+	bg_tz_req->tzapp_bg_cmd = req->tzapp_bg_cmd;
+	bg_tz_req->address_fw = req->address_fw;
+	bg_tz_req->size_fw = req->size_fw;
+	rc = qseecom_send_command(pbd->qseecom_handle,
+		(void *)bg_tz_req, req_len, (void *)bg_tz_rsp, rsp_len);
+	pr_debug("BG PIL qseecom returned with value 0x%x and status 0x%x\n",
+		rc, bg_tz_rsp->status);
+	if (rc || bg_tz_rsp->status)
+		pbd->cmd_status = bg_tz_rsp->status;
+	else
+		pbd->cmd_status = 0;
+end:
+	return rc;
+}
+
+/**
+ * wait_for_err_ready() - Called in power_up to wait for error ready.
+ * Signal waiting function.
+ * @bg_data: BG PIL private structure.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int wait_for_err_ready(struct pil_bg_data *bg_data)
+{
+	int ret;
+
+	if ((!bg_data->status_irq))
+		return 0;
+
+	ret = wait_for_completion_timeout(&bg_data->err_ready,
+			msecs_to_jiffies(10000));
+	if (!ret) {
+		pr_err("[%s]: Error ready timed out\n", bg_data->desc.name);
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+/**
+ * bg_powerup() - Called by SSR framework on userspace invocation.
+ * does load tz app and call peripheral loader.
+ * @subsys: struct containing private BG data.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int bg_powerup(const struct subsys_desc *subsys)
+{
+	struct pil_bg_data *bg_data = subsys_to_data(subsys);
+	int ret;
+
+	init_completion(&bg_data->err_ready);
+	if (!bg_data->qseecom_handle) {
+		ret = pil_load_bg_tzapp(bg_data);
+		if (ret) {
+			dev_err(bg_data->desc.dev,
+				"%s: BG TZ app load failure\n",
+				__func__);
+			return ret;
+		}
+	}
+	pr_debug("bgapp loaded\n");
+	bg_data->desc.fw_name = subsys->fw_name;
+
+	ret = devm_request_irq(bg_data->desc.dev, bg_data->status_irq,
+		bg_status_change,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+		"bg2ap_status", bg_data);
+	if (ret < 0) {
+		dev_err(bg_data->desc.dev,
+			"%s: BG2AP_STATUS IRQ#%d re registration failed, err=%d",
+			__func__, bg_data->status_irq, ret);
+			return ret;
+	}
+	disable_irq(bg_data->status_irq);
+
+	/* Enable status and err fatal irqs */
+	ret = pil_boot(&bg_data->desc);
+	if (ret) {
+		dev_err(bg_data->desc.dev,
+			"%s: BG PIL Boot failed\n", __func__);
+		return ret;
+	}
+	enable_irq(bg_data->status_irq);
+	enable_irq(bg_data->errfatal_irq);
+	ret = wait_for_err_ready(bg_data);
+	if (ret) {
+		dev_err(bg_data->desc.dev,
+			"[%s:%d]: Timed out waiting for error ready: %s!\n",
+			current->comm, current->pid, bg_data->desc.name);
+		return ret;
+	}
+	return ret;
+}
+
+/**
+ * bg_shutdown() - Called by SSR framework on userspace invocation.
+ * disable status interrupt to avoid spurious signal during PRM exit.
+ * @subsys: struct containing private BG data.
+ * @force_stop: unused
+ *
+ * Return: always success
+ */
+static int bg_shutdown(const struct subsys_desc *subsys, bool force_stop)
+{
+	struct pil_bg_data *bg_data = subsys_to_data(subsys);
+
+	disable_irq(bg_data->status_irq);
+	devm_free_irq(bg_data->desc.dev, bg_data->status_irq, bg_data);
+	disable_irq(bg_data->errfatal_irq);
+	bg_data->is_ready = false;
+	return 0;
+}
+
+/**
+ * bg_auth_metadata() - Called by Peripheral loader framework
+ * send command to tz app for authentication of metadata.
+ * @pil: pil descriptor.
+ * @metadata: metadata load address
+ * @size: size of metadata
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int bg_auth_metadata(struct pil_desc *pil,
+	const u8 *metadata, size_t size,
+	phys_addr_t addr, void *sz)
+{
+	struct pil_bg_data *bg_data = desc_to_data(pil);
+	struct tzapp_bg_req bg_tz_req;
+	void *mdata_buf;
+	dma_addr_t mdata_phys;
+	unsigned long attrs = 0;
+	struct device dev = {0};
+	int ret;
+
+	arch_setup_dma_ops(&dev, 0, 0, NULL, 0);
+
+	dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
+	attrs |= DMA_ATTR_STRONGLY_ORDERED;
+	mdata_buf = dma_alloc_attrs(&dev, size,
+			&mdata_phys, GFP_KERNEL, attrs);
+
+	if (!mdata_buf) {
+		pr_err("BG_PIL: Allocation for metadata failed.\n");
+		return -ENOMEM;
+	}
+
+	/* Make sure there are no mappings in PKMAP and fixmap */
+	kmap_flush_unused();
+	kmap_atomic_flush_unused();
+
+	memcpy(mdata_buf, metadata, size);
+
+	bg_tz_req.tzapp_bg_cmd = BGPIL_AUTH_MDT;
+	bg_tz_req.address_fw = (phys_addr_t)mdata_phys;
+	bg_tz_req.size_fw = size;
+
+	ret = bgpil_tzapp_comm(bg_data, &bg_tz_req);
+	if (ret || bg_data->cmd_status) {
+		dev_err(pil->dev,
+			"%s: BGPIL_AUTH_MDT qseecom call failed\n",
+				__func__);
+		return bg_data->cmd_status;
+	}
+	dma_free_attrs(&dev, size, mdata_buf, mdata_phys, attrs);
+	pr_debug("BG MDT Authenticated\n");
+	return 0;
+}
+
+/**
+ * bg_get_firmware_addr() - Called by Peripheral loader framework
+ * to get address and size of bg firmware binaries.
+ * @pil: pil descriptor.
+ * @addr: fw load address
+ * @size: size of fw
+ *
+ * Return: 0 on success.
+ */
+static int bg_get_firmware_addr(struct pil_desc *pil,
+					phys_addr_t addr, size_t size)
+{
+	struct pil_bg_data *bg_data = desc_to_data(pil);
+
+	bg_data->address_fw = addr;
+	bg_data->size_fw = size;
+	pr_debug("BG PIL loads firmware blobs at 0x%x with size 0x%x\n",
+		addr, size);
+	return 0;
+}
+
+
+/**
+ * bg_auth_and_xfer() - Called by Peripheral loader framework
+ * to signal tz app to authenticate and boot bg chip.
+ * @pil: pil descriptor.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int bg_auth_and_xfer(struct pil_desc *pil)
+{
+	struct pil_bg_data *bg_data = desc_to_data(pil);
+	struct tzapp_bg_req bg_tz_req;
+	int ret;
+
+	bg_tz_req.tzapp_bg_cmd = BGPIL_IMAGE_LOAD;
+	bg_tz_req.address_fw = bg_data->address_fw;
+	bg_tz_req.size_fw = bg_data->size_fw;
+
+	ret = bgpil_tzapp_comm(bg_data, &bg_tz_req);
+	if (bg_data->cmd_status == BG_CRASH_IN_TWM) {
+		/* Do ramdump and resend boot cmd */
+		bg_data->subsys_desc.ramdump(true, &bg_data->subsys_desc);
+		bg_tz_req.tzapp_bg_cmd = BGPIL_DLOAD_CONT;
+		ret = bgpil_tzapp_comm(bg_data, &bg_tz_req);
+	}
+	if (ret || bg_data->cmd_status) {
+		dev_err(pil->dev,
+			"%s: BGPIL_IMAGE_LOAD qseecom call failed\n",
+			__func__);
+		pil_free_memory(&bg_data->desc);
+		return bg_data->cmd_status;
+	}
+	/* BG Transfer of image is complete, free up the memory */
+	pr_debug("BG Firmware authentication and transfer done\n");
+	pil_free_memory(&bg_data->desc);
+	return 0;
+}
+
+/**
+ * bg_ramdump() - Called by SSR framework to save dump of BG internal
+ * memory, BG PIL does allocate region from dynamic memory and pass this
+ * region to tz to dump memory content of BG.
+ * @subsys: subsystem descriptor.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int bg_ramdump(int enable, const struct subsys_desc *subsys)
+{
+	struct pil_bg_data *bg_data = subsys_to_data(subsys);
+	struct pil_desc desc = bg_data->desc;
+	struct ramdump_segment *ramdump_segments;
+	struct tzapp_bg_req bg_tz_req;
+	phys_addr_t start_addr;
+	void *region;
+	int ret;
+	struct device dev = {0};
+
+	arch_setup_dma_ops(&dev, 0, 0, NULL, 0);
+
+	desc.attrs = 0;
+	desc.attrs |= DMA_ATTR_SKIP_ZEROING;
+	desc.attrs |= DMA_ATTR_STRONGLY_ORDERED;
+
+	region = dma_alloc_attrs(desc.dev, BG_RAMDUMP_SZ,
+				&start_addr, GFP_KERNEL, desc.attrs);
+
+	if (region == NULL) {
+		dev_dbg(desc.dev,
+			"BG PIL failure to allocate ramdump region of size %zx\n",
+			BG_RAMDUMP_SZ);
+		return -ENOMEM;
+	}
+
+	ramdump_segments = kcalloc(1, sizeof(*ramdump_segments), GFP_KERNEL);
+	if (!ramdump_segments)
+		return -ENOMEM;
+
+	bg_tz_req.tzapp_bg_cmd = BGPIL_RAMDUMP;
+	bg_tz_req.address_fw = start_addr;
+	bg_tz_req.size_fw = BG_RAMDUMP_SZ;
+
+	ret = bgpil_tzapp_comm(bg_data, &bg_tz_req);
+	if (ret || bg_data->cmd_status) {
+		dev_dbg(desc.dev, "%s: BG PIL ramdump collection failed\n",
+			__func__);
+		return bg_data->cmd_status;
+	}
+
+	ramdump_segments->address = start_addr;
+	ramdump_segments->size = BG_RAMDUMP_SZ;
+
+	do_ramdump(bg_data->ramdump_dev, ramdump_segments, 1);
+	kfree(ramdump_segments);
+	dma_free_attrs(desc.dev, BG_RAMDUMP_SZ, region,
+		       start_addr, desc.attrs);
+	return 0;
+}
+
+static struct pil_reset_ops pil_ops_trusted = {
+	.init_image = bg_auth_metadata,
+	.mem_setup =  bg_get_firmware_addr,
+	.auth_and_reset = bg_auth_and_xfer,
+	.shutdown = NULL,
+	.proxy_vote = NULL,
+	.proxy_unvote = NULL,
+};
+
+/**
+ * bg_restart_work() - scheduled by interrupt handler to carry
+ * out ssr sequence
+ * @work: work struct.
+ *
+ * Return: none.
+ */
+static void bg_restart_work(struct work_struct *work)
+{
+	struct pil_bg_data *drvdata =
+		container_of(work, struct pil_bg_data, restart_work);
+		subsystem_restart_dev(drvdata->subsys);
+}
+
+static irqreturn_t bg_errfatal(int irq, void *dev_id)
+{
+	struct pil_bg_data *drvdata = (struct pil_bg_data *)dev_id;
+
+	if (!drvdata)
+		return IRQ_HANDLED;
+
+	dev_dbg(drvdata->desc.dev, "BG s/w err fatal\n");
+
+	queue_work(drvdata->bg_queue, &drvdata->restart_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bg_status_change(int irq, void *dev_id)
+{
+	bool value;
+	struct pil_bg_data *drvdata = (struct pil_bg_data *)dev_id;
+
+	if (!drvdata)
+		return IRQ_HANDLED;
+
+	value = gpio_get_value(drvdata->gpios[0]);
+	if (value == true && !drvdata->is_ready) {
+		dev_info(drvdata->desc.dev,
+			"BG services are up and running: irq state changed 0->1\n");
+		drvdata->is_ready = true;
+		complete(&drvdata->err_ready);
+	} else if (value == false && drvdata->is_ready) {
+		dev_err(drvdata->desc.dev,
+			"BG got unexpected reset: irq state changed 1->0\n");
+			drvdata->is_ready = false;
+		queue_work(drvdata->bg_queue, &drvdata->restart_work);
+	} else {
+		dev_err(drvdata->desc.dev,
+			"BG status irq: unknown status\n");
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * setup_bg_gpio_irq() - called in probe to configure input/
+ * output gpio.
+ * @drvdata: private data struct for BG.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int setup_bg_gpio_irq(struct platform_device *pdev,
+					struct pil_bg_data *drvdata)
+{
+	int ret = -1;
+	int irq, i;
+
+	if (gpio_request(drvdata->gpios[0], "BG2AP_STATUS")) {
+		dev_err(&pdev->dev,
+			"%s Failed to configure BG2AP_STATUS gpio\n",
+				__func__);
+		goto err;
+	}
+	if (gpio_request(drvdata->gpios[1], "BG2AP_ERRFATAL")) {
+		dev_err(&pdev->dev,
+			"%s Failed to configure BG2AP_ERRFATAL gpio\n",
+				__func__);
+		goto err;
+	}
+	gpio_direction_input(drvdata->gpios[0]);
+	gpio_direction_input(drvdata->gpios[1]);
+	/* BG2AP STATUS IRQ */
+	irq = gpio_to_irq(drvdata->gpios[0]);
+	if (irq < 0) {
+		dev_err(&pdev->dev,
+		"%s: bad BG2AP_STATUS IRQ resource, err = %d\n",
+			__func__, irq);
+		goto err;
+	}
+
+	drvdata->status_irq = irq;
+	/* BG2AP ERR_FATAL irq. */
+	irq = gpio_to_irq(drvdata->gpios[1]);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "bad BG2AP_ERRFATAL IRQ resource\n");
+		goto err;
+	}
+	ret = request_irq(irq, bg_errfatal,
+		IRQF_TRIGGER_RISING | IRQF_ONESHOT, "bg2ap_errfatal", drvdata);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"%s: BG2AP_ERRFATAL IRQ#%d request failed,\n",
+				__func__, irq);
+		goto err;
+	}
+	drvdata->errfatal_irq = irq;
+	enable_irq(drvdata->errfatal_irq);
+	/* Configure outgoing GPIO's */
+	if (gpio_request(drvdata->gpios[2], "AP2BG_ERRFATAL")) {
+		dev_err(&pdev->dev,
+			"%s Failed to configure AP2BG_ERRFATAL gpio\n",
+				__func__);
+		goto err;
+	}
+	if (gpio_request(drvdata->gpios[3], "AP2BG_STATUS")) {
+		dev_err(&pdev->dev,
+			"%s Failed to configure AP2BG_STATUS gpio\n",
+				__func__);
+		goto err;
+	}
+	/*
+	 * Put status gpio in default high state which will
+	 * make transition to low on any sudden reset case of msm
+	 */
+	gpio_direction_output(drvdata->gpios[2], 0);
+	gpio_direction_output(drvdata->gpios[3], 1);
+	/* Inform BG that AP is up */
+	gpio_set_value(drvdata->gpios[3], 1);
+	return 0;
+err:
+	for (i = 0; i < NUM_GPIOS; ++i) {
+		if (gpio_is_valid(drvdata->gpios[i]))
+			gpio_free(drvdata->gpios[i]);
+	}
+	return ret;
+}
+
+/**
+ * bg_dt_parse_gpio() - called in probe to parse gpio's
+ * @drvdata: private data struct for BG.
+ *
+ * Return: 0 on success. Error code on failure.
+ */
+static int bg_dt_parse_gpio(struct platform_device *pdev,
+				struct pil_bg_data *drvdata)
+{
+	int i, val;
+
+	for (i = 0; i < NUM_GPIOS; i++)
+		drvdata->gpios[i] = INVALID_GPIO;
+	val = of_get_named_gpio(pdev->dev.of_node,
+					"qcom,bg2ap-status-gpio", 0);
+	if (val >= 0)
+		drvdata->gpios[0] = val;
+	else {
+		pr_err("BG status gpio not found, error=%d\n", val);
+		return -EINVAL;
+	}
+	val = of_get_named_gpio(pdev->dev.of_node,
+					"qcom,bg2ap-errfatal-gpio", 0);
+	if (val >= 0)
+		drvdata->gpios[1] = val;
+	else {
+		pr_err("BG err-fatal gpio not found, error=%d\n", val);
+		return -EINVAL;
+	}
+	val = of_get_named_gpio(pdev->dev.of_node,
+					"qcom,ap2bg-errfatal-gpio", 0);
+	if (val >= 0)
+		drvdata->gpios[2] = val;
+	else {
+		pr_err("ap2bg err-fatal gpio not found, error=%d\n", val);
+		return -EINVAL;
+	}
+	val = of_get_named_gpio(pdev->dev.of_node,
+					"qcom,ap2bg-status-gpio", 0);
+	if (val >= 0)
+		drvdata->gpios[3] = val;
+	else {
+		pr_err("ap2bg status gpio not found, error=%d\n", val);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int pil_bg_driver_probe(struct platform_device *pdev)
+{
+	struct pil_bg_data *bg_data;
+	int rc;
+
+	bg_data = devm_kzalloc(&pdev->dev, sizeof(*bg_data), GFP_KERNEL);
+	if (!bg_data)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, bg_data);
+	rc = of_property_read_string(pdev->dev.of_node,
+			"qcom,firmware-name", &bg_data->desc.name);
+	if (rc)
+		return rc;
+	bg_data->desc.dev = &pdev->dev;
+	bg_data->desc.owner = THIS_MODULE;
+	bg_data->desc.ops = &pil_ops_trusted;
+	rc = pil_desc_init(&bg_data->desc);
+	if (rc)
+		return rc;
+	/* Read gpio configuration */
+	rc = bg_dt_parse_gpio(pdev, bg_data);
+	if (rc)
+		return rc;
+	rc = setup_bg_gpio_irq(pdev, bg_data);
+	if (rc < 0)
+		return rc;
+	bg_data->subsys_desc.name = bg_data->desc.name;
+	bg_data->subsys_desc.owner = THIS_MODULE;
+	bg_data->subsys_desc.dev = &pdev->dev;
+	bg_data->subsys_desc.shutdown = bg_shutdown;
+	bg_data->subsys_desc.powerup = bg_powerup;
+	bg_data->subsys_desc.ramdump = bg_ramdump;
+	bg_data->subsys_desc.free_memory = NULL;
+	bg_data->subsys_desc.crash_shutdown = bg_app_shutdown_notify;
+	bg_data->ramdump_dev =
+		create_ramdump_device(bg_data->subsys_desc.name, &pdev->dev);
+	if (!bg_data->ramdump_dev) {
+		rc = -ENOMEM;
+		goto err_ramdump;
+	}
+	bg_data->subsys = subsys_register(&bg_data->subsys_desc);
+	if (IS_ERR(bg_data->subsys)) {
+		rc = PTR_ERR(bg_data->subsys);
+		goto err_subsys;
+	}
+
+	bg_data->reboot_blk.notifier_call = bg_app_reboot_notify;
+	register_reboot_notifier(&bg_data->reboot_blk);
+
+	bg_data->bg_queue = alloc_workqueue("bg_queue", 0, 0);
+	if (!bg_data->bg_queue) {
+		dev_err(&pdev->dev, "could not create bg_queue\n");
+		subsys_unregister(bg_data->subsys);
+		goto err_subsys;
+	}
+	INIT_WORK(&bg_data->restart_work, bg_restart_work);
+	return 0;
+err_subsys:
+	destroy_ramdump_device(bg_data->ramdump_dev);
+err_ramdump:
+	pil_desc_release(&bg_data->desc);
+	return rc;
+}
+
+static int pil_bg_driver_exit(struct platform_device *pdev)
+{
+	struct pil_bg_data *bg_data = platform_get_drvdata(pdev);
+
+	subsys_unregister(bg_data->subsys);
+	destroy_ramdump_device(bg_data->ramdump_dev);
+	pil_desc_release(&bg_data->desc);
+
+	return 0;
+}
+
+const struct of_device_id pil_bg_match_table[] = {
+	{.compatible = "qcom,pil-blackghost"},
+	{}
+};
+
+static struct platform_driver pil_bg_driver = {
+	.probe = pil_bg_driver_probe,
+	.remove = pil_bg_driver_exit,
+	.driver = {
+		.name = "subsys-pil-bg",
+		.of_match_table = pil_bg_match_table,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pil_bg_init(void)
+{
+	return platform_driver_register(&pil_bg_driver);
+}
+module_init(pil_bg_init);
+
+static void __exit pil_bg_exit(void)
+{
+	platform_driver_unregister(&pil_bg_driver);
+}
+module_exit(pil_bg_exit);
+
+MODULE_DESCRIPTION("Support for booting QTI Blackghost SoC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/tsens-dbg.c b/drivers/thermal/tsens-dbg.c
index e1fc6b9..9f128fe 100644
--- a/drivers/thermal/tsens-dbg.c
+++ b/drivers/thermal/tsens-dbg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -190,17 +190,34 @@
 	__ATTR(zonehist, 0644, zonehist_show, zonehist_store),
 };
 
+static struct device_attribute tsens_mtc_dev_attr_V14[] = {
+	__ATTR(zonemask, 0644, zonemask_show, zonemask_store),
+	__ATTR(zonelog, 0644, zonelog_show, zonelog_store),
+};
+
 static int tsens_dbg_mtc_data(struct tsens_device *data,
 					u32 id, u32 dbg_type, int *val)
 {
 	int result = 0, i;
 	struct tsens_device *tmdev = NULL;
 	struct device_attribute *attr_ptr = NULL;
+	u32 ver_major;
+	u32 ver_minor;
+	u32 num_elem;
 
-	attr_ptr = tsens_mtc_dev_attr;
 	tmdev = data;
+	ver_major = tmdev->ctrl_data->ver_major;
+	ver_minor = tmdev->ctrl_data->ver_minor;
 
-	for (i = 0; i < ARRAY_SIZE(tsens_mtc_dev_attr); i++) {
+	if (ver_major == 1 && ver_minor == 4) {
+		attr_ptr = tsens_mtc_dev_attr_V14;
+		num_elem = ARRAY_SIZE(tsens_mtc_dev_attr_V14);
+	} else {
+		attr_ptr = tsens_mtc_dev_attr;
+		num_elem = ARRAY_SIZE(tsens_mtc_dev_attr);
+	}
+
+	for (i = 0; i < num_elem; i++) {
 		result = device_create_file(&tmdev->pdev->dev, &attr_ptr[i]);
 		if (result < 0)
 			goto error;
diff --git a/drivers/thermal/tsens-mtc.c b/drivers/thermal/tsens-mtc.c
index 451c6c7..6e43533 100644
--- a/drivers/thermal/tsens-mtc.c
+++ b/drivers/thermal/tsens-mtc.c
@@ -76,6 +76,8 @@
 	unsigned int reg_cntl;
 	void __iomem *sensor_addr;
 	struct tsens_device *tmdev = NULL;
+	u32 ver_major;
+	u32 ver_minor;
 
 	if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
 		return -EINVAL;
@@ -86,8 +88,16 @@
 		return -EPROBE_DEFER;
 	}
 
-	sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR
-					(tmdev->tsens_tm_addr);
+	ver_major = tmdev->ctrl_data->ver_major;
+	ver_minor = tmdev->ctrl_data->ver_minor;
+
+	if (ver_major == 1 && ver_minor == 4) {
+		sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR_V14
+			(tmdev->tsens_tm_addr);
+	} else {
+		sensor_addr = TSENS_TM_MTC_ZONE0_SW_MASK_ADDR
+			(tmdev->tsens_tm_addr);
+	}
 
 	if (th1_enable && th2_enable)
 		writel_relaxed(TSENS_MTC_IN_EFFECT,
@@ -120,6 +130,8 @@
 	int *zlog = (int *)zone_log;
 	void __iomem *sensor_addr;
 	struct tsens_device *tmdev = NULL;
+	u32 ver_major;
+	u32 ver_minor;
 
 	if (zone > TSENS_NUM_MTC_ZONES_SUPPORT)
 		return -EINVAL;
@@ -130,8 +142,13 @@
 		return -EPROBE_DEFER;
 	}
 
-	sensor_addr = TSENS_TM_MTC_ZONE0_LOG(tmdev->tsens_tm_addr);
+	ver_major = tmdev->ctrl_data->ver_major;
+	ver_minor = tmdev->ctrl_data->ver_minor;
 
+	if (ver_major == 1 && ver_minor == 4)
+		sensor_addr = TSENS_TM_MTC_ZONE0_LOG_V14(tmdev->tsens_tm_addr);
+	else
+		sensor_addr = TSENS_TM_MTC_ZONE0_LOG(tmdev->tsens_tm_addr);
 	reg_cntl = readl_relaxed((sensor_addr +
 				(zone * TSENS_SN_ADDR_OFFSET)));
 	is_valid = (reg_cntl & TSENS_LOGS_VALID_MASK)
diff --git a/drivers/thermal/tsens-mtc.h b/drivers/thermal/tsens-mtc.h
index 8782728..b3a2c8e 100644
--- a/drivers/thermal/tsens-mtc.h
+++ b/drivers/thermal/tsens-mtc.h
@@ -15,6 +15,9 @@
 #define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR(n)      ((n) + 0x140)
 #define TSENS_TM_MTC_ZONE0_LOG(n)               ((n) + 0x150)
 #define TSENS_TM_MTC_ZONE0_HISTORY(n)           ((n) + 0x160)
+#define TSENS_TM_MTC_ZONE0_SW_MASK_ADDR_V14(n)  ((n) + 0xC0)
+#define TSENS_TM_MTC_ZONE0_LOG_V14(n)           ((n) + 0xD0)
+
 #define TSENS_SN_ADDR_OFFSET             0x4
 #define TSENS_RESET_HISTORY_MASK        0x4
 #define TSENS_ZONEMASK_PARAMS           3
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index 885b15c..730d124 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -127,6 +127,8 @@
 	u32				wd_bark_mask;
 	bool				mtc;
 	bool				valid_status_check;
+	u32				ver_major;
+	u32				ver_minor;
 };
 
 struct tsens_mtc_sysfs {
diff --git a/drivers/thermal/tsens1xxx.c b/drivers/thermal/tsens1xxx.c
index 0ea4a46..d63fcd1 100644
--- a/drivers/thermal/tsens1xxx.c
+++ b/drivers/thermal/tsens1xxx.c
@@ -306,6 +306,10 @@
 	*temp = code_to_degc(last_temp, sensor);
 	*temp = *temp * TSENS_SCALE_MILLIDEG;
 
+	if (tmdev->ops->dbg)
+		tmdev->ops->dbg(tmdev, (u32)sensor->hw_id,
+				TSENS_DBG_LOG_TEMP_READS, temp);
+
 	return 0;
 }
 
@@ -565,6 +569,9 @@
 	/* Disable monitoring sensor trip threshold for triggered sensor */
 	mb();
 
+	if (tm->ops->dbg)
+		tm->ops->dbg(tm, 0, TSENS_DBG_LOG_INTERRUPT_TIMESTAMP, NULL);
+
 	return IRQ_HANDLED;
 }
 
@@ -600,6 +607,11 @@
 
 	spin_lock_init(&tmdev->tsens_upp_low_lock);
 
+	if (tmdev->ctrl_data->mtc) {
+		if (tmdev->ops->dbg)
+			tmdev->ops->dbg(tmdev, 0, TSENS_DBG_MTC_DATA, NULL);
+	}
+
 	return 0;
 }
 
@@ -646,13 +658,16 @@
 	.hw_init		= tsens1xxx_hw_init,
 	.get_temp		= tsens1xxx_get_temp,
 	.set_trips		= tsens1xxx_set_trip_temp,
-	.interrupts_reg		= tsens1xxx_register_interrupts,
+	.interrupts_reg	= tsens1xxx_register_interrupts,
 	.sensor_en		= tsens1xxx_hw_sensor_en,
 	.calibrate		= calibrate_8937,
+	.dbg            = tsens2xxx_dbg,
 };
 
 const struct tsens_data data_tsens14xx = {
-	.ops			= &ops_tsens1xxx,
-	.valid_status_check	= true,
-	.mtc			= true,
+	.ops = &ops_tsens1xxx,
+	.valid_status_check = true,
+	.mtc = true,
+	.ver_major = 1,
+	.ver_minor = 4,
 };
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 148568b..f76b3db 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -96,6 +96,7 @@
 /* UART S_CMD OP codes */
 #define UART_START_READ		(0x1)
 #define UART_PARAM		(0x1)
+#define UART_PARAM_RFR_OPEN		(BIT(7))
 
 /* UART DMA Rx GP_IRQ_BITS */
 #define UART_DMA_RX_PARITY_ERR	BIT(5)
@@ -613,6 +614,27 @@
 	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
 }
 
+static void msm_geni_serial_complete_rx_eot(struct uart_port *uport)
+{
+	int poll_done = 0, tries = 0;
+	u32 geni_status = 0;
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	do {
+		poll_done = msm_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT,
+								RX_EOT, true);
+		tries++;
+	} while (!poll_done && tries < 5);
+
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+
+	if (!poll_done)
+		IPC_LOG_MSG(port->ipc_log_misc, "%s: RX_EOT, GENI:0x%x\n",
+							__func__, geni_status);
+	else
+		geni_write_reg_nolog(RX_EOT, uport->membase, SE_DMA_RX_IRQ_CLR);
+}
+
 #ifdef CONFIG_CONSOLE_POLL
 static int msm_geni_serial_get_char(struct uart_port *uport)
 {
@@ -999,12 +1021,14 @@
 	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 	int ret;
+	u32 geni_se_param = UART_PARAM_RFR_OPEN;
 
 	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
 	if (geni_status & S_GENI_CMD_ACTIVE)
 		msm_geni_serial_stop_rx(uport);
 
-	geni_setup_s_cmd(uport->membase, UART_START_READ, 0);
+	/* Start RX with the RFR_OPEN to keep RFR in always ready state */
+	geni_setup_s_cmd(uport->membase, UART_START_READ, geni_se_param);
 
 	if (port->xfer_mode == FIFO_MODE) {
 		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
@@ -1078,7 +1102,7 @@
 	unsigned int geni_m_irq_en;
 	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
-	u32 irq_clear = S_CMD_DONE_EN;
+	u32 irq_clear = S_CMD_CANCEL_EN;
 	bool done;
 
 	IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
@@ -1100,22 +1124,31 @@
 	/* Possible stop rx is called multiple times. */
 	if (!(geni_status & S_GENI_CMD_ACTIVE))
 		goto exit_rx_seq;
+
 	geni_cancel_s_cmd(uport->membase);
 	/*
 	 * Ensure that the cancel goes through before polling for the
 	 * cancel control bit.
 	 */
 	mb();
+	msm_geni_serial_complete_rx_eot(uport);
 	done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
 					S_GENI_CMD_CANCEL, false);
-	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
-	if (!done)
+	if (done) {
+		geni_write_reg_nolog(irq_clear, uport->membase,
+						SE_GENI_S_IRQ_CLEAR);
+		goto exit_rx_seq;
+	} else {
 		IPC_LOG_MSG(port->ipc_log_misc, "%s Cancel fail 0x%x\n",
-							__func__, geni_status);
+						__func__, geni_status);
+	}
 
-	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
-	if ((geni_status & S_GENI_CMD_ACTIVE))
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+	if ((geni_status & S_GENI_CMD_ACTIVE)) {
+		IPC_LOG_MSG(port->ipc_log_misc, "%s:Abort Rx, GENI:0x%x\n",
+						__func__, geni_status);
 		msm_geni_serial_abort_rx(uport);
+	}
 exit_rx_seq:
 	if (port->xfer_mode == SE_DMA && port->rx_dma) {
 		msm_geni_serial_rx_fsm_rst(uport);
@@ -1693,6 +1726,9 @@
 		ret = -ENXIO;
 		goto exit_startup;
 	}
+	IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: FW Ver:0x%x%x\n",
+		__func__,
+		get_se_m_fw(uport->membase), get_se_s_fw(uport->membase));
 
 	get_tx_fifo_size(msm_port);
 	if (!msm_port->port_setup) {
@@ -2525,7 +2561,6 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	struct msm_geni_serial_port *port = platform_get_drvdata(pdev);
 	int ret = 0;
-	u32 uart_manual_rfr = 0;
 	u32 geni_status = geni_read_reg_nolog(port->uport.membase,
 							SE_GENI_STATUS);
 
@@ -2537,28 +2572,8 @@
 	 * Resources off
 	 */
 	disable_irq(port->uport.irq);
-	/*
-	 * If the clients haven't done a manual flow on/off then go ahead and
-	 * set this to manual flow on.
-	 */
-	if (!port->manual_flow) {
-		u32 geni_ios;
-
-		uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_READY);
-		geni_write_reg_nolog(uart_manual_rfr, port->uport.membase,
-							SE_UART_MANUAL_RFR);
-		/*
-		 * Ensure that the manual flow on writes go through before
-		 * doing a stop_rx else we could end up flowing off the peer.
-		 */
-		mb();
-		geni_ios = geni_read_reg_nolog(port->uport.membase,
-								SE_GENI_IOS);
-		IPC_LOG_MSG(port->ipc_log_pwr, "%s: Manual Flow ON 0x%x 0x%x\n",
-					 __func__, uart_manual_rfr, geni_ios);
-		udelay(10);
-	}
 	stop_rx_sequencer(&port->uport);
+	geni_status = geni_read_reg_nolog(port->uport.membase, SE_GENI_STATUS);
 	if ((geni_status & M_GENI_CMD_ACTIVE))
 		stop_tx_sequencer(&port->uport);
 	ret = se_geni_resources_off(&port->serial_rsc);
@@ -2603,9 +2618,6 @@
 		goto exit_runtime_resume;
 	}
 	start_rx_sequencer(&port->uport);
-	if (!port->manual_flow)
-		geni_write_reg_nolog(0, port->uport.membase,
-						SE_UART_MANUAL_RFR);
 	/* Ensure that the Rx is running before enabling interrupts */
 	mb();
 	if (pm_runtime_enabled(dev))
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 3650b98..989956d 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -983,13 +983,17 @@
 
 	gsi_dbl_address_lsb = devm_ioremap_nocache(mdwc->dev,
 				request->db_reg_phs_addr_lsb, sizeof(u32));
-	if (!gsi_dbl_address_lsb)
-		dev_dbg(mdwc->dev, "Failed to get GSI DBL address LSB\n");
+	if (!gsi_dbl_address_lsb) {
+		dev_err(mdwc->dev, "Failed to get GSI DBL address LSB\n");
+		return;
+	}
 
 	gsi_dbl_address_msb = devm_ioremap_nocache(mdwc->dev,
 			request->db_reg_phs_addr_msb, sizeof(u32));
-	if (!gsi_dbl_address_msb)
-		dev_dbg(mdwc->dev, "Failed to get GSI DBL address MSB\n");
+	if (!gsi_dbl_address_msb) {
+		dev_err(mdwc->dev, "Failed to get GSI DBL address MSB\n");
+		return;
+	}
 
 	offset = dwc3_trb_dma_offset(dep, &dep->trb_pool[num_trbs-1]);
 	dev_dbg(mdwc->dev, "Writing link TRB addr: %pa to %pK (%x) for ep:%s\n",
@@ -4096,7 +4100,7 @@
 
 	usb_speed = (event == 0 ? USB_SPEED_HIGH : USB_SPEED_SUPER);
 	if (dwc->maximum_speed == usb_speed)
-		goto err;
+		return 0;
 
 	dbg_event(0xFF, "fw_restarthost", 0);
 	flush_delayed_work(&mdwc->sm_work);
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 10d737f..939c219 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -3474,6 +3474,7 @@
 		if (udc->udc_driver->notify_event)
 			udc->udc_driver->notify_event(udc,
 				CI13XXX_CONTROLLER_DISCONNECT_EVENT);
+		usb_gadget_set_state(&udc->gadget, USB_STATE_NOTATTACHED);
 	}
 
 	return 0;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 47f1a8e..7897f68 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1404,7 +1404,7 @@
 		*to = p->data;
 	}
 
-	ffs_log("enter");
+	ffs_log("exit");
 
 	return res;
 }
@@ -3633,6 +3633,8 @@
 	if (ffs->func) {
 		ffs_func_eps_disable(ffs->func);
 		ffs->func = NULL;
+		/* matching put to allow LPM on disconnect */
+		usb_gadget_autopm_put_async(ffs->gadget);
 	}
 
 	if (ffs->state == FFS_DEACTIVATED) {
@@ -3665,13 +3667,9 @@
 
 static void ffs_func_disable(struct usb_function *f)
 {
-	struct ffs_function *func = ffs_func_from_usb(f);
-	struct ffs_data *ffs = func->ffs;
-
 	ffs_log("enter");
 	ffs_func_set_alt(f, 0, (unsigned)-1);
-	/* matching put to allow LPM on disconnect */
-	usb_gadget_autopm_put_async(ffs->gadget);
+
 	ffs_log("exit");
 }
 
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 39ac337..c6e64fd 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -856,6 +856,7 @@
 
 	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
 };
 
 static struct usb_endpoint_descriptor mbim_gsi_fs_out_desc = {
@@ -864,6 +865,7 @@
 
 	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
 };
 
 static struct usb_descriptor_header *mbim_gsi_fs_function[] = {
@@ -1157,6 +1159,7 @@
 
 	.bEndpointAddress =	USB_DIR_IN,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(ECM_QC_STATUS_BYTECOUNT),
 };
 
 static struct usb_endpoint_descriptor ecm_gsi_fs_out_desc = {
@@ -1165,6 +1168,7 @@
 
 	.bEndpointAddress =	USB_DIR_OUT,
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(ECM_QC_STATUS_BYTECOUNT),
 };
 
 static struct usb_descriptor_header *ecm_gsi_fs_function[] = {
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 56b2a6d..f68f413 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1818,6 +1818,7 @@
 	pd->num_svids = 0;
 	kfree(pd->vdm_tx);
 	pd->vdm_tx = NULL;
+	pd->ss_lane_svid = 0x0;
 }
 
 static void dr_swap(struct usbpd *pd)
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index 4a844e9..589f2df 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -1738,12 +1738,15 @@
 		break;
 	case metadata_op_get_ion_fd:
 		if (mfd->fb_ion_handle && mfd->fb_ion_client) {
+			get_dma_buf(mfd->fbmem_buf);
 			metadata->data.fbmem_ionfd =
 				ion_share_dma_buf_fd(mfd->fb_ion_client,
 					mfd->fb_ion_handle);
-			if (metadata->data.fbmem_ionfd < 0)
+			if (metadata->data.fbmem_ionfd < 0) {
+				dma_buf_put(mfd->fbmem_buf);
 				pr_err("fd allocation failed. fd = %d\n",
-						metadata->data.fbmem_ionfd);
+					metadata->data.fbmem_ionfd);
+			}
 		}
 		break;
 	default:
diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h
index 2c22b9f..2310989 100644
--- a/include/linux/input/ft5x06_ts.h
+++ b/include/linux/input/ft5x06_ts.h
@@ -22,8 +22,30 @@
 #define FT5X16_ID		0x0A
 #define FT5X36_ID		0x14
 #define FT6X06_ID		0x06
+#define FT6X36_ID       0x36
+
+struct fw_upgrade_info {
+	bool auto_cal;
+	u16 delay_aa;
+	u16 delay_55;
+	u8 upgrade_id_1;
+	u8 upgrade_id_2;
+	u16 delay_readid;
+	u16 delay_erase_flash;
+};
+
+struct ft5x06_gesture_platform_data {
+	int gesture_enable_to_set;	/* enable/disable gesture */
+	int in_pocket;	/* whether in pocket mode or not */
+	struct device *dev;
+	struct class *gesture_class;
+	struct ft5x06_ts_data *data;
+};
 
 struct ft5x06_ts_platform_data {
+	struct fw_upgrade_info info;
+	const char *name;
+	const char *fw_name;
 	u32 irqflags;
 	u32 irq_gpio;
 	u32 irq_gpio_flags;
@@ -38,8 +60,16 @@
 	u32 panel_miny;
 	u32 panel_maxx;
 	u32 panel_maxy;
+	u32 group_id;
+	u32 hard_rst_dly;
+	u32 soft_rst_dly;
+	u32 num_max_touches;
+	bool fw_vkey_support;
 	bool no_force_update;
 	bool i2c_pull_up;
+	bool ignore_id_check;
+	bool gesture_support;
+	bool resume_in_workqueue;
 	int (*power_init)(bool);
 	int (*power_on)(bool);
 };
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index b59d548..4331b83 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -288,6 +288,7 @@
 	POWER_SUPPLY_PROP_MANUFACTURER,
 	POWER_SUPPLY_PROP_SERIAL_NUMBER,
 	POWER_SUPPLY_PROP_BATTERY_TYPE,
+	POWER_SUPPLY_PROP_CYCLE_COUNTS,
 };
 
 enum power_supply_type {
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 2c6c511..6c0168b 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -47,6 +47,29 @@
 	PWMF_EXPORTED = 1 << 1,
 };
 
+/**
+ * enum pwm_output_type - output type of the PWM signal
+ * @PWM_OUTPUT_FIXED: PWM output is fixed until a change request
+ * @PWM_OUTPUT_MODULATED: PWM output is modulated in hardware
+ * autonomously with a predefined pattern
+ */
+enum pwm_output_type {
+	PWM_OUTPUT_FIXED = 1 << 0,
+	PWM_OUTPUT_MODULATED = 1 << 1,
+};
+
+/**
+ * struct pwm_output_pattern - PWM duty pattern for MODULATED duty type
+ * @duty_pattern: PWM duty cycles in the pattern for duty modulation
+ * @num_entries: number of entries in the pattern
+ * @cycles_per_duty: number of PWM period cycles an entry stays at
+ */
+struct pwm_output_pattern {
+	unsigned int *duty_pattern;
+	unsigned int num_entries;
+	unsigned int cycles_per_duty;
+};
+
 /*
  * struct pwm_state - state of a PWM channel
  * @period: PWM period (in nanoseconds)
@@ -58,6 +81,8 @@
 	unsigned int period;
 	unsigned int duty_cycle;
 	enum pwm_polarity polarity;
+	enum pwm_output_type output_type;
+	struct pwm_output_pattern *output_pattern;
 	bool enabled;
 };
 
@@ -143,6 +168,26 @@
 	return state.polarity;
 }
 
+static inline enum pwm_output_type pwm_get_output_type(
+		const struct pwm_device *pwm)
+{
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+
+	return state.output_type;
+}
+
+static inline struct pwm_output_pattern *pwm_get_output_pattern(
+				struct pwm_device *pwm)
+{
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+
+	return pwm->state.output_pattern ?: NULL;
+}
+
 static inline void pwm_get_args(const struct pwm_device *pwm,
 				struct pwm_args *args)
 {
@@ -246,6 +291,9 @@
  * @capture: capture and report PWM signal
  * @enable: enable PWM output toggling
  * @disable: disable PWM output toggling
+ * @get_output_type_supported: get the supported output type
+ * @set_output_type: set PWM output type
+ * @set_output_pattern: set the pattern for the modulated output
  * @apply: atomically apply a new PWM config. The state argument
  *	   should be adjusted with the real hardware config (if the
  *	   approximate the period or duty_cycle value, state should
@@ -267,6 +315,13 @@
 		       struct pwm_capture *result, unsigned long timeout);
 	int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
 	void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
+	int (*get_output_type_supported)(struct pwm_chip *chip,
+			struct pwm_device *pwm);
+	int (*set_output_type)(struct pwm_chip *chip, struct pwm_device *pwm,
+			enum pwm_output_type output_type);
+	int (*set_output_pattern)(struct pwm_chip *chip,
+			struct pwm_device *pwm,
+			struct pwm_output_pattern *output_pattern);
 	int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
 		     struct pwm_state *state);
 	void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -323,6 +378,21 @@
 int pwm_adjust_config(struct pwm_device *pwm);
 
 /**
+ * pwm_output_type_support()
+ * @pwm: PWM device
+ *
+ * Returns:  output types supported by the PWM device
+ */
+static inline int pwm_get_output_type_supported(struct pwm_device *pwm)
+{
+	if (pwm->chip->ops->get_output_type_supported != NULL)
+		return pwm->chip->ops->
+			get_output_type_supported(pwm->chip, pwm);
+	else
+		return PWM_OUTPUT_FIXED;
+}
+
+/**
  * pwm_config() - change a PWM device configuration
  * @pwm: PWM device
  * @duty_ns: "on" time (in nanoseconds)
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index fc02ece..b8dd63a 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -154,6 +154,7 @@
 /* FW_REVISION_RO fields */
 #define FW_REV_PROTOCOL_MSK	(GENMASK(15, 8))
 #define FW_REV_PROTOCOL_SHFT	(8)
+#define FW_REV_VERSION_MSK	(GENMASK(7, 0))
 
 /* GENI_CLK_SEL fields */
 #define CLK_SEL_MSK		(GENMASK(2, 0))
@@ -405,6 +406,22 @@
 int get_se_proto(void __iomem *base);
 
 /**
+ * get_se_m_fw() - Read the Firmware ver for the Main seqeuncer engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * Return:	Firmware version for the Main seqeuncer engine
+ */
+int get_se_m_fw(void __iomem *base);
+
+/**
+ * get_se_s_fw() - Read the Firmware ver for the Secondry seqeuncer engine
+ * @base:	Base address of the serial engine's register block.
+ *
+ * Return:	Firmware version for the Secondry seqeuncer engine
+ */
+int get_se_s_fw(void __iomem *base);
+
+/**
  * geni_se_init() - Initialize the GENI Serial Engine
  * @base:	Base address of the serial engine's register block.
  * @rx_wm:	Receive watermark to be configured.
diff --git a/include/linux/qpnp/qpnp-misc.h b/include/linux/qpnp/qpnp-misc.h
index 7d95bf2..a7a5616 100644
--- a/include/linux/qpnp/qpnp-misc.h
+++ b/include/linux/qpnp/qpnp-misc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2017-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,11 @@
 
 #include <linux/errno.h>
 
+enum twm_state {
+	PMIC_TWM_CLEAR,
+	PMIC_TWM_ENABLE,
+};
+
 #ifdef CONFIG_QPNP_MISC
 /**
  * qpnp_misc_irqs_available - check if IRQs are available
@@ -42,6 +47,25 @@
  */
 
 int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val);
+/**
+ * qpnp_misc_twm_notifier_register - register to the twm mode notifier
+ *
+ * @nb: pointer to the client's notifier handle
+ *
+ * This function returns 0 if the client is successfuly added to the
+ * notifer list.
+ */
+int qpnp_misc_twm_notifier_register(struct notifier_block *nb);
+
+/**
+ * qpnp_misc_twm_notifier_unregister - unregister to the twm mode notifier
+ *
+ * @nb: pointer to the client's notifier handle
+ *
+ * This function returns 0 if the client is successfuly removed from the
+ * notifer list.
+ */
+int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb);
 #else
 static inline int qpnp_misc_irqs_available(struct device *consumer_dev)
 {
@@ -52,5 +76,13 @@
 {
 	return 0;
 }
+static inline int qpnp_misc_twm_notifier_register(struct notifier_block *nb)
+{
+	return 0;
+}
+static inline int qpnp_misc_twm_notifier_unregister(struct notifier_block *nb)
+{
+	return 0;
+}
 #endif
 #endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index bae4f35..4b2a1bc 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1768,6 +1768,7 @@
 	struct sched_entity se;
 	struct sched_rt_entity rt;
 	u64 last_sleep_ts;
+	u64 last_cpu_selected_ts;
 #ifdef CONFIG_SCHED_WALT
 	struct ravg ravg;
 	/*
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index a17b435..75aa98ff 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -511,4 +511,5 @@
 header-y += msm_dsps.h
 header-y += msm-core-interface.h
 header-y += msm_rotator.h
+header-y += bgcom_interface.h
 header-y += nfc/
diff --git a/include/uapi/linux/bgcom_interface.h b/include/uapi/linux/bgcom_interface.h
new file mode 100644
index 0000000..f18280a
--- /dev/null
+++ b/include/uapi/linux/bgcom_interface.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+ * GNU General Public License for more details.
+ */
+#ifndef LINUX_BG_CHAR_H
+#define LINUX_BG_CHAR_H
+#define BGCOM_REG_READ  0
+#define BGCOM_AHB_READ  1
+#define BGCOM_AHB_WRITE 2
+#define BGCOM_SET_SPI_FREE  3
+#define BGCOM_SET_SPI_BUSY  4
+#define BGCOM_REG_WRITE  5
+#define BGCOM_SOFT_RESET  6
+#define BGCOM_MODEM_DOWN2_BG  7
+#define EXCHANGE_CODE  'V'
+
+struct bg_ui_data {
+	uint64_t  __user write;
+	uint64_t  __user result;
+	uint32_t  bg_address;
+	uint32_t  cmd;
+	uint32_t  num_of_words;
+};
+
+enum bg_event_type {
+	BG_BEFORE_POWER_DOWN = 1,
+	BG_AFTER_POWER_UP,
+	MODEM_BEFORE_POWER_DOWN,
+	MODEM_AFTER_POWER_UP,
+};
+
+#define REG_READ \
+	_IOWR(EXCHANGE_CODE, BGCOM_REG_READ, \
+	struct bg_ui_data)
+#define AHB_READ \
+	_IOWR(EXCHANGE_CODE, BGCOM_AHB_READ, \
+	struct bg_ui_data)
+#define AHB_WRITE \
+	_IOW(EXCHANGE_CODE, BGCOM_AHB_WRITE, \
+	struct bg_ui_data)
+#define SET_SPI_FREE \
+	_IOR(EXCHANGE_CODE, BGCOM_SET_SPI_FREE, \
+	struct bg_ui_data)
+#define SET_SPI_BUSY \
+	_IOR(EXCHANGE_CODE, BGCOM_SET_SPI_BUSY, \
+	struct bg_ui_data)
+#define REG_WRITE \
+	_IOWR(EXCHANGE_CODE, BGCOM_REG_WRITE, \
+	struct bg_ui_data)
+#define BG_SOFT_RESET \
+	_IOWR(EXCHANGE_CODE, BGCOM_SOFT_RESET, \
+	struct bg_ui_data)
+#define BG_MODEM_DOWN2_BG_DONE \
+	_IOWR(EXCHANGE_CODE, BGCOM_MODEM_DOWN2_BG, \
+	struct bg_ui_data)
+#endif
diff --git a/include/uapi/linux/qcedev.h b/include/uapi/linux/qcedev.h
index fb51c23..108af3c 100644
--- a/include/uapi/linux/qcedev.h
+++ b/include/uapi/linux/qcedev.h
@@ -228,6 +228,33 @@
 	void *kernel;
 };
 
+/**
+ * struct qcedev_map_buf_req - Holds the mapping request information
+ * fd (IN):            Array of fds.
+ * num_fds (IN):       Number of fds in fd[].
+ * fd_size (IN):       Array of sizes corresponding to each fd in fd[].
+ * fd_offset (IN):     Array of offset corresponding to each fd in fd[].
+ * vaddr (OUT):        Array of mapped virtual address corresponding to
+ *			each fd in fd[].
+ */
+struct qcedev_map_buf_req {
+	int32_t         fd[QCEDEV_MAX_BUFFERS];
+	uint32_t        num_fds;
+	uint32_t        fd_size[QCEDEV_MAX_BUFFERS];
+	uint32_t        fd_offset[QCEDEV_MAX_BUFFERS];
+	uint64_t        buf_vaddr[QCEDEV_MAX_BUFFERS];
+};
+
+/**
+ * struct qcedev_unmap_buf_req - Holds the hashing request information
+ * fd (IN):            Array of fds to unmap
+ * num_fds (IN):       Number of fds in fd[].
+ */
+struct  qcedev_unmap_buf_req {
+	int32_t         fd[QCEDEV_MAX_BUFFERS];
+	uint32_t        num_fds;
+};
+
 struct file;
 
 #define QCEDEV_IOC_MAGIC	0x87
@@ -250,8 +277,8 @@
 	_IO(QCEDEV_IOC_MAGIC, 8)
 #define QCEDEV_IOCTL_GET_CMAC_REQ	\
 	_IOWR(QCEDEV_IOC_MAGIC, 9, struct qcedev_sha_op_req)
-#define QCEDEV_IOCTL_UPDATE_FIPS_STATUS		\
-	_IOWR(QCEDEV_IOC_MAGIC, 10, enum fips_status)
-#define QCEDEV_IOCTL_QUERY_FIPS_STATUS	\
-	_IOR(QCEDEV_IOC_MAGIC, 11, enum fips_status)
+#define QCEDEV_IOCTL_MAP_BUF_REQ	\
+	_IOWR(QCEDEV_IOC_MAGIC, 10, struct qcedev_map_buf_req)
+#define QCEDEV_IOCTL_UNMAP_BUF_REQ	\
+	_IOWR(QCEDEV_IOC_MAGIC, 11, struct qcedev_unmap_buf_req)
 #endif /* _UAPI_QCEDEV__H */
diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h
index 3f1facd..841c40a 100644
--- a/include/uapi/media/cam_req_mgr.h
+++ b/include/uapi/media/cam_req_mgr.h
@@ -41,9 +41,9 @@
 #define V4L_EVENT_CAM_REQ_MGR_EVENT       (V4L2_EVENT_PRIVATE_START + 0)
 
 /* Specific event ids to get notified in user space */
-#define V4L_EVENT_CAM_REQ_MGR_SOF         0
-#define V4L_EVENT_CAM_REQ_MGR_ERROR       1
-#define V4L_EVENT_CAM_REQ_MGR_MAX         2
+#define V4L_EVENT_CAM_REQ_MGR_SOF            0
+#define V4L_EVENT_CAM_REQ_MGR_ERROR          1
+#define V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS    2
 
 /* SOF Event status */
 #define CAM_REQ_MGR_SOF_EVENT_SUCCESS           0
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index f4330a2..a29eaee 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -77,6 +77,23 @@
 
 	  For more information take a look at <file:Documentation/power/swsusp.txt>.
 
+config HIBERNATION_IMAGE_REUSE
+	bool "Reuse hibernation image"
+	depends on HIBERNATION
+	---help---
+	  By default this hibernation image is erased after either a
+	  successful or unsuccessful hibernation restore sequeunce. Since
+	  filesystem contents on disk are not part of the hibernation
+	  image, failure to create a new hibernation image every boot can
+	  lead to filesystem corruption.
+
+	  Conversely, if the usecase can guarantee that the filesystem is
+	  not ever modified, the same hibernation image can be reused. This
+	  prevents creating additional hibernation images unncesarily.
+
+	  For more details, refer to the description of CONFIG_HIBERNATION
+	  for booting without resuming.
+
 config ARCH_SAVE_PAGE_KEYS
 	bool
 
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index a3b1e61..95db6b79 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -1542,10 +1542,12 @@
 
 		if (!memcmp(HIBERNATE_SIG, swsusp_header->sig, 10)) {
 			memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10);
+#ifndef CONFIG_HIBERNATION_IMAGE_REUSE
 			/* Reset swap signature now */
 			error = hib_submit_io(REQ_OP_WRITE, WRITE_SYNC,
 						swsusp_resume_block,
 						swsusp_header, NULL);
+#endif
 		} else {
 			error = -EINVAL;
 		}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 23a7f9c..0ca1647 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2330,6 +2330,7 @@
 	p->se.nr_migrations		= 0;
 	p->se.vruntime			= 0;
 	p->last_sleep_ts		= 0;
+	p->last_cpu_selected_ts		= 0;
 
 	INIT_LIST_HEAD(&p->se.group_node);
 
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c00e731..04ba6d0 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7296,6 +7296,39 @@
 	       task_fits_max(p, cpu);
 }
 
+#define SCHED_SELECT_PREV_CPU_NSEC	2000000
+#define SCHED_FORCE_CPU_SELECTION_NSEC	20000000
+
+static inline bool
+bias_to_prev_cpu(struct task_struct *p, struct cpumask *rtg_target)
+{
+	int prev_cpu = task_cpu(p);
+#ifdef CONFIG_SCHED_WALT
+	u64 ms = p->ravg.mark_start;
+#else
+	u64 ms = sched_clock();
+#endif
+
+	if (cpu_isolated(prev_cpu) || !idle_cpu(prev_cpu))
+		return false;
+
+	if (!ms)
+		return false;
+
+	if (ms - p->last_cpu_selected_ts >= SCHED_SELECT_PREV_CPU_NSEC) {
+		p->last_cpu_selected_ts = ms;
+		return false;
+	}
+
+	if (ms - p->last_sleep_ts >= SCHED_SELECT_PREV_CPU_NSEC)
+		return false;
+
+	if (rtg_target && !cpumask_test_cpu(prev_cpu, rtg_target))
+		return false;
+
+	return true;
+}
+
 #ifdef CONFIG_SCHED_WALT
 static inline struct cpumask *find_rtg_target(struct task_struct *p)
 {
@@ -7374,6 +7407,9 @@
 		}
 	}
 
+	if (bias_to_prev_cpu(p, rtg_target))
+		return prev_cpu;
+
 	rcu_read_lock();
 
 	sd = rcu_dereference(per_cpu(sd_ea, prev_cpu));
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 3637d96..f27ab13 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -823,6 +823,8 @@
 	int prev_top;
 	int curr_top;
 	bool notif_pending;
+	u64 last_cc_update;
+	u64 cycles;
 #endif
 
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index e6a11c1..a9fb367 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -301,10 +301,27 @@
 	return 0;
 }
 
-static void update_task_cpu_cycles(struct task_struct *p, int cpu)
+/*
+ * Assumes rq_lock is held and wallclock was recorded in the same critical
+ * section as this function's invocation.
+ */
+static inline u64 read_cycle_counter(int cpu, u64 wallclock)
+{
+	struct rq *rq = cpu_rq(cpu);
+
+	if (rq->last_cc_update != wallclock) {
+		rq->cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+		rq->last_cc_update = wallclock;
+	}
+
+	return rq->cycles;
+}
+
+static void update_task_cpu_cycles(struct task_struct *p, int cpu,
+				   u64 wallclock)
 {
 	if (use_cycle_counter)
-		p->cpu_cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+		p->cpu_cycles = read_cycle_counter(cpu, wallclock);
 }
 
 void clear_ed_task(struct task_struct *p, struct rq *rq)
@@ -348,7 +365,7 @@
 	if (is_idle_task(curr)) {
 		/* We're here without rq->lock held, IRQ disabled */
 		raw_spin_lock(&rq->lock);
-		update_task_cpu_cycles(curr, cpu);
+		update_task_cpu_cycles(curr, cpu, ktime_get_ns());
 		raw_spin_unlock(&rq->lock);
 	}
 }
@@ -757,7 +774,7 @@
 	update_task_ravg(p, task_rq(p), TASK_MIGRATE,
 			 wallclock, 0);
 
-	update_task_cpu_cycles(p, new_cpu);
+	update_task_cpu_cycles(p, new_cpu, wallclock);
 
 	/*
 	 * When a task is migrating during the wakeup, adjust
@@ -1839,7 +1856,7 @@
 		return;
 	}
 
-	cur_cycles = cpu_cycle_counter_cb.get_cpu_cycle_counter(cpu);
+	cur_cycles = read_cycle_counter(cpu, wallclock);
 
 	/*
 	 * If current task is idle task and irqtime == 0 CPU was
@@ -1904,7 +1921,7 @@
 	old_window_start = update_window_start(rq, wallclock, event);
 
 	if (!p->ravg.mark_start) {
-		update_task_cpu_cycles(p, cpu_of(rq));
+		update_task_cpu_cycles(p, cpu_of(rq), wallclock);
 		goto done;
 	}
 
@@ -2035,7 +2052,7 @@
 	p->ravg.mark_start = p->last_wake_ts = wallclock;
 	p->last_enqueued_ts = wallclock;
 	p->last_switch_out_ts = 0;
-	update_task_cpu_cycles(p, cpu_of(rq));
+	update_task_cpu_cycles(p, cpu_of(rq), wallclock);
 }
 
 static cpumask_t all_cluster_cpus = CPU_MASK_NONE;
@@ -3255,6 +3272,8 @@
 	rq->curr_table = 0;
 	rq->prev_top = 0;
 	rq->curr_top = 0;
+	rq->last_cc_update = 0;
+	rq->cycles = 0;
 	for (j = 0; j < NUM_TRACKED_WINDOWS; j++) {
 		memset(&rq->load_subs[j], 0,
 				sizeof(struct load_subtractions));