Merge "clk: qcom: clk-cpu-osm: correct the logic to check per-core DCVS status"
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt
new file mode 100644
index 0000000..a7a2eda
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt
@@ -0,0 +1,180 @@
+Qualcomm Technologies Inc. PNP Flash LED
+
+QPNP (Qualcomm Technologies Inc. Plug N Play) Flash LED (Light
+Emitting Diode) driver is used to provide illumination to
+camera sensor when background light is dim to capture good
+picture. It can also be used for flashlight/torch application.
+It is part of PMIC on Qualcomm Technologies Inc. reference platforms.
+The PMIC is connected to the host processor via SPMI bus.
+
+Required properties:
+- compatible		: should be "qcom,qpnp-flash-led"
+- reg			: base address and size for flash LED modules
+
+Optional properties:
+- qcom,headroom		: headroom to use. Values should be 250, 300,
+			  400 and 500 in mV.
+- qcom,startup-dly	: delay before flashing after flash executed.
+			  Values should 10, 32, 64, and 128 in us.
+- qcom,clamp-curr	: current to clamp at when voltage droop happens.
+			  Values are in integer from 0 to 1000 inclusive,
+			  indicating 0 to 1000 mA.
+- qcom,self-check-enabled : boolean type. self fault check enablement
+- qcom,thermal-derate-enabled : boolean type. derate enablement when module
+				temperature reaches threshold
+- qcom,thermal-derate-threshold : thermal threshold for derate. Values
+				should be 95, 105, 115, 125 in C.
+- qcom,thermal-derate-rate	: derate rate when module temperature
+				reaches threshold. Values should be
+				"1_PERCENT", "1P25_PERCENT", "2_PERCENT",
+				"2P5_PERCENT", "5_PERCENT" in string.
+- qcom,current-ramp-enabled	: boolean type. stepped current ramp enablement
+- qcom,ramp-up-step		: current ramp up rate. Values should be
+				  "0P2US", "0P4US", "0P8US", "1P6US", "3P3US",
+				  "6P7US", "13P5US", "27US".
+- qcom,ramp-dn-step		: current ramp down rate. Values should be
+				  "0P2US", "0P4US", "0P8US", "1P6US", "3P3US",
+				  "6P7US", "13P5US", "27US".
+- qcom,vph-pwr-droop-enabled	: boolean type. VPH power droop enablement. Enablement
+				  allows current clamp when phone power drops below
+				  pre-determined threshold
+- qcom,vph-pwr-droop-threshold	: VPH power threshold for module to clamp current.
+				  Values are 2500 - 3200 in mV with 100 mV steps.
+- qcom,vph-pwr-droop-debounce-time : debounce time for module to confirm a voltage
+				     droop is happening. Values are 0, 10, 32, 64
+				     in us.
+- qcom,pmic-charger-support	: Boolean type. This tells if flash utilizes charger boost
+				  support
+- qcom,headroom-sense-ch0-enabled: Boolean type. This configures headroom sensing enablement
+				   for LED channel 0
+- qcom,headroom-sense-ch1-enabled: Boolean type. This configures headroom sensing enablement
+				   for LED channel 1
+- qcom,power-detect-enabled	: Boolean type. This enables driver to get maximum flash LED
+				  current at current battery level to avoid intensity clamp
+				  when battery voltage is low
+- qcom,otst2-moduled-enabled	: Boolean type. This enables driver to enable MASK to support
+				OTST2 connection.
+- qcom,follow-otst2-rb-disabled	: Boolean type. This allows driver to reset/deset module.
+				By default, driver resets module. This entry allows driver to
+				bypass reset module sequence.
+- qcom,die-current-derate-enabled: Boolean type. This enables driver to get maximum flash LED
+                                   current, based on PMIC die temperature threshold to
+				   avoid significant current derate from hardware. This property
+				   is not needed if PMIC is older than PMI8994v2.0.
+- qcom,die-temp-vadc		: VADC channel source for flash LED. This property is not
+				  needed if PMIC is older than PMI8994v2.0.
+- qcom,die-temp-threshold       : Integer type array for PMIC die temperature threshold.
+				  Array should have at least one value. Values should be in
+				  celcius. This property is not needed if PMIC is older than
+				  PMI8994v2.0.
+- qcom,die-temp-derate-current	: Integer type arrray for PMIC die temperature derate
+				  current. Array should have at least one value. Values
+				  should be in mA. This property is not needed if PMIC is older
+				  than PMI8994v2.0.
+
+Required properties inside child node. Chile node contains settings for each individual LED.
+Each LED hardware needs a node for itself and a switch node to control brightness.
+For the purpose of turning on/off LED and better regulator control, "led:switch" node
+is introduced. "led:switch" acquires several existing properties from other nodes for
+operational simplification. For backward compatibility purpose, switch node can be optional:
+- label			: type of led that will be used, either "flash" or "torch".
+- qcom,led-name		: name of the LED. Accepted values are "led:flash_0",
+			  "led:flash_1", "led:torch_0", "led:torch_1"
+- qcom,default-led-trigger	: trigger for the camera flash and torch. Accepted values are
+				"flash0_trigger", "flash1_trigger", "torch0_trigger", torch1_trigger"
+- qcom,id		: enumerated ID for each physical LED. Accepted values are "0",
+			  "1", etc..
+- qcom,max-current	: maximum current allowed on this LED. Valid values should be
+			  integer from 0 to 1000 inclusive, indicating 0 to 1000 mA.
+- qcom,pmic-revid	: PMIC revision id source. This property is needed for PMI8996
+			revision check.
+
+Optional properties inside child node:
+- qcom,current		: default current intensity for LED. Accepted values should be
+			  integer from 0 t 1000 inclusive, indicating 0 to 1000 mA.
+- qcom,duration	: Duration for flash LED. When duration time expires, hardware will turn off
+		flash LED. Values should be from 10 ms to 1280 ms with 10 ms incremental
+		step. Not applicable to torch. It is required for LED:SWITCH node to handle
+		LED used as flash.
+- reg<n>	: reg<n> (<n> represents number. eg 0,1,2,..) property is to add support for
+		multiple power sources. It includes two properties regulator-name and max-voltage.
+		Required property inside regulator node:
+		- regulator-name	: This denotes this node is a regulator node and which
+					regulator to use.
+		Optional property inside regulator node:
+		- max-voltage		: This specifies max voltage of regulator. Some switch
+					or boost regulator does not need this property.
+
+Example:
+	qcom,leds@d300 {
+		compatible = "qcom,qpnp-flash-led";
+		status = "okay";
+		reg = <0xd300 0x100>;
+		label = "flash";
+		qcom,headroom = <500>;
+		qcom,startup-dly = <128>;
+		qcom,clamp-curr = <200>;
+		qcom,pmic-charger-support;
+		qcom,self-check-enabled;
+		qcom,thermal-derate-enabled;
+		qcom,thermal-derate-threshold = <80>;
+		qcom,thermal-derate-rate = "4_PERCENT";
+		qcom,current-ramp-enabled;
+		qcom,ramp_up_step = "27US";
+		qcom,ramp_dn_step = "27US";
+		qcom,vph-pwr-droop-enabled;
+		qcom,vph-pwr-droop-threshold = <3200>;
+		qcom,vph-pwr-droop-debounce-time = <10>;
+		qcom,headroom-sense-ch0-enabled;
+		qcom,headroom-sense-ch1-enabled;
+		qcom,die-current-derate-enabled;
+		qcom,die-temp-vadc = <&pmi8994_vadc>;
+		qcom,die-temp-threshold = <85 80 75 70 65>;
+		qcom,die-temp-derate-current = <400 800 1200 1600 2000>;
+		qcom,pmic-revid = <&pmi8994_revid>;
+
+		pm8226_flash0: qcom,flash_0 {
+			label = "flash";
+			qcom,led-name = "led:flash_0";
+			qcom,default-led-trigger =
+					"flash0_trigger";
+			qcom,max-current = <1000>;
+			qcom,id = <0>;
+			qcom,duration = <1280>;
+			qcom,current = <625>;
+		};
+
+		pm8226_torch: qcom,torch_0 {
+			label = "torch";
+			qcom,led-name = "led:torch_0";
+			qcom,default-led-trigger =
+					"torch0_trigger";
+			boost-supply = <&pm8226_chg_boost>;
+			qcom,max-current = <200>;
+			qcom,id = <0>;
+			qcom,current = <120>;
+			qcom,max-current = <200>;
+			reg0 {
+				regulator-name =
+					 "pm8226_chg_boost";
+				max-voltage = <3600000>;
+			};
+		};
+
+		pm8226_switch: qcom,switch {
+			lable = "switch";
+			qcom,led-name = "led:switch";
+			qcom,default-led-trigger =
+					"switch_trigger";
+			qcom,id = <2>;
+			qcom,current = <625>;
+			qcom,duration = <1280>;
+			qcom,max-current = <1000>;
+			reg0 {
+				regulator-name =
+					"pm8226_chg_boost";
+				max-voltage = <3600000>;
+			};
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 6222881..001f74f3 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -41,6 +41,11 @@
 				"HS200_1p2v" - indicates that host can support HS200 at 1.2v.
 				"DDR_1p8v" - indicates that host can support DDR mode at 1.8v.
 				"DDR_1p2v" - indicates that host can support DDR mode at 1.2v.
+	  - qcom,bus-aggr-clk-rates: this is an array that specifies the frequency for
+	  			the bus-aggr-clk which should be set corresponding to the
+				frequency used from clk-rate. The Frequency of this clock
+				should be decided based on the power mode in which the
+				apps clk would run with frequency in clk-rates.
 	- qcom,devfreq,freq-table - specifies supported frequencies for clock scaling.
 				    Clock scaling logic shall toggle between these frequencies based
 				    on card load. In case the defined frequencies are over or below
diff --git a/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt b/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt
new file mode 100644
index 0000000..faf56c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt
@@ -0,0 +1,141 @@
+MSM PCI express endpoint
+
+Required properties:
+  - compatible: should be "qcom,pcie-ep".
+  - reg: should contain PCIe register maps.
+  - reg-names: indicates various resources passed to driver by name.
+		Should be "msi", "dm_core", "elbi", "parf", "phy", "mmio".
+		These correspond to different modules within the PCIe domain.
+  - #address-cells: Should provide a value of 0.
+  - interrupt-parent: Should be the PCIe device node itself here.
+  - interrupts: Should be in the format <0 1 2> and it is an index to the
+		interrupt-map that contains PCIe related interrupts.
+  - #interrupt-cells: Should provide a value of 1.
+  - #interrupt-map-mask: should provide a value of 0xffffffff.
+  - interrupt-map:  Must create mapping for the number of interrupts
+		    that are defined in above interrupts property.
+		    For PCIe device node, it should define 6 mappings for
+		    the corresponding PCIe interrupts supporting the
+		    specification.
+  - interrupt-names: indicates interrupts passed to driver by name.
+		     Should be "int_pm_turnoff", "int_dstate_change",
+				"int_l1sub_timeout", "int_link_up",
+				"int_link_down", "int_bridge_flush_n".
+  - perst-gpio: PERST GPIO specified by PCIe spec.
+  - wake-gpio: WAKE GPIO specified by PCIe spec.
+  - clkreq-gpio: CLKREQ GPIO specified by PCIe spec.
+  - <supply-name>-supply: phandle to the regulator device tree node.
+    Refer to the schematics for the corresponding voltage regulators.
+    vreg-1.8-supply: phandle to the analog supply for the PCIe controller.
+    vreg-0.9-supply: phandle to the analog supply for the PCIe controller.
+
+Optional Properties:
+  - qcom,<supply-name>-voltage-level: specifies voltage levels for supply.
+    Should be specified in pairs (max, min, optimal), units uV.
+  - clock-names: list of names of clock inputs.
+		     Should be "pcie_0_pipe_clk",
+				"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
+				"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
+				"pcie_0_ldo";
+  - max-clock-frequency-hz: list of the maximum operating frequencies stored
+				in the same order of clock names;
+  - resets: reset specifier pair consists of phandle for the reset controller
+    and reset lines used by this controller.
+  - reset-names: reset signal names sorted in the same order as the property
+    of resets.
+  - qcom,pcie-phy-ver: version of PCIe PHY.
+  - qcom,phy-init: The initialization sequence to bring up the PCIe PHY.
+    Should be specified in groups (offset, value, delay, direction).
+  - qcom,phy-status-reg: Register offset for PHY status.
+  - qcom,dbi-base-reg: Register offset for DBI base address.
+  - qcom,slv-space-reg: Register offset for slave address space size.
+  - qcom,pcie-link-speed: generation of PCIe link speed. The value could be
+    1, 2 or 3.
+  - qcom,pcie-active-config: boolean type; active configuration of PCIe
+    addressing.
+  - qcom,pcie-aggregated-irq: boolean type; interrupts are aggregated.
+  - qcom,pcie-mhi-a7-irq: boolean type; MHI a7 has separate irq.
+  - qcom,pcie-perst-enum: Link enumeration will be triggered by PERST
+    deassertion.
+  - mdm2apstatus-gpio: GPIO used by PCIe endpoint side to notify the host side.
+  - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+    below optional properties:
+	- qcom,msm-bus,name
+	- qcom,msm-bus,num-cases
+	- qcom,msm-bus,num-paths
+	- qcom,msm-bus,vectors-KBps
+
+Example:
+
+	pcie_ep: qcom,pcie@bfffd000 {
+		compatible = "qcom,pcie-ep";
+
+		reg = <0xbfffd000 0x1000>,
+			<0xbfffe000 0x1000>,
+			<0xbffff000 0x1000>,
+			<0xfc520000 0x2000>,
+			<0xfc526000 0x1000>,
+			<0xfc527000 0x1000>;
+		reg-names = "msi", "dm_core", "elbi", "parf", "phy", "mmio";
+
+		#address-cells = <0>;
+		interrupt-parent = <&pcie_ep>;
+		interrupts = <0 1 2 3 4 5>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 44 0
+				1 &intc 0 46 0
+				2 &intc 0 47 0
+				3 &intc 0 50 0
+				4 &intc 0 51 0
+				5 &intc 0 52 0>;
+		interrupt-names = "int_pm_turnoff", "int_dstate_change",
+				"int_l1sub_timeout", "int_link_up",
+				"int_link_down", "int_bridge_flush_n";
+
+		perst-gpio = <&msmgpio 65 0>;
+		wake-gpio = <&msmgpio 61 0>;
+		clkreq-gpio = <&msmgpio 64 0>;
+		mdm2apstatus-gpio = <&tlmm_pinmux 16 0>;
+
+		gdsc-vdd-supply = <&gdsc_pcie_0>;
+		vreg-1.8-supply = <&pmd9635_l8>;
+		vreg-0.9-supply = <&pmd9635_l4>;
+
+		qcom,vreg-1.8-voltage-level = <1800000 1800000 1000>;
+		qcom,vreg-0.9-voltage-level = <950000 950000 24000>;
+
+		clock-names = "pcie_0_pipe_clk",
+				"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
+				"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
+				"pcie_0_ldo";
+		max-clock-frequency-hz = <62500000>, <1000000>,
+						<0>, <0>, <0>, <0>;
+
+		resets = <&clock_gcc GCC_PCIE_BCR>,
+			<&clock_gcc GCC_PCIE_PHY_BCR>;
+
+		reset-names = "pcie_0_core_reset", "pcie_0_phy_reset";
+
+		qcom,msm-bus,name = "pcie-ep";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<45 512 0 0>,
+				<45 512 500 800>;
+
+		qcom,pcie-link-speed = <1>;
+		qcom,pcie-active-config;
+		qcom,pcie-aggregated-irq;
+		qcom,pcie-mhi-a7-irq;
+		qcom,pcie-perst-enum;
+		qcom,phy-status-reg = <0x728>;
+		qcom,dbi-base-reg = <0x168>;
+		qcom,slv-space-reg = <0x16c>;
+
+		qcom,phy-init = <0x604 0x03 0x0 0x1
+					0x048 0x08 0x0 0x1
+					0x64c 0x4d 0x0 0x1
+					0x600 0x00 0x0 0x1
+					0x608 0x03 0x0 0x1>;
+	};
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index b0db996..0c5f696 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -13,6 +13,7 @@
 Optional properties:
 - qcom,fastrpc-glink:	Flag to use glink instead of smd for IPC
 - qcom,rpc-latency-us:	FastRPC QoS latency vote
+- qcom,adsp-remoteheap-vmid:  FastRPC remote heap VMID number
 
 Optional subnodes:
 - qcom,msm_fastrpc_compute_cb :	Child nodes representing the compute context
@@ -28,6 +29,7 @@
 		compatible = "qcom,msm-fastrpc-adsp";
 		qcom,fastrpc-glink;
 		qcom,rpc-latency-us = <2343>;
+		qcom,adsp-remoteheap-vmid = <37>;
 
 		qcom,msm_fastrpc_compute_cb_1 {
 			compatible = "qcom,msm-fastrpc-compute-cb";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index b0be698..96390434 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -20,7 +20,7 @@
 / {
 	model = "Qualcomm Technologies, Inc. SDX POORWILLS";
 	compatible = "qcom,sdxpoorwills";
-	qcom,msm-id = <334 0x0>;
+	qcom,msm-id = <334 0x0>, <335 0x0>;
 	interrupt-parent = <&intc>;
 
 	reserved-memory {
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 6adb5df..3df7439 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -179,6 +179,7 @@
 	sda670-cdp.dtb \
 	sda670-pm660a-mtp.dtb \
 	sda670-pm660a-cdp.dtb \
+	qcs605-360camera.dtb \
 	qcs605-mtp.dtb \
 	qcs605-cdp.dtb \
 	qcs605-external-codec-mtp.dtb
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index e90c30b..87d5f34 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -640,10 +640,10 @@
 		interrupts = <GIC_SPI 190 IRQ_TYPE_NONE>;
 		qcom,ee = <0>;
 		qcom,channel = <0>;
-		#address-cells = <1>;
+		#address-cells = <2>;
 		#size-cells = <0>;
 		interrupt-controller;
-		#interrupt-cells = <3>;
+		#interrupt-cells = <4>;
 		cell-index = <0>;
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/qcs605-360camera.dts b/arch/arm64/boot/dts/qcom/qcs605-360camera.dts
new file mode 100644
index 0000000..8caad4b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-360camera.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605.dtsi"
+#include "qcs605-360camera.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L 360camera";
+	compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+	qcom,board-id = <0x0000000b 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
new file mode 100644
index 0000000..efd6f4a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm670-mtp.dtsi"
+#include "sdm670-camera-sensor-360camera.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+&qupv3_se3_i2c {
+	status = "disabled";
+};
+
+&qupv3_se10_i2c {
+	status = "okay";
+};
+
+&qupv3_se12_2uart {
+	status = "okay";
+};
+
+&qupv3_se6_4uart {
+	status = "okay";
+};
+
+&qupv3_se13_i2c {
+	status = "disabled";
+};
+
+&qupv3_se13_spi {
+	status = "disabled";
+};
+
+&int_codec {
+	qcom,model = "sdm670-360cam-snd-card";
+	qcom,audio-routing =
+		"RX_BIAS", "INT_MCLK0",
+		"SPK_RX_BIAS", "INT_MCLK0",
+		"INT_LDO_H", "INT_MCLK0",
+		"DMIC1", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic1",
+		"DMIC2", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic2",
+		"DMIC3", "MIC BIAS External2",
+		"MIC BIAS External2", "Digital Mic3",
+		"DMIC4", "MIC BIAS External2",
+		"MIC BIAS External2", "Digital Mic4",
+		"PDM_IN_RX1", "PDM_OUT_RX1",
+		"PDM_IN_RX2", "PDM_OUT_RX2",
+		"PDM_IN_RX3", "PDM_OUT_RX3",
+		"ADC1_IN", "ADC1_OUT",
+		"ADC2_IN", "ADC2_OUT",
+		"ADC3_IN", "ADC3_OUT";
+	qcom,wsa-max-devs = <0>;
+};
+
+&tlmm {
+	pwr_led_green_default: pwr_led_green_default {
+		mux {
+			pins = "gpio106";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio106";
+			drive-strength = <8>;   /* 8 mA */
+			bias-disable;
+			output-low;
+		};
+	};
+
+	pwr_led_red_default: pwr_led_red_default {
+		mux {
+			pins = "gpio111";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio111";
+			drive-strength = <8>;   /* 8 mA */
+			bias-disable;
+			output-low;
+		};
+	};
+
+	wifi_led_green_default: wifi_led_green_default {
+		mux {
+			pins = "gpio114";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio114";
+			drive-strength = <8>;   /* 8 mA */
+			bias-disable;
+			output-low;
+		};
+	};
+
+	wifi_led_red_default: wifi_led_red_default {
+		mux {
+			pins = "gpio115";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio115";
+			drive-strength = <8>;   /* 8 mA */
+			bias-disable;
+			output-low;
+		};
+	};
+
+	key_wcnss_default: key_wcnss_default {
+		mux {
+			pins = "gpio120";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio120";
+			drive-strength = <8>;   /* 8 mA */
+			bias-pull-up;
+			input-enable;
+		};
+	};
+
+	key_record_default: key_record_default {
+		mux {
+			pins = "gpio119";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio119";
+			drive-strength = <8>;   /* 8 mA */
+			bias-pull-up;
+			input-enable;
+		};
+	};
+
+	key_snapshot_default: key_snapshot_default {
+		mux {
+			pins = "gpio91";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio91";
+			drive-strength = <8>;   /* 8 mA */
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&soc {
+	gpio-leds {
+		compatible = "gpio-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwr_led_green_default
+				&pwr_led_red_default
+				&wifi_led_green_default
+				&wifi_led_red_default>;
+		status = "okay";
+
+		led@1 {
+			label = "PWR_LED:red:106";
+			gpios = <&tlmm 106 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "wlan";
+			default-state = "off";
+		};
+
+		led@2 {
+			label = "PWR_LED:green:111";
+			gpios = <&tlmm 111 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "wlan";
+			default-state = "on";
+		};
+
+		led@3 {
+			label = "WIFI_LED:red:114";
+			gpios = <&tlmm 114 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "wlan";
+			default-state = "on";
+		};
+
+		led@4 {
+			label = "WIFI_LED:green:115";
+			gpios = <&tlmm 115 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "wlan";
+			default-state = "off";
+		};
+	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		label = "gpio-keys";
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&key_snapshot_default
+				&key_record_default
+				&key_wcnss_default>;
+		status = "okay";
+		cam_snapshot {
+			label = "cam_snapshot";
+			gpios = <&tlmm 91 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <766>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		cam_record {
+			label = "cam_record";
+			gpios = <&tlmm 119 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <766>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		wcnss_key {
+			label = "wcnss_key";
+			gpios = <&tlmm 120 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <528>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605.dtsi b/arch/arm64/boot/dts/qcom/qcs605.dtsi
index 12da650..66493d1 100644
--- a/arch/arm64/boot/dts/qcom/qcs605.dtsi
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -17,3 +17,13 @@
 	model = "Qualcomm Technologies, Inc. QCS605";
 	qcom,msm-id = <347 0x0>;
 };
+
+&soc {
+	qcom,rmnet-ipa {
+		status = "disabled";
+	};
+};
+
+&ipa_hw {
+	status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
index d212554..26a73b0 100644
--- a/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
@@ -22,3 +22,19 @@
 &sdhc_2 {
 	cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
 };
+
+&usb1 {
+	status = "ok";
+	dwc3@a800000 {
+		maximum-speed = "high-speed";
+		dr_mode = "host";
+	};
+};
+
+&qusb_phy1 {
+	status = "ok";
+};
+
+&usb_qmp_phy {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-360camera.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-360camera.dtsi
new file mode 100644
index 0000000..18b0cd8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-360camera.dtsi
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	led_flash_rear: qcom,camera-flash@0 {
+		cell-index = <0>;
+		reg = <0x00 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash0 &pm660l_flash1>;
+		torch-source = <&pm660l_torch0 &pm660l_torch1>;
+		switch-source = <&pm660l_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@1 {
+		cell-index = <1>;
+		reg = <0x01 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash2>;
+		torch-source = <&pm660l_torch2>;
+		switch-source = <&pm660l_switch1>;
+		status = "ok";
+	};
+
+	actuator_regulator: gpio-regulator@0 {
+		compatible = "regulator-fixed";
+		reg = <0x00 0x00>;
+		regulator-name = "actuator_regulator";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-enable-ramp-delay = <100>;
+		enable-active-high;
+		gpio = <&tlmm 27 0>;
+	};
+
+	camera_ldo: gpio-regulator@2 {
+		compatible = "regulator-fixed";
+		reg = <0x02 0x00>;
+		regulator-name = "camera_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_rear_ldo: gpio-regulator@1 {
+		compatible = "regulator-fixed";
+		reg = <0x01 0x00>;
+		regulator-name = "camera_rear_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <135>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_rear_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_vio_ldo: gpio-regulator@3 {
+		compatible = "regulator-fixed";
+		reg = <0x03 0x00>;
+		regulator-name = "camera_vio_ldo";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 29 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vio>;
+		vin-supply = <&pm660_s4>;
+	};
+
+	camera_vana_ldo: gpio-regulator@4 {
+		compatible = "regulator-fixed";
+		reg = <0x04 0x00>;
+		regulator-name = "camera_vana_ldo";
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 8 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vana>;
+		vin-supply = <&pm660l_bob>;
+	};
+};
+
+&cam_cci {
+	actuator_rear: qcom,actuator@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,actuator";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_front: qcom,actuator@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	ois_rear: qcom,ois@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,ois";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+		status = "disabled";
+	};
+
+	eeprom_rear: qcom,eeprom@0 {
+		cell-index = <0>;
+		reg = <0>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_rear_aux: qcom,eeprom@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_front: qcom,eeprom@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x0>;
+		csiphy-sd-index = <0>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear>;
+		ois-src = <&ois_rear>;
+		eeprom-src = <&eeprom_rear>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x1>;
+		csiphy-sd-index = <1>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		eeprom-src = <&eeprom_rear_aux>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@2 {
+		cell-index = <2>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x02>;
+		csiphy-sd-index = <2>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		eeprom-src = <&eeprom_front>;
+		actuator-src = <&actuator_front>;
+		led-flash-src = <&led_flash_front>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
index c92fe1d..a885495 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
@@ -537,6 +537,58 @@
 					  <&funnel_apss_merg_out_funnel_in2>;
 				};
 			};
+			port@4 {
+				reg = <6>;
+				funnel_in2_in_funnel_gfx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&funnel_gfx_out_funnel_in2>;
+				};
+			};
+		};
+	};
+
+	funnel_gfx: funnel@0x6943000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b908>;
+
+		reg = <0x6943000 0x1000>;
+		reg-names = "funnel-base";
+
+		coresight-name = "coresight-funnel-gfx";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				funnel_gfx_out_funnel_in2: endpoint {
+					remote-endpoint =
+					  <&funnel_in2_in_funnel_gfx>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				funnel_in2_in_gfx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&gfx_out_funnel_in2>;
+				};
+			};
+
+			port@2 {
+				reg = <1>;
+				funnel_in2_in_gfx_cx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&gfx_cx_out_funnel_in2>;
+				};
+			};
 		};
 	};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index 75a7ac2..1a93fc2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -129,6 +129,36 @@
 
 		qcom,gpu-speed-bin = <0x41a0 0x1fe00000 21>;
 
+		qcom,gpu-coresights {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "qcom,gpu-coresight";
+
+			qcom,gpu-coresight@0 {
+				reg = <0>;
+				coresight-name = "coresight-gfx";
+				coresight-atid = <50>;
+				port {
+					gfx_out_funnel_in2: endpoint {
+						remote-endpoint =
+						  <&funnel_in2_in_gfx>;
+					};
+				};
+			};
+
+			qcom,gpu-coresight@1 {
+				reg = <1>;
+				coresight-name = "coresight-gfx-cx";
+				coresight-atid = <51>;
+				port {
+					gfx_cx_out_funnel_in2: endpoint {
+						remote-endpoint =
+						  <&funnel_in2_in_gfx_cx>;
+					};
+				};
+			};
+		};
+
 		/* GPU Mempools */
 		qcom,gpu-mempools {
 			#address-cells = <1>;
@@ -394,7 +424,7 @@
 
 		gfx3d_secure: gfx3d_secure {
 			compatible = "qcom,smmu-kgsl-cb";
-			iommus = <&kgsl_smmu 2>;
+			iommus = <&kgsl_smmu 2>, <&kgsl_smmu 1>;
 		};
 	};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index 93e4c51..0e87314 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -68,6 +68,10 @@
 	};
 };
 
+&eud {
+	vdda33-supply = <&pm660l_l7>;
+};
+
 &pm660_fg {
 	qcom,battery-data = <&qrd_batterydata>;
 	qcom,fg-bmd-en-delay-ms = <300>;
@@ -142,6 +146,29 @@
 	};
 };
 
+&qusb_phy0 {
+	qcom,qusb-phy-init-seq =
+		/* <value reg_offset> */
+		   <0x23 0x210 /* PWR_CTRL1 */
+		    0x03 0x04  /* PLL_ANALOG_CONTROLS_TWO */
+		    0x7c 0x18c /* PLL_CLOCK_INVERTERS */
+		    0x80 0x2c  /* PLL_CMODE */
+		    0x0a 0x184 /* PLL_LOCK_DELAY */
+		    0x19 0xb4  /* PLL_DIGITAL_TIMERS_TWO */
+		    0x40 0x194 /* PLL_BIAS_CONTROL_1 */
+		    0x20 0x198 /* PLL_BIAS_CONTROL_2 */
+		    0x21 0x214 /* PWR_CTRL2 */
+		    0x07 0x220 /* IMP_CTRL1 */
+		    0x58 0x224 /* IMP_CTRL2 */
+		    0x77 0x240 /* TUNE1 */
+		    0x29 0x244 /* TUNE2 */
+		    0xca 0x248 /* TUNE3 */
+		    0x04 0x24c /* TUNE4 */
+		    0x03 0x250 /* TUNE5 */
+		    0x00 0x23c /* CHG_CTRL2 */
+		    0x22 0x210>; /* PWR_CTRL1 */
+};
+
 &pm660_haptics {
 	qcom,vmax-mv = <1800>;
 	qcom,wave-play-rate-us = <4255>;
@@ -279,7 +306,7 @@
 
 &pm660l_wled {
 	status = "okay";
-	qcom,led-strings-list = [01 02];
+	qcom,led-strings-list = [00 01];
 };
 
 &mdss_mdp {
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 58af833..a3b8c78 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -1138,6 +1138,11 @@
 			reg = <0x10 8>;
 		};
 
+		dload_type@1c {
+			compatible = "qcom,msm-imem-dload-type";
+			reg = <0x1c 0x4>;
+		};
+
 		restart_reason@65c {
 			compatible = "qcom,msm-imem-restart_reason";
 			reg = <0x65c 4>;
@@ -1312,52 +1317,52 @@
 		compatible = "qcom,mem-dump";
 		memory-region = <&dump_mem>;
 
-		rpmh_dump {
+		rpmh {
 			qcom,dump-size = <0x2000000>;
 			qcom,dump-id = <0xec>;
 		};
 
-		rpm_sw_dump {
+		rpm_sw {
 			qcom,dump-size = <0x28000>;
 			qcom,dump-id = <0xea>;
 		};
 
-		pmic_dump {
+		pmic {
 			qcom,dump-size = <0x10000>;
 			qcom,dump-id = <0xe4>;
 		};
 
-		tmc_etf_dump {
+		tmc_etf {
 			qcom,dump-size = <0x10000>;
 			qcom,dump-id = <0xf0>;
 		};
 
-		tmc_etf_swao_dump {
+		tmc_etfswao {
 			qcom,dump-size = <0x8400>;
 			qcom,dump-id = <0xf1>;
 		};
 
-		tmc_etr_reg_dump {
+		tmc_etr_reg {
 			qcom,dump-size = <0x1000>;
 			qcom,dump-id = <0x100>;
 		};
 
-		tmc_etf_reg_dump {
+		tmc_etf_reg {
 			qcom,dump-size = <0x1000>;
 			qcom,dump-id = <0x101>;
 		};
 
-		tmc_etf_swao_reg_dump {
+		etfswao_reg {
 			qcom,dump-size = <0x1000>;
 			qcom,dump-id = <0x102>;
 		};
 
-		misc_data_dump {
+		misc_data {
 			qcom,dump-size = <0x1000>;
 			qcom,dump-id = <0xe8>;
 		};
 
-		power_regs_data_dump {
+		power_regs {
 			qcom,dump-size = <0x100000>;
 			qcom,dump-id = <0xed>;
 		};
@@ -2236,10 +2241,10 @@
 			<1 782 100000 100000>,
 			/* 50 MB/s */
 			<150 512 130718 200000>,
-			<1 782 133320 133320>,
+			<1 782 100000 100000>,
 			/* 100 MB/s */
 			<150 512 130718 200000>,
-			<1 782 150000 150000>,
+			<1 782 130000 130000>,
 			/* 200 MB/s */
 			<150 512 261438 400000>,
 			<1 782 300000 300000>,
@@ -2272,7 +2277,6 @@
 
 		qcom,nonremovable;
 
-		qcom,scaling-lower-bus-speed-mode = "DDR52";
 		status = "disabled";
 	};
 
@@ -2311,10 +2315,10 @@
 			<1 608 100000 100000>,
 			/* 50 MB/s */
 			<81 512 130718 200000>,
-			<1 608 133320 133320>,
+			<1 608 100000 100000>,
 			/* 100 MB/s */
 			<81 512 261438 200000>,
-			<1 608 150000 150000>,
+			<1 608 130000 130000>,
 			/* 200 MB/s */
 			<81 512 261438 400000>,
 			<1 608 300000 300000>,
@@ -2349,6 +2353,7 @@
 
 	qcom,msm_fastrpc {
 		compatible = "qcom,msm-fastrpc-compute";
+		qcom,adsp-remoteheap-vmid = <37>;
 
 		qcom,msm_fastrpc_compute_cb1 {
 			compatible = "qcom,msm-fastrpc-compute-cb";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 0f6650d..1fcf893 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -594,7 +594,7 @@
 			qcom,gpu-freq = <520000000>;
 			qcom,bus-freq = <9>;
 			qcom,bus-min = <8>;
-			qcom,bus-max = <10>;
+			qcom,bus-max = <11>;
 		};
 
 		qcom,gpu-pwrlevel@4 {
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 8d60f57..9a43bb6 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -74,6 +74,7 @@
 CONFIG_CP15_BARRIER_EMULATION=y
 CONFIG_SETEND_EMULATION=y
 # CONFIG_ARM64_VHE is not set
+CONFIG_RANDOMIZE_BASE=y
 # CONFIG_EFI is not set
 CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -429,8 +430,10 @@
 CONFIG_USB_CONFIGFS_F_ACC=y
 CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
 CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_UAC2=y
 CONFIG_USB_CONFIGFS_F_MIDI=y
 CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_CONFIGFS_F_UVC=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
 CONFIG_USB_CONFIGFS_F_CDEV=y
 CONFIG_USB_CONFIGFS_F_CCID=y
@@ -450,6 +453,7 @@
 CONFIG_MMC_CQ_HCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
 CONFIG_LEDS_QPNP=y
 CONFIG_LEDS_QPNP_FLASH_V2=y
 CONFIG_LEDS_QPNP_WLED=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 6ce68881..822324d 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -79,6 +79,7 @@
 CONFIG_CP15_BARRIER_EMULATION=y
 CONFIG_SETEND_EMULATION=y
 # CONFIG_ARM64_VHE is not set
+CONFIG_RANDOMIZE_BASE=y
 CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 CONFIG_COMPAT=y
@@ -433,8 +434,10 @@
 CONFIG_USB_CONFIGFS_F_ACC=y
 CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
 CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_UAC2=y
 CONFIG_USB_CONFIGFS_F_MIDI=y
 CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_CONFIGFS_F_UVC=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
 CONFIG_USB_CONFIGFS_F_CDEV=y
 CONFIG_USB_CONFIGFS_F_CCID=y
@@ -455,6 +458,7 @@
 CONFIG_MMC_CQ_HCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
 CONFIG_LEDS_QPNP=y
 CONFIG_LEDS_QPNP_FLASH_V2=y
 CONFIG_LEDS_QPNP_WLED=y
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 2437f15..623dd48 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -54,6 +54,7 @@
 #include <asm/tlbflush.h>
 #include <asm/ptrace.h>
 #include <asm/virt.h>
+#include <soc/qcom/minidump.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/ipi.h>
@@ -844,6 +845,7 @@
 		pr_crit("CPU%u: stopping\n", cpu);
 		show_regs(regs);
 		dump_stack();
+		dump_stack_minidump(regs->sp);
 		raw_spin_unlock(&stop_lock);
 	}
 
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 28afd5d..f64e86f 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -1,7 +1,7 @@
 /*
  *  Bluetooth supports for Qualcomm Atheros chips
  *
- *  Copyright (c) 2015 The Linux Foundation. All rights reserved.
+ *  Copyright (c) 2017 The Linux Foundation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
@@ -27,6 +27,9 @@
 
 #define VERSION "0.1"
 
+#define MAX_PATCH_FILE_SIZE (100*1024)
+#define MAX_NVM_FILE_SIZE   (10*1024)
+
 static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
 {
 	struct sk_buff *skb;
@@ -285,27 +288,63 @@
 				  struct rome_config *config)
 {
 	const struct firmware *fw;
+	u32 type_len, length;
+	struct tlv_type_hdr *tlv;
 	int ret;
 
-	BT_INFO("%s: ROME Downloading %s", hdev->name, config->fwname);
-
+	BT_INFO("%s: ROME Downloading file: %s", hdev->name, config->fwname);
 	ret = request_firmware(&fw, config->fwname, &hdev->dev);
-	if (ret) {
-		BT_ERR("%s: Failed to request file: %s (%d)", hdev->name,
-		       config->fwname, ret);
+
+	if (ret || !fw || !fw->data || fw->size <= 0) {
+		BT_ERR("Failed to request file: err = (%d)", ret);
+		ret = ret ? ret : -EINVAL;
 		return ret;
 	}
 
-	rome_tlv_check_data(config, fw);
-
-	ret = rome_tlv_download_request(hdev, fw);
-	if (ret) {
-		BT_ERR("%s: Failed to download file: %s (%d)", hdev->name,
-		       config->fwname, ret);
+	if (config->type != TLV_TYPE_NVM &&
+		config->type != TLV_TYPE_PATCH) {
+		ret = -EINVAL;
+		BT_ERR("TLV_NVM dload: wrong config type selected");
+		goto exit;
 	}
 
-	release_firmware(fw);
+	if (config->type == TLV_TYPE_PATCH &&
+		(fw->size > MAX_PATCH_FILE_SIZE)) {
+		ret = -EINVAL;
+		BT_ERR("TLV_PATCH dload: wrong patch file sizes");
+		goto exit;
+	} else if (config->type == TLV_TYPE_NVM &&
+		(fw->size > MAX_NVM_FILE_SIZE)) {
+		ret = -EINVAL;
+		BT_ERR("TLV_NVM dload: wrong NVM file sizes");
+		goto exit;
+	}
 
+	if (fw->size < sizeof(struct tlv_type_hdr)) {
+		ret = -EINVAL;
+		BT_ERR("Firware size smaller to fit minimum value");
+		goto exit;
+	}
+
+	tlv = (struct tlv_type_hdr *)fw->data;
+	type_len = le32_to_cpu(tlv->type_len);
+	length = (type_len >> 8) & 0x00ffffff;
+
+	if (fw->size - 4 != length) {
+		ret = -EINVAL;
+		BT_ERR("Requested size not matching size in header");
+		goto exit;
+	}
+
+	rome_tlv_check_data(config, fw);
+	ret = rome_tlv_download_request(hdev, fw);
+
+	if (ret) {
+		BT_ERR("Failed to download FW: error = (%d)", ret);
+	}
+
+exit:
+	release_firmware(fw);
 	return ret;
 }
 
@@ -316,8 +355,9 @@
 	int err;
 
 	cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD;
-	cmd[1] = 0x02; 			/* TAG ID */
-	cmd[2] = sizeof(bdaddr_t);	/* size */
+	/* Set the TAG ID of 0x02 for NVM set and size of tag */
+	cmd[1] = 0x02;
+	cmd[2] = sizeof(bdaddr_t);
 	memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t));
 	skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd,
 				HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index c92819c..35eea02 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -234,6 +234,7 @@
 	int prevssrcount;
 	int issubsystemup;
 	int vmid;
+	int rhvmid;
 	int ramdumpenabled;
 	void *remoteheap_ramdump_dev;
 	struct fastrpc_glink_info link;
@@ -1614,7 +1615,7 @@
 	struct fastrpc_mmap *file = NULL, *mem = NULL;
 	char *proc_name = NULL;
 	int srcVM[1] = {VMID_HLOS};
-	int destVM[1] = {VMID_ADSP_Q6};
+	int destVM[1] = {me->channel[fl->cid].rhvmid};
 	int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 	int hlosVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 
@@ -1830,6 +1831,7 @@
 			       struct fastrpc_mmap *map)
 {
 	struct fastrpc_ioctl_invoke_crc ioctl;
+	struct fastrpc_apps *me = &gfa;
 	struct smq_phy_page page;
 	int num = 1;
 	remote_arg_t ra[3];
@@ -1884,7 +1886,7 @@
 	} else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
 
 		int srcVM[1] = {VMID_HLOS};
-		int destVM[1] = {VMID_ADSP_Q6};
+		int destVM[1] = {me->channel[fl->cid].rhvmid};
 		int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 
 		VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size,
@@ -1900,7 +1902,8 @@
 				 struct fastrpc_mmap *map)
 {
 	int err = 0;
-	int srcVM[1] = {VMID_ADSP_Q6};
+	struct fastrpc_apps *me = &gfa;
+	int srcVM[1] = {me->channel[fl->cid].rhvmid};
 	int destVM[1] = {VMID_HLOS};
 	int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 
@@ -3002,6 +3005,17 @@
 	struct cma *cma;
 	uint32_t val;
 
+
+	if (of_device_is_compatible(dev->of_node,
+					"qcom,msm-fastrpc-compute")) {
+		of_property_read_u32(dev->of_node, "qcom,adsp-remoteheap-vmid",
+			&gcinfo[0].rhvmid);
+
+		pr_info("ADSPRPC : vmids adsp=%d\n", gcinfo[0].rhvmid);
+
+		of_property_read_u32(dev->of_node, "qcom,rpc-latency-us",
+			&me->latency);
+	}
 	if (of_device_is_compatible(dev->of_node,
 					"qcom,msm-fastrpc-compute-cb"))
 		return fastrpc_cb_probe(dev);
@@ -3046,10 +3060,6 @@
 		return 0;
 	}
 
-	err = of_property_read_u32(dev->of_node, "qcom,rpc-latency-us",
-		&me->latency);
-	if (err)
-		me->latency = 0;
 	VERIFY(err, !of_platform_populate(pdev->dev.of_node,
 					  fastrpc_match_table,
 					  NULL, &pdev->dev));
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index b30bfad..8e5d836 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -554,6 +554,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	if (!diag_apps_responds())
 		return 0;
@@ -655,7 +660,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
-
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 	if (!diag_apps_responds())
 		return 0;
 
@@ -668,6 +677,12 @@
 	rsp.status = MSG_STATUS_FAIL;
 	rsp.padding = 0;
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		if ((req->ssid_first < mask->ssid_first) ||
 		    (req->ssid_first > mask->ssid_last_tools)) {
@@ -710,11 +725,23 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	req = (struct diag_msg_build_mask_t *)src_buf;
 	mutex_lock(&mask_info->lock);
 	mutex_lock(&driver->msg_mask_lock);
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		if (i < (driver->msg_mask_tbl_count - 1)) {
 			mask_next = mask;
@@ -833,6 +860,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	req = (struct diag_msg_config_rsp_t *)src_buf;
 
@@ -840,6 +872,13 @@
 	mutex_lock(&driver->msg_mask_lock);
 
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED :
 					   DIAG_CTRL_MASK_ALL_DISABLED;
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
@@ -937,7 +976,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
-
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 	req = (struct diag_event_mask_config_t *)src_buf;
 	mask_len = EVENT_COUNT_TO_BYTES(req->num_bits);
 	if (mask_len <= 0 || mask_len > event_mask.mask_len) {
@@ -1000,6 +1043,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	toggle = *(src_buf + 1);
 	mutex_lock(&mask_info->lock);
@@ -1063,6 +1111,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	if (!diag_apps_responds())
 		return 0;
@@ -1082,6 +1135,11 @@
 	write_len += rsp_header_len;
 
 	log_item = (struct diag_log_mask_t *)mask_info->ptr;
+	if (!log_item->ptr) {
+		pr_err("diag: Invalid input in %s, mask: %pK\n",
+			__func__, log_item);
+		return -EINVAL;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, log_item++) {
 		if (log_item->equip_id != req->equip_id)
 			continue;
@@ -1187,11 +1245,20 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	req = (struct diag_log_config_req_t *)src_buf;
 	read_len += req_header_len;
 	mask = (struct diag_log_mask_t *)mask_info->ptr;
-
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		return -EINVAL;
+	}
 	if (req->equip_id >= MAX_EQUIP_ID) {
 		pr_err("diag: In %s, Invalid logging mask request, equip_id: %d\n",
 		       __func__, req->equip_id);
@@ -1314,9 +1381,17 @@
 		       mask_info);
 		return -EINVAL;
 	}
-
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 	mask = (struct diag_log_mask_t *)mask_info->ptr;
-
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		return -EINVAL;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
 		mutex_lock(&mask->lock);
 		memset(mask->ptr, 0, mask->range);
@@ -1586,7 +1661,7 @@
 
 static void __diag_mask_exit(struct diag_mask_info *mask_info)
 {
-	if (!mask_info)
+	if (!mask_info || !mask_info->ptr)
 		return;
 
 	mutex_lock(&mask_info->lock);
@@ -1642,11 +1717,17 @@
 	int i;
 	struct diag_log_mask_t *mask = NULL;
 
-	if (!mask_info)
+	if (!mask_info || !mask_info->ptr)
 		return;
 
 	mutex_lock(&mask_info->lock);
 	mask = (struct diag_log_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&mask_info->lock);
+		return;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
 		kfree(mask->ptr);
 		mask->ptr = NULL;
@@ -1722,11 +1803,18 @@
 	int i;
 	struct diag_msg_mask_t *mask = NULL;
 
-	if (!mask_info)
+	if (!mask_info || !mask_info->ptr)
 		return;
 	mutex_lock(&mask_info->lock);
 	mutex_lock(&driver->msg_mask_lock);
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		kfree(mask->ptr);
 		mask->ptr = NULL;
@@ -1888,6 +1976,11 @@
 	if (!mask_info)
 		return -EIO;
 
+	if (!mask_info->ptr || !mask_info->update_buf) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK, mask_info->update_buf: %pK\n",
+			__func__, mask_info->ptr, mask_info->update_buf);
+		return -EINVAL;
+	}
 	mutex_lock(&driver->diag_maskclear_mutex);
 	if (driver->mask_clear) {
 		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
@@ -1900,6 +1993,13 @@
 	mutex_lock(&driver->msg_mask_lock);
 
 	mask = (struct diag_msg_mask_t *)(mask_info->ptr);
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		ptr = mask_info->update_buf;
 		len = 0;
@@ -1957,8 +2057,20 @@
 	if (!mask_info)
 		return -EIO;
 
+	if (!mask_info->ptr || !mask_info->update_buf) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK, mask_info->update_buf: %pK\n",
+			__func__, mask_info->ptr, mask_info->update_buf);
+		return -EINVAL;
+	}
+
 	mutex_lock(&mask_info->lock);
 	mask = (struct diag_log_mask_t *)(mask_info->ptr);
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
 		ptr = mask_info->update_buf;
 		len = 0;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index fc1b4e4..03fce64 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -39,6 +39,7 @@
 #include <soc/qcom/event_timer.h>
 #include <soc/qcom/lpm-stats.h>
 #include <soc/qcom/system_pm.h>
+#include <soc/qcom/minidump.h>
 #include <asm/arch_timer.h>
 #include <asm/suspend.h>
 #include <asm/cpuidle.h>
@@ -121,9 +122,6 @@
 		const struct cpumask *cpu, int child_idx, bool from_idle,
 		int64_t time);
 
-static bool menu_select;
-module_param_named(menu_select, menu_select, bool, 0664);
-
 static int msm_pm_sleep_time_override;
 module_param_named(sleep_time_override,
 	msm_pm_sleep_time_override, int, 0664);
@@ -981,7 +979,7 @@
 		if (suspend_in_progress && from_idle && level->notify_rpm)
 			continue;
 
-		if (level->is_reset && !system_sleep_allowed())
+		if (level->notify_rpm && !system_sleep_allowed())
 			continue;
 
 		best_level = i;
@@ -1641,6 +1639,7 @@
 	int ret;
 	int size;
 	struct kobject *module_kobj = NULL;
+	struct md_region md_entry;
 
 	get_online_cpus();
 	lpm_root_node = lpm_of_parse_cluster(pdev);
@@ -1697,6 +1696,14 @@
 		goto failed;
 	}
 
+	/* Add lpm_debug to Minidump*/
+	strlcpy(md_entry.name, "KLPMDEBUG", sizeof(md_entry.name));
+	md_entry.virt_addr = (uintptr_t)lpm_debug;
+	md_entry.phys_addr = lpm_debug_phys;
+	md_entry.size = size;
+	if (msm_minidump_add_region(&md_entry))
+		pr_info("Failed to add lpm_debug in Minidump\n");
+
 	return 0;
 failed:
 	free_cluster_node(lpm_root_node);
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index 71416f7..2f7a55d 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -33,7 +33,6 @@
 	struct power_params pwr;
 	unsigned int psci_id;
 	bool is_reset;
-	bool hyp_psci;
 	int reset_level;
 };
 
diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c
index ef80ec6..9b79a5b 100644
--- a/drivers/gpu/drm/drm_property.c
+++ b/drivers/gpu/drm/drm_property.c
@@ -557,7 +557,7 @@
 	if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
 		return ERR_PTR(-EINVAL);
 
-	blob = vmalloc(sizeof(struct drm_property_blob)+length);
+	blob = vzalloc(sizeof(struct drm_property_blob)+length);
 	if (!blob)
 		return ERR_PTR(-ENOMEM);
 
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index c237a23..cfb4436 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -338,6 +338,7 @@
 	struct drm_msm_ext_hdr_metadata *hdr;
 	void __iomem *base;
 	u32 header, parity, data;
+	u8 buf[SZ_128], off = 0;
 
 	if (!panel) {
 		pr_err("invalid input\n");
@@ -354,8 +355,8 @@
 	data   = ((header << HEADER_BYTE_1_BIT)
 			| (parity << PARITY_BYTE_1_BIT));
 	dp_write(base + MMSS_DP_VSCEXT_0, data);
-	pr_debug("Header#1: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_VSCEXT_0: 0x%x\n", data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	/* HEADER BYTE 2 */
 	header = panel->hdr_data.vscext_header_byte2;
@@ -363,8 +364,6 @@
 	data   = ((header << HEADER_BYTE_2_BIT)
 			| (parity << PARITY_BYTE_2_BIT));
 	dp_write(base + MMSS_DP_VSCEXT_1, data);
-	pr_debug("Header#2: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_VSCEXT_1: 0x%x\n", data);
 
 	/* HEADER BYTE 3 */
 	header = panel->hdr_data.vscext_header_byte3;
@@ -373,65 +372,80 @@
 			| (parity << PARITY_BYTE_3_BIT));
 	data |= dp_read(base + MMSS_DP_VSCEXT_1);
 	dp_write(base + MMSS_DP_VSCEXT_1, data);
-	pr_debug("Header#3: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_VSCEXT_1: 0x%x\n", data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = panel->hdr_data.version;
 	data |= panel->hdr_data.length << 8;
 	data |= hdr->eotf << 16;
-	pr_debug("DP_VSCEXT_2: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_2, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = (DP_GET_LSB(hdr->display_primaries_x[0]) |
 		(DP_GET_MSB(hdr->display_primaries_x[0]) << 8) |
 		(DP_GET_LSB(hdr->display_primaries_y[0]) << 16) |
 		(DP_GET_MSB(hdr->display_primaries_y[0]) << 24));
-	pr_debug("DP_VSCEXT_3: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_3, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = (DP_GET_LSB(hdr->display_primaries_x[1]) |
 		(DP_GET_MSB(hdr->display_primaries_x[1]) << 8) |
 		(DP_GET_LSB(hdr->display_primaries_y[1]) << 16) |
 		(DP_GET_MSB(hdr->display_primaries_y[1]) << 24));
-	pr_debug("DP_VSCEXT_4: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_4, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = (DP_GET_LSB(hdr->display_primaries_x[2]) |
 		(DP_GET_MSB(hdr->display_primaries_x[2]) << 8) |
 		(DP_GET_LSB(hdr->display_primaries_y[2]) << 16) |
 		(DP_GET_MSB(hdr->display_primaries_y[2]) << 24));
-	pr_debug("DP_VSCEXT_5: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_5, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = (DP_GET_LSB(hdr->white_point_x) |
 		(DP_GET_MSB(hdr->white_point_x) << 8) |
 		(DP_GET_LSB(hdr->white_point_y) << 16) |
 		(DP_GET_MSB(hdr->white_point_y) << 24));
-	pr_debug("DP_VSCEXT_6: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_6, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = (DP_GET_LSB(hdr->max_luminance) |
 		(DP_GET_MSB(hdr->max_luminance) << 8) |
 		(DP_GET_LSB(hdr->min_luminance) << 16) |
 		(DP_GET_MSB(hdr->min_luminance) << 24));
-	pr_debug("DP_VSCEXT_7: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_7, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	data = (DP_GET_LSB(hdr->max_content_light_level) |
 		(DP_GET_MSB(hdr->max_content_light_level) << 8) |
 		(DP_GET_LSB(hdr->max_average_light_level) << 16) |
 		(DP_GET_MSB(hdr->max_average_light_level) << 24));
-	pr_debug("DP_VSCEXT_8: 0x%x\n", data);
 	dp_write(base + MMSS_DP_VSCEXT_8, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
-	dp_write(base + MMSS_DP_VSCEXT_9, 0x00);
+	data = 0;
+	dp_write(base + MMSS_DP_VSCEXT_9, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	print_hex_dump(KERN_DEBUG, "[drm-dp] VSCEXT: ",
+			DUMP_PREFIX_NONE, 16, 4, buf, off, false);
 }
 
-static void dp_catalog_panel_setup_ext_sdp(struct dp_catalog_panel *panel)
+static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
 {
 	struct dp_catalog_private *catalog;
 	void __iomem *base;
 	u32 header, parity, data;
+	u8 bpc, off = 0;
+	u8 buf[SZ_128];
 
 	if (!panel) {
 		pr_err("invalid input\n");
@@ -442,57 +456,13 @@
 	base = catalog->io->dp_link.base;
 
 	/* HEADER BYTE 1 */
-	header = panel->hdr_data.ext_header_byte1;
-	parity = dp_header_get_parity(header);
-	data   = ((header << HEADER_BYTE_1_BIT)
-			| (parity << PARITY_BYTE_1_BIT));
-	dp_write(base + MMSS_DP_EXTENSION_0, data);
-	pr_debug("Header#1: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_EXTENSION_0: 0x%x\n", data);
-
-	/* HEADER BYTE 2 */
-	header = panel->hdr_data.ext_header_byte2;
-	parity = dp_header_get_parity(header);
-	data   = ((header << HEADER_BYTE_2_BIT)
-			| (parity << PARITY_BYTE_2_BIT));
-	dp_write(base + MMSS_DP_EXTENSION_1, data);
-	pr_debug("Header#2: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_EXTENSION_1: 0x%x\n", data);
-
-	dp_write(base + MMSS_DP_EXTENSION_1, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_2, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_3, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_4, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_5, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_6, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_7, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_8, 0x5AA55AA5);
-	dp_write(base + MMSS_DP_EXTENSION_9, 0x5AA55AA5);
-}
-
-static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
-{
-	struct dp_catalog_private *catalog;
-	void __iomem *base;
-	u32 header, parity, data;
-	u8 bpc;
-
-	if (!panel) {
-		pr_err("invalid input\n");
-		return;
-	}
-
-	dp_catalog_get_priv(panel);
-	base = catalog->io->ctrl_io.base;
-
-	/* HEADER BYTE 1 */
 	header = panel->hdr_data.vsc_header_byte1;
 	parity = dp_header_get_parity(header);
 	data   = ((header << HEADER_BYTE_1_BIT)
 			| (parity << PARITY_BYTE_1_BIT));
 	dp_write(base + MMSS_DP_GENERIC0_0, data);
-	pr_debug("Header#1: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_GENERIC0_0: 0x%x\n", data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	/* HEADER BYTE 2 */
 	header = panel->hdr_data.vsc_header_byte2;
@@ -500,8 +470,6 @@
 	data   = ((header << HEADER_BYTE_2_BIT)
 			| (parity << PARITY_BYTE_2_BIT));
 	dp_write(base + MMSS_DP_GENERIC0_1, data);
-	pr_debug("Header#2: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_GENERIC0_1: 0x%x\n", data);
 
 	/* HEADER BYTE 3 */
 	header = panel->hdr_data.vsc_header_byte3;
@@ -510,14 +478,25 @@
 			| (parity << PARITY_BYTE_3_BIT));
 	data |= dp_read(base + MMSS_DP_GENERIC0_1);
 	dp_write(base + MMSS_DP_GENERIC0_1, data);
-	pr_debug("Header#3: 0x%x, parity = 0x%x\n", header, parity);
-	pr_debug("DP_GENERIC0_1: 0x%x\n", data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
-	dp_write(base + MMSS_DP_GENERIC0_2, 0x00);
-	dp_write(base + MMSS_DP_GENERIC0_3, 0x00);
-	dp_write(base + MMSS_DP_GENERIC0_4, 0x00);
-	dp_write(base + MMSS_DP_GENERIC0_5, 0x00);
+	data = 0;
+	dp_write(base + MMSS_DP_GENERIC0_2, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
+	dp_write(base + MMSS_DP_GENERIC0_3, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(base + MMSS_DP_GENERIC0_4, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(base + MMSS_DP_GENERIC0_5, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
 
 	switch (panel->hdr_data.bpc) {
 	default:
@@ -538,18 +517,32 @@
 		((panel->hdr_data.dynamic_range & 0x1) << 15) |
 		((panel->hdr_data.content_type & 0x7) << 16);
 
-	pr_debug("DP_GENERIC0_6: 0x%x\n", data);
 	dp_write(base + MMSS_DP_GENERIC0_6, data);
-	dp_write(base + MMSS_DP_GENERIC0_7, 0x00);
-	dp_write(base + MMSS_DP_GENERIC0_8, 0x00);
-	dp_write(base + MMSS_DP_GENERIC0_9, 0x00);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	data = 0;
+	dp_write(base + MMSS_DP_GENERIC0_7, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(base + MMSS_DP_GENERIC0_8, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	dp_write(base + MMSS_DP_GENERIC0_9, data);
+	memcpy(buf + off, &data, sizeof(data));
+	off += sizeof(data);
+
+	print_hex_dump(KERN_DEBUG, "[drm-dp] VSC: ",
+			DUMP_PREFIX_NONE, 16, 4, buf, off, false);
 }
 
-static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel)
+static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en)
 {
 	struct dp_catalog_private *catalog;
 	void __iomem *base;
-	u32 cfg, cfg2;
+	u32 cfg, cfg2, misc;
 
 	if (!panel) {
 		pr_err("invalid input\n");
@@ -560,39 +553,44 @@
 	base = catalog->io->dp_link.base;
 
 	cfg = dp_read(base + MMSS_DP_SDP_CFG);
-	/* EXTENSION_SDP_EN */
-	cfg |= BIT(4);
-
-	/* VSCEXT_SDP_EN */
-	cfg |= BIT(16);
-
-	/* GEN0_SDP_EN */
-	cfg |= BIT(17);
-
-	/* GEN1_SDP_EN */
-	cfg |= BIT(18);
-	dp_write(base + MMSS_DP_SDP_CFG, cfg);
-
 	cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
-	/* EXTN_SDPSIZE */
-	cfg2 |= BIT(15);
+	misc = dp_read(base + DP_MISC1_MISC0);
 
-	/* GENERIC0_SDPSIZE */
-	cfg |= BIT(16);
+	if (en) {
+		/* VSCEXT_SDP_EN, GEN0_SDP_EN */
+		cfg |= BIT(16) | BIT(17);
+		dp_write(base + MMSS_DP_SDP_CFG, cfg);
 
-	/* GENERIC1_SDPSIZE */
-	cfg |= BIT(17);
-	dp_write(base + MMSS_DP_SDP_CFG2, cfg2);
+		/* EXTN_SDPSIZE GENERIC0_SDPSIZE */
+		cfg2 |= BIT(15) | BIT(16);
+		dp_write(base + MMSS_DP_SDP_CFG2, cfg2);
 
-	dp_catalog_panel_setup_ext_sdp(panel);
-	dp_catalog_panel_setup_vsc_sdp(panel);
-	dp_catalog_panel_setup_infoframe_sdp(panel);
+		dp_catalog_panel_setup_vsc_sdp(panel);
+		dp_catalog_panel_setup_infoframe_sdp(panel);
 
-	cfg = dp_read(base + DP_MISC1_MISC0);
-	/* Indicates presence of VSC */
-	cfg |= BIT(6) << 8;
+		/* indicates presence of VSC (BIT(6) of MISC1) */
+		misc |= BIT(14);
 
-	dp_write(base + DP_MISC1_MISC0, cfg);
+		if (panel->hdr_data.hdr_meta.eotf)
+			pr_debug("Enabled\n");
+		else
+			pr_debug("Reset\n");
+	} else {
+		/* VSCEXT_SDP_EN, GEN0_SDP_EN */
+		cfg &= ~BIT(16) & ~BIT(17);
+		dp_write(base + MMSS_DP_SDP_CFG, cfg);
+
+		/* EXTN_SDPSIZE GENERIC0_SDPSIZE */
+		cfg2 &= ~BIT(15) & ~BIT(16);
+		dp_write(base + MMSS_DP_SDP_CFG2, cfg2);
+
+		/* switch back to MSA */
+		misc &= ~BIT(14);
+
+		pr_debug("Disabled\n");
+	}
+
+	dp_write(base + DP_MISC1_MISC0, misc);
 
 	dp_write(base + MMSS_DP_SDP_CFG3, 0x01);
 	dp_write(base + MMSS_DP_SDP_CFG3, 0x00);
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index b270545..d03be6a 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -171,7 +171,7 @@
 	u32 display_hctl;
 
 	int (*timing_cfg)(struct dp_catalog_panel *panel);
-	void (*config_hdr)(struct dp_catalog_panel *panel);
+	void (*config_hdr)(struct dp_catalog_panel *panel, bool en);
 	void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
 	void (*config_spd)(struct dp_catalog_panel *panel);
 };
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 576ed52..006f723 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1168,9 +1168,17 @@
 	return false;
 }
 
-static int dp_ctrl_link_maintenance(struct dp_ctrl_private *ctrl)
+static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)
 {
 	int ret = 0;
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("Invalid input data\n");
+		return -EINVAL;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
 
 	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
 	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
@@ -1214,9 +1222,17 @@
 	return ret;
 }
 
-static void dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
+static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl)
 {
 	int ret = 0;
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
 
 	if (!ctrl->link->phy_params.phy_test_pattern_sel) {
 		pr_debug("no test pattern selected by sink\n");
@@ -1294,45 +1310,6 @@
 			dp_link_get_phy_test_pattern(pattern_requested));
 }
 
-static bool dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
-{
-	struct dp_ctrl_private *ctrl;
-	u32 sink_request = 0x0;
-	bool req_handled = false;
-
-	if (!dp_ctrl) {
-		pr_err("invalid input\n");
-		goto end;
-	}
-
-	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
-	sink_request = ctrl->link->sink_request;
-
-	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
-		pr_info("PHY_TEST_PATTERN\n");
-		dp_ctrl_process_phy_test_request(ctrl);
-
-		req_handled = true;
-	}
-
-	if (sink_request & DP_LINK_STATUS_UPDATED) {
-		pr_info("DP_LINK_STATUS_UPDATED\n");
-		dp_ctrl_link_maintenance(ctrl);
-
-		req_handled = true;
-	}
-
-	if (sink_request & DP_TEST_LINK_TRAINING) {
-		pr_info("DP_TEST_LINK_TRAINING\n");
-		ctrl->link->send_test_response(ctrl->link);
-		dp_ctrl_link_maintenance(ctrl);
-
-		req_handled = true;
-	}
-end:
-	return req_handled;
-}
-
 static void dp_ctrl_reset(struct dp_ctrl *dp_ctrl)
 {
 	struct dp_ctrl_private *ctrl;
@@ -1505,7 +1482,8 @@
 	dp_ctrl->abort     = dp_ctrl_abort;
 	dp_ctrl->isr       = dp_ctrl_isr;
 	dp_ctrl->reset	   = dp_ctrl_reset;
-	dp_ctrl->handle_sink_request = dp_ctrl_handle_sink_request;
+	dp_ctrl->link_maintenance = dp_ctrl_link_maintenance;
+	dp_ctrl->process_phy_test_request = dp_ctrl_process_phy_test_request;
 
 	return dp_ctrl;
 error:
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index aaac0ab..229c779 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -32,6 +32,8 @@
 	void (*abort)(struct dp_ctrl *dp_ctrl);
 	void (*isr)(struct dp_ctrl *dp_ctrl);
 	bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
+	void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl);
+	int (*link_maintenance)(struct dp_ctrl *dp_ctrl);
 };
 
 struct dp_ctrl_in {
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index d00f159..6342fef 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -23,6 +23,7 @@
 #include "dp_ctrl.h"
 #include "dp_debug.h"
 #include "drm_connector.h"
+#include "sde_connector.h"
 #include "dp_display.h"
 
 #define DEBUG_NAME "drm_dp"
@@ -618,6 +619,201 @@
 	return len;
 }
 
+static ssize_t dp_debug_write_hdr(struct file *file,
+	const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct drm_connector *connector;
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_1K];
+	size_t len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	connector = *debug->connector;
+	c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(connector->state);
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_1K - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
+			&c_state->hdr_meta.hdr_supported,
+			&c_state->hdr_meta.hdr_state,
+			&c_state->hdr_meta.eotf,
+			&c_state->hdr_meta.display_primaries_x[0],
+			&c_state->hdr_meta.display_primaries_x[1],
+			&c_state->hdr_meta.display_primaries_x[2],
+			&c_state->hdr_meta.display_primaries_y[0],
+			&c_state->hdr_meta.display_primaries_y[1],
+			&c_state->hdr_meta.display_primaries_y[2],
+			&c_state->hdr_meta.white_point_x,
+			&c_state->hdr_meta.white_point_y,
+			&c_state->hdr_meta.max_luminance,
+			&c_state->hdr_meta.min_luminance,
+			&c_state->hdr_meta.max_content_light_level,
+			&c_state->hdr_meta.max_average_light_level) != 15) {
+		pr_err("invalid input\n");
+		len = -EINVAL;
+	}
+end:
+	return len;
+}
+
+static ssize_t dp_debug_read_hdr(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0, i;
+	u32 max_size = SZ_4K;
+	int rc = 0;
+	struct drm_connector *connector;
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+	struct drm_msm_ext_hdr_metadata *hdr;
+
+	if (!debug) {
+		pr_err("invalid data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	connector = *debug->connector;
+
+	if (!connector) {
+		pr_err("connector is NULL\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (*ppos)
+		goto error;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(connector->state);
+
+	hdr = &c_state->hdr_meta;
+
+	rc = snprintf(buf + len, max_size,
+		"============SINK HDR PARAMETERS===========\n");
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "eotf = %d\n",
+		connector->hdr_eotf);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "type_one = %d\n",
+		connector->hdr_metadata_type_one);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "max_luminance = %d\n",
+		connector->hdr_max_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "avg_luminance = %d\n",
+		connector->hdr_avg_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "min_luminance = %d\n",
+		connector->hdr_min_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"============VIDEO HDR PARAMETERS===========\n");
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "hdr_state = %d\n", hdr->hdr_state);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "hdr_supported = %d\n",
+			hdr->hdr_supported);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "eotf = %d\n", hdr->eotf);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "white_point_x = %d\n",
+		hdr->white_point_x);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "white_point_y = %d\n",
+		hdr->white_point_y);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "max_luminance = %d\n",
+		hdr->max_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "min_luminance = %d\n",
+		hdr->min_luminance);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "max_content_light_level = %d\n",
+		hdr->max_content_light_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size, "min_content_light_level = %d\n",
+		hdr->max_average_light_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	for (i = 0; i < HDR_PRIMARIES_COUNT; i++) {
+		rc = snprintf(buf + len, max_size, "primaries_x[%d] = %d\n",
+			i, hdr->display_primaries_x[i]);
+		if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+			goto error;
+
+		rc = snprintf(buf + len, max_size, "primaries_y[%d] = %d\n",
+			i, hdr->display_primaries_y[i]);
+		if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+			goto error;
+	}
+
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		rc = -EFAULT;
+		goto error;
+	}
+
+	*ppos += len;
+	kfree(buf);
+
+	return len;
+error:
+	return rc;
+}
+
 static const struct file_operations dp_debug_fops = {
 	.open = simple_open,
 	.read = dp_debug_read_info,
@@ -661,6 +857,12 @@
 	.write = dp_debug_tpg_write,
 };
 
+static const struct file_operations hdr_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_hdr,
+	.read = dp_debug_read_hdr,
+};
+
 static int dp_debug_init(struct dp_debug *dp_debug)
 {
 	int rc = 0;
@@ -753,6 +955,16 @@
 		goto error_remove_dir;
 	}
 
+	file = debugfs_create_file("hdr", 0644, dir,
+		debug, &hdr_fops);
+
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		pr_err("[%s] debugfs hdr failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
 	return 0;
 
 error_remove_dir:
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 01a2a9c..884dd68 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -655,9 +655,23 @@
 	return rc;
 }
 
+static void dp_display_handle_maintenance_req(struct dp_display_private *dp)
+{
+	mutex_lock(&dp->audio->ops_lock);
+
+	if (dp->audio_supported)
+		dp->audio->off(dp->audio);
+
+	dp->ctrl->link_maintenance(dp->ctrl);
+
+	if (dp->audio_supported)
+		dp->audio->on(dp->audio);
+
+	mutex_unlock(&dp->audio->ops_lock);
+}
+
 static void dp_display_attention_work(struct work_struct *work)
 {
-	bool req_handled;
 	struct dp_display_private *dp = container_of(work,
 			struct dp_display_private, attention_work);
 
@@ -688,17 +702,20 @@
 		return;
 	}
 
-	mutex_lock(&dp->audio->ops_lock);
-	req_handled = dp->ctrl->handle_sink_request(dp->ctrl);
-	mutex_unlock(&dp->audio->ops_lock);
+	if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		dp->ctrl->process_phy_test_request(dp->ctrl);
+		return;
+	}
 
-	/*
-	 * reconfigure audio if test was executed
-	 * which could have changed the contoller's state
-	 */
-	if (req_handled && dp->audio_supported) {
-		dp->audio->off(dp->audio);
-		dp->audio->on(dp->audio);
+	if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) {
+		dp_display_handle_maintenance_req(dp);
+		return;
+	}
+
+	if (dp->link->sink_request & DP_TEST_LINK_TRAINING) {
+		dp->link->send_test_response(dp->link);
+		dp_display_handle_maintenance_req(dp);
+		return;
 	}
 }
 
@@ -1179,6 +1196,7 @@
 static int dp_display_pre_kickoff(struct dp_display *dp_display,
 			struct drm_msm_ext_hdr_metadata *hdr)
 {
+	int rc = 0;
 	struct dp_display_private *dp;
 
 	if (!dp_display) {
@@ -1188,7 +1206,10 @@
 
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
 
-	return dp->panel->setup_hdr(dp->panel, hdr);
+	if (hdr->hdr_supported)
+		rc = dp->panel->setup_hdr(dp->panel, hdr);
+
+	return rc;
 }
 
 static int dp_display_create_workqueue(struct dp_display_private *dp)
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 96f9d3a..02edafb 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -19,8 +19,41 @@
 #define DP_PANEL_DEFAULT_BPP 24
 #define DP_MAX_DS_PORT_COUNT 1
 
-enum {
-	DP_LINK_RATE_MULTIPLIER = 27000000,
+enum dp_panel_hdr_pixel_encoding {
+	RGB,
+	YCbCr444,
+	YCbCr422,
+	YCbCr420,
+	YONLY,
+	RAW,
+};
+
+enum dp_panel_hdr_rgb_colorimetry {
+	sRGB,
+	RGB_WIDE_GAMUT_FIXED_POINT,
+	RGB_WIDE_GAMUT_FLOATING_POINT,
+	ADOBERGB,
+	DCI_P3,
+	CUSTOM_COLOR_PROFILE,
+	ITU_R_BT_2020_RGB,
+};
+
+enum dp_panel_hdr_dynamic_range {
+	VESA,
+	CEA,
+};
+
+enum dp_panel_hdr_content_type {
+	NOT_DEFINED,
+	GRAPHICS,
+	PHOTO,
+	VIDEO,
+	GAME,
+};
+
+enum dp_panel_hdr_state {
+	HDR_DISABLED,
+	HDR_ENABLED,
 };
 
 struct dp_panel_private {
@@ -32,6 +65,7 @@
 	bool custom_edid;
 	bool custom_dpcd;
 	bool panel_on;
+	enum dp_panel_hdr_state hdr_state;
 	u8 spd_vendor_name[8];
 	u8 spd_product_description[16];
 };
@@ -621,37 +655,29 @@
 	return min_link_rate_khz;
 }
 
-enum dp_panel_hdr_pixel_encoding {
-	RGB,
-	YCbCr444,
-	YCbCr422,
-	YCbCr420,
-	YONLY,
-	RAW,
-};
+static bool dp_panel_is_validate_hdr_state(struct dp_panel_private *panel,
+		struct drm_msm_ext_hdr_metadata *hdr_meta)
+{
+	struct drm_msm_ext_hdr_metadata *panel_hdr_meta =
+			&panel->catalog->hdr_data.hdr_meta;
 
-enum dp_panel_hdr_rgb_colorimetry {
-	sRGB,
-	RGB_WIDE_GAMUT_FIXED_POINT,
-	RGB_WIDE_GAMUT_FLOATING_POINT,
-	ADOBERGB,
-	DCI_P3,
-	CUSTOM_COLOR_PROFILE,
-	ITU_R_BT_2020_RGB,
-};
+	if (!hdr_meta)
+		goto end;
 
-enum dp_panel_hdr_dynamic_range {
-	VESA,
-	CEA,
-};
+	/* bail out if HDR not active */
+	if (hdr_meta->hdr_state == HDR_DISABLED &&
+	    panel->hdr_state == HDR_DISABLED)
+		goto end;
 
-enum dp_panel_hdr_content_type {
-	NOT_DEFINED,
-	GRAPHICS,
-	PHOTO,
-	VIDEO,
-	GAME,
-};
+	/* bail out if same meta data is received */
+	if (hdr_meta->hdr_state == HDR_ENABLED &&
+		panel_hdr_meta->eotf == hdr_meta->eotf)
+		goto end;
+
+	return true;
+end:
+	return false;
+}
 
 static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
 		struct drm_msm_ext_hdr_metadata *hdr_meta)
@@ -660,9 +686,6 @@
 	struct dp_panel_private *panel;
 	struct dp_catalog_hdr_data *hdr;
 
-	if (!hdr_meta || !hdr_meta->hdr_state)
-		goto end;
-
 	if (!dp_panel) {
 		pr_err("invalid input\n");
 		rc = -EINVAL;
@@ -670,6 +693,12 @@
 	}
 
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (!dp_panel_is_validate_hdr_state(panel, hdr_meta))
+		goto end;
+
+	panel->hdr_state = hdr_meta->hdr_state;
+
 	hdr = &panel->catalog->hdr_data;
 
 	hdr->ext_header_byte0 = 0x00;
@@ -682,6 +711,11 @@
 	hdr->vsc_header_byte2 = 0x05;
 	hdr->vsc_header_byte3 = 0x13;
 
+	hdr->vscext_header_byte0 = 0x00;
+	hdr->vscext_header_byte1 = 0x87;
+	hdr->vscext_header_byte2 = 0x1D;
+	hdr->vscext_header_byte3 = 0x13 << 2;
+
 	/* VSC SDP Payload for DB16 */
 	hdr->pixel_encoding = RGB;
 	hdr->colorimetry = ITU_R_BT_2020_RGB;
@@ -694,17 +728,12 @@
 
 	hdr->bpc = dp_panel->pinfo.bpp / 3;
 
-	hdr->vscext_header_byte0 = 0x00;
-	hdr->vscext_header_byte1 = 0x87;
-	hdr->vscext_header_byte2 = 0x1D;
-	hdr->vscext_header_byte3 = 0x13 << 2;
-
 	hdr->version = 0x01;
 	hdr->length = 0x1A;
 
 	memcpy(&hdr->hdr_meta, hdr_meta, sizeof(hdr->hdr_meta));
 
-	panel->catalog->config_hdr(panel->catalog);
+	panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
 end:
 	return rc;
 }
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 982d16e..2286603 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -4453,8 +4453,9 @@
 	void *data;
 	u32 version = 0;
 
-	display =  container_of(work, struct dsi_display, fifo_overflow_work);
-	if (!display || (display->panel->panel_mode != DSI_OP_VIDEO_MODE))
+	display =  container_of(work, struct dsi_display, lp_rx_timeout_work);
+	if (!display || !display->panel ||
+			(display->panel->panel_mode != DSI_OP_VIDEO_MODE))
 		return;
 	pr_debug("handle DSI LP RX Timeout error\n");
 
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index f68f64d..057d4a9 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -3957,12 +3957,6 @@
 
 		sde_cp_crtc_post_ipc(crtc);
 
-		event.type = DRM_EVENT_SDE_POWER;
-		event.length = sizeof(power_on);
-		power_on = 1;
-		msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
-				(u8 *)&power_on);
-
 		for (i = 0; i < sde_crtc->num_mixers; ++i) {
 			m = &sde_crtc->mixers[i];
 			if (!m->hw_lm || !m->hw_lm->ops.setup_misr ||
diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
index 9a75179..f157b11 100644
--- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c
+++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
@@ -34,19 +34,21 @@
 		struct sde_dbg_evtlog *evtlog, const char *str)
 {
 	struct sde_evtlog_filter *filter_node;
+	size_t len;
 	bool rc;
 
 	if (!str)
 		return true;
 
+	len = strlen(str);
+
 	/*
 	 * Filter the incoming string IFF the list is not empty AND
 	 * a matching entry is not in the list.
 	 */
 	rc = !list_empty(&evtlog->filter_list);
 	list_for_each_entry(filter_node, &evtlog->filter_list, list)
-		if (strnstr(str, filter_node->filter,
-					SDE_EVTLOG_FILTER_STRSIZE - 1)) {
+		if (strnstr(str, filter_node->filter, len)) {
 			rc = false;
 			break;
 		}
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index 9a9e1fc..770cf3b 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -347,7 +347,8 @@
 		.minor = 0,
 		.patchid = ANY_ID,
 		.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_IFPC |
-			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
+			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM |
+			ADRENO_IOCOHERENT,
 		.sqefw_name = "a630_sqe.fw",
 		.zap_name = "a630_zap",
 		.gpudev = &adreno_a6xx_gpudev,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 373264a..13fe0a7 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -119,6 +119,7 @@
 		.skipsaverestore = 1,
 		.usesgmem = 1,
 	},
+	.priv = BIT(ADRENO_DEVICE_PREEMPTION_EXECUTION),
 };
 
 /* Ptr to array for the current set of fault detect registers */
@@ -1148,6 +1149,9 @@
 	if (!ADRENO_FEATURE(adreno_dev, ADRENO_CONTENT_PROTECTION))
 		device->mmu.secured = false;
 
+	if (ADRENO_FEATURE(adreno_dev, ADRENO_IOCOHERENT))
+		device->mmu.features |= KGSL_MMU_IO_COHERENT;
+
 	status = adreno_ringbuffer_probe(adreno_dev, nopreempt);
 	if (status)
 		goto out;
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 6f5fb6b..0b4e1df 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -121,6 +121,8 @@
 #define ADRENO_HW_NAP BIT(14)
 /* The GMU supports min voltage*/
 #define ADRENO_MIN_VOLT BIT(15)
+/* The core supports IO-coherent memory */
+#define ADRENO_IOCOHERENT BIT(16)
 
 /*
  * Adreno GPU quirks - control bits for various workarounds
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index 6dc62866..d1a6005 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -621,7 +621,8 @@
 	header->index = info->bank;
 	header->size = block->sz;
 
-	memcpy(data, registers.hostptr + info->offset, block->sz);
+	memcpy(data, registers.hostptr + info->offset,
+		block->sz * sizeof(unsigned int));
 
 	return SHADER_SECTION_SZ(block->sz);
 }
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 0b18a34..1d065f4 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -268,7 +268,8 @@
 	{ 0x0, 0x4F9, 0 },
 	{ 0x501, 0xA, 0 },
 	{ 0x511, 0x44, 0 },
-	{ 0xE00, 0xE, 1 },
+	{ 0xE00, 0x1, 1 },
+	{ 0xE03, 0xB, 1 },
 	{ 0x8E00, 0x0, 1 },
 	{ 0x8E50, 0xF, 1 },
 	{ 0xBE02, 0x0, 1 },
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index c1a76bc..b9a2f8d 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -640,7 +640,7 @@
 	header->size = block->sz;
 
 	memcpy(data, a6xx_crashdump_registers.hostptr + info->offset,
-		block->sz);
+		block->sz * sizeof(unsigned int));
 
 	return SHADER_SECTION_SZ(block->sz);
 }
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index a5e3804..5d07380 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2294,7 +2294,8 @@
 	param->flags &= KGSL_MEMFLAGS_GPUREADONLY
 		| KGSL_CACHEMODE_MASK
 		| KGSL_MEMTYPE_MASK
-		| KGSL_MEMFLAGS_FORCE_32BIT;
+		| KGSL_MEMFLAGS_FORCE_32BIT
+		| KGSL_MEMFLAGS_IOCOHERENT;
 
 	/* Specifying SECURE is an explicit error */
 	if (param->flags & KGSL_MEMFLAGS_SECURE)
@@ -2388,7 +2389,12 @@
 			| KGSL_MEMALIGN_MASK
 			| KGSL_MEMFLAGS_USE_CPU_MAP
 			| KGSL_MEMFLAGS_SECURE
-			| KGSL_MEMFLAGS_FORCE_32BIT;
+			| KGSL_MEMFLAGS_FORCE_32BIT
+			| KGSL_MEMFLAGS_IOCOHERENT;
+
+	/* Disable IO coherence if it is not supported on the chip */
+	if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
+		param->flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
 
 	entry->memdesc.flags = param->flags;
 
@@ -2673,7 +2679,13 @@
 			| KGSL_MEMTYPE_MASK
 			| KGSL_MEMALIGN_MASK
 			| KGSL_MEMFLAGS_USE_CPU_MAP
-			| KGSL_MEMFLAGS_SECURE;
+			| KGSL_MEMFLAGS_SECURE
+			| KGSL_MEMFLAGS_IOCOHERENT;
+
+	/* Disable IO coherence if it is not supported on the chip */
+	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;
 
@@ -3072,6 +3084,7 @@
 	int ret;
 	struct kgsl_process_private *private = dev_priv->process_priv;
 	struct kgsl_mem_entry *entry;
+	struct kgsl_mmu *mmu = &dev_priv->device->mmu;
 	unsigned int align;
 
 	flags &= KGSL_MEMFLAGS_GPUREADONLY
@@ -3080,14 +3093,15 @@
 		| KGSL_MEMALIGN_MASK
 		| KGSL_MEMFLAGS_USE_CPU_MAP
 		| KGSL_MEMFLAGS_SECURE
-		| KGSL_MEMFLAGS_FORCE_32BIT;
+		| KGSL_MEMFLAGS_FORCE_32BIT
+		| KGSL_MEMFLAGS_IOCOHERENT;
 
 	/* Turn off SVM if the system doesn't support it */
-	if (!kgsl_mmu_use_cpu_map(&dev_priv->device->mmu))
+	if (!kgsl_mmu_use_cpu_map(mmu))
 		flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
 
 	/* Return not supported error if secure memory isn't enabled */
-	if (!kgsl_mmu_is_secured(&dev_priv->device->mmu) &&
+	if (!kgsl_mmu_is_secured(mmu) &&
 			(flags & KGSL_MEMFLAGS_SECURE)) {
 		dev_WARN_ONCE(dev_priv->device->dev, 1,
 				"Secure memory not supported");
@@ -3116,11 +3130,15 @@
 
 	flags = kgsl_filter_cachemode(flags);
 
+	/* Disable IO coherence if it is not supported on the chip */
+	if (!MMU_FEATURE(mmu, KGSL_MMU_IO_COHERENT))
+		flags &= ~((uint64_t)KGSL_MEMFLAGS_IOCOHERENT);
+
 	entry = kgsl_mem_entry_create();
 	if (entry == NULL)
 		return ERR_PTR(-ENOMEM);
 
-	if (MMU_FEATURE(&dev_priv->device->mmu, KGSL_MMU_NEED_GUARD_PAGE))
+	if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE))
 		entry->memdesc.priv |= KGSL_MEMDESC_GUARD_PAGE;
 
 	if (flags & KGSL_MEMFLAGS_SECURE)
diff --git a/drivers/gpu/msm/kgsl_hfi.c b/drivers/gpu/msm/kgsl_hfi.c
index eef5f45..3a5b489 100644
--- a/drivers/gpu/msm/kgsl_hfi.c
+++ b/drivers/gpu/msm/kgsl_hfi.c
@@ -183,7 +183,7 @@
 		rsp->ret_hdr.size,
 		rsp->ret_hdr.seqnum);
 
-	spin_lock(&hfi->msglock);
+	spin_lock_bh(&hfi->msglock);
 	list_for_each_entry_safe(msg, next, &hfi->msglist, node) {
 		if (msg->msg_id == rsp->ret_hdr.id &&
 				msg->seqnum == rsp->ret_hdr.seqnum) {
@@ -193,7 +193,7 @@
 	}
 
 	if (in_queue == false) {
-		spin_unlock(&hfi->msglock);
+		spin_unlock_bh(&hfi->msglock);
 		dev_err(&gmu->pdev->dev,
 				"Cannot find receiver of ack msg with id=%d\n",
 				rsp->ret_hdr.id);
@@ -202,7 +202,7 @@
 
 	memcpy(&msg->results, (void *) rsp, rsp->hdr.size << 2);
 	complete(&msg->msg_complete);
-	spin_unlock(&hfi->msglock);
+	spin_unlock_bh(&hfi->msglock);
 }
 
 static void receive_err_msg(struct gmu_device *gmu, struct hfi_msg_rsp *rsp)
@@ -231,9 +231,9 @@
 	ret_msg->msg_id = msg->id;
 	ret_msg->seqnum = msg->seqnum;
 
-	spin_lock(&hfi->msglock);
+	spin_lock_bh(&hfi->msglock);
 	list_add_tail(&ret_msg->node, &hfi->msglist);
-	spin_unlock(&hfi->msglock);
+	spin_unlock_bh(&hfi->msglock);
 
 	if (hfi_cmdq_write(gmu, HFI_CMD_QUEUE, msg) != size) {
 		rc = -EINVAL;
@@ -253,9 +253,9 @@
 	/* If we got here we succeeded */
 	rc = 0;
 done:
-	spin_lock(&hfi->msglock);
+	spin_lock_bh(&hfi->msglock);
 	list_del(&ret_msg->node);
-	spin_unlock(&hfi->msglock);
+	spin_unlock_bh(&hfi->msglock);
 	return rc;
 }
 
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 8fc2c82..ab3ab31 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -1835,6 +1835,9 @@
 	if (memdesc->priv & KGSL_MEMDESC_PRIVILEGED)
 		flags |= IOMMU_PRIV;
 
+	if (memdesc->flags & KGSL_MEMFLAGS_IOCOHERENT)
+		flags |= IOMMU_CACHE;
+
 	return flags;
 }
 
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 2ea904e..430a140 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -138,6 +138,8 @@
 #define KGSL_MMU_PAGED BIT(8)
 /* The device requires a guard page */
 #define KGSL_MMU_NEED_GUARD_PAGE BIT(9)
+/* The device supports IO coherency */
+#define KGSL_MMU_IO_COHERENT BIT(10)
 
 /**
  * struct kgsl_mmu - Master definition for KGSL MMU devices
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 32fac88..6825c2b 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -843,13 +843,17 @@
 	}
 
 	b = pwr->bus_mod;
-	if (_check_fast_hint(bus_flag) &&
-		((pwr_level->bus_freq + pwr->bus_mod) < pwr_level->bus_max))
+	if (_check_fast_hint(bus_flag))
 		pwr->bus_mod++;
-	else if (_check_slow_hint(bus_flag) &&
-		((pwr_level->bus_freq + pwr->bus_mod) > pwr_level->bus_min))
+	else if (_check_slow_hint(bus_flag))
 		pwr->bus_mod--;
 
+	/* trim calculated change to fit range */
+	if (pwr_level->bus_freq + pwr->bus_mod < pwr_level->bus_min)
+		pwr->bus_mod = -(pwr_level->bus_freq - pwr_level->bus_min);
+	else if (pwr_level->bus_freq + pwr->bus_mod > pwr_level->bus_max)
+		pwr->bus_mod = pwr_level->bus_max - pwr_level->bus_freq;
+
 	/* Update bus vote if AB or IB is modified */
 	if ((pwr->bus_mod != b) || (pwr->bus_ab_mbytes != ab_mbytes)) {
 		pwr->bus_percent_ab = device->pwrscale.bus_profile.percent_ab;
diff --git a/drivers/gpu/msm/kgsl_sync.h b/drivers/gpu/msm/kgsl_sync.h
index d58859d..7c9f334e 100644
--- a/drivers/gpu/msm/kgsl_sync.h
+++ b/drivers/gpu/msm/kgsl_sync.h
@@ -13,7 +13,7 @@
 #ifndef __KGSL_SYNC_H
 #define __KGSL_SYNC_H
 
-#include "sync_file.h"
+#include <linux/sync_file.h>
 #include "kgsl_device.h"
 
 #define KGSL_TIMELINE_NAME_LEN 32
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 280ce0cf..45fbd09 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -4865,7 +4865,7 @@
 	 * Prefetch only works properly if the start and end of all
 	 * buffers in the page table are aligned to 16 Kb.
 	 */
-	if ((iommudata->actlr >> QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT) &&
+	if ((iommudata->actlr >> QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT) &
 			QSMMUV500_ACTLR_DEEP_PREFETCH_MASK)
 		smmu_domain->qsmmuv500_errata2_min_align = true;
 
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 785d689..b8f30cd 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -668,6 +668,15 @@
 	  LEDs in both PWM and light pattern generator (LPG) modes.  For older
 	  PMICs, it also supports WLEDs and flash LEDs.
 
+config LEDS_QPNP_FLASH
+	tristate "Support for QPNP Flash LEDs"
+	depends on LEDS_CLASS && MFD_SPMI_PMIC
+	help
+	  This driver supports the flash LED functionality of Qualcomm
+	  Technologies, Inc. QPNP PMICs.  This driver supports PMICs up through
+	  PM8994.  It can configure the flash LED target current for several
+	  independent channels.
+
 config LEDS_QPNP_FLASH_V2
 	tristate "Support for QPNP V2 Flash LEDs"
 	depends on LEDS_CLASS && MFD_SPMI_PMIC
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 2ff9a7c..ba9bb8d 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -72,6 +72,7 @@
 obj-$(CONFIG_LEDS_PM8058)		+= leds-pm8058.o
 obj-$(CONFIG_LEDS_MLXCPLD)		+= leds-mlxcpld.o
 obj-$(CONFIG_LEDS_QPNP)			+= leds-qpnp.o
+obj-$(CONFIG_LEDS_QPNP_FLASH)		+= leds-qpnp-flash.o
 obj-$(CONFIG_LEDS_QPNP_FLASH_V2)	+= leds-qpnp-flash-v2.o
 obj-$(CONFIG_LEDS_QPNP_WLED)		+= leds-qpnp-wled.o
 obj-$(CONFIG_LEDS_QPNP_HAPTICS)	+= leds-qpnp-haptics.o
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
new file mode 100644
index 0000000..3b07af8
--- /dev/null
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -0,0 +1,2709 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/errno.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <linux/power_supply.h>
+#include <linux/leds-qpnp-flash.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include "leds.h"
+
+#define FLASH_LED_PERIPHERAL_SUBTYPE(base)			(base + 0x05)
+#define FLASH_SAFETY_TIMER(base)				(base + 0x40)
+#define FLASH_MAX_CURRENT(base)					(base + 0x41)
+#define FLASH_LED0_CURRENT(base)				(base + 0x42)
+#define FLASH_LED1_CURRENT(base)				(base + 0x43)
+#define	FLASH_CLAMP_CURRENT(base)				(base + 0x44)
+#define FLASH_MODULE_ENABLE_CTRL(base)				(base + 0x46)
+#define	FLASH_LED_STROBE_CTRL(base)				(base + 0x47)
+#define FLASH_LED_TMR_CTRL(base)				(base + 0x48)
+#define FLASH_HEADROOM(base)					(base + 0x4A)
+#define	FLASH_STARTUP_DELAY(base)				(base + 0x4B)
+#define FLASH_MASK_ENABLE(base)					(base + 0x4C)
+#define FLASH_VREG_OK_FORCE(base)				(base + 0x4F)
+#define FLASH_FAULT_DETECT(base)				(base + 0x51)
+#define	FLASH_THERMAL_DRATE(base)				(base + 0x52)
+#define	FLASH_CURRENT_RAMP(base)				(base + 0x54)
+#define	FLASH_VPH_PWR_DROOP(base)				(base + 0x5A)
+#define	FLASH_HDRM_SNS_ENABLE_CTRL0(base)			(base + 0x5C)
+#define	FLASH_HDRM_SNS_ENABLE_CTRL1(base)			(base + 0x5D)
+#define	FLASH_LED_UNLOCK_SECURE(base)				(base + 0xD0)
+#define FLASH_PERPH_RESET_CTRL(base)				(base + 0xDA)
+#define	FLASH_TORCH(base)					(base + 0xE4)
+
+#define FLASH_STATUS_REG_MASK					0xFF
+#define FLASH_LED_FAULT_STATUS(base)				(base + 0x08)
+#define INT_LATCHED_STS(base)					(base + 0x18)
+#define IN_POLARITY_HIGH(base)					(base + 0x12)
+#define INT_SET_TYPE(base)					(base + 0x11)
+#define INT_EN_SET(base)					(base + 0x15)
+#define INT_LATCHED_CLR(base)					(base + 0x14)
+
+#define	FLASH_HEADROOM_MASK					0x03
+#define FLASH_STARTUP_DLY_MASK					0x03
+#define	FLASH_VREG_OK_FORCE_MASK				0xC0
+#define	FLASH_FAULT_DETECT_MASK					0x80
+#define	FLASH_THERMAL_DERATE_MASK				0xBF
+#define FLASH_SECURE_MASK					0xFF
+#define FLASH_TORCH_MASK					0x03
+#define FLASH_CURRENT_MASK					0x7F
+#define FLASH_TMR_MASK						0x03
+#define FLASH_TMR_SAFETY					0x00
+#define FLASH_SAFETY_TIMER_MASK					0x7F
+#define FLASH_MODULE_ENABLE_MASK				0xE0
+#define FLASH_STROBE_MASK					0xC0
+#define FLASH_CURRENT_RAMP_MASK					0xBF
+#define FLASH_VPH_PWR_DROOP_MASK				0xF3
+#define FLASH_LED_HDRM_SNS_ENABLE_MASK				0x81
+#define	FLASH_MASK_MODULE_CONTRL_MASK				0xE0
+#define FLASH_FOLLOW_OTST2_RB_MASK				0x08
+
+#define FLASH_LED_TRIGGER_DEFAULT				"none"
+#define FLASH_LED_HEADROOM_DEFAULT_MV				500
+#define FLASH_LED_STARTUP_DELAY_DEFAULT_US			128
+#define FLASH_LED_CLAMP_CURRENT_DEFAULT_MA			200
+#define	FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C		80
+#define	FLASH_LED_RAMP_UP_STEP_DEFAULT_US			3
+#define	FLASH_LED_RAMP_DN_STEP_DEFAULT_US			3
+#define	FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV		3200
+#define	FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US	10
+#define FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT		2
+#define FLASH_RAMP_UP_DELAY_US_MIN				1000
+#define	FLASH_RAMP_UP_DELAY_US_MAX				1001
+#define FLASH_RAMP_DN_DELAY_US_MIN				2160
+#define	FLASH_RAMP_DN_DELAY_US_MAX				2161
+#define FLASH_BOOST_REGULATOR_PROBE_DELAY_MS			2000
+#define	FLASH_TORCH_MAX_LEVEL					0x0F
+#define	FLASH_MAX_LEVEL						0x4F
+#define	FLASH_LED_FLASH_HW_VREG_OK				0x40
+#define	FLASH_LED_FLASH_SW_VREG_OK				0x80
+#define FLASH_LED_STROBE_TYPE_HW				0x04
+#define	FLASH_DURATION_DIVIDER					10
+#define	FLASH_LED_HEADROOM_DIVIDER				100
+#define	FLASH_LED_HEADROOM_OFFSET				2
+#define	FLASH_LED_MAX_CURRENT_MA				1000
+#define	FLASH_LED_THERMAL_THRESHOLD_MIN				95
+#define	FLASH_LED_THERMAL_DEVIDER				10
+#define	FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV			2500
+#define	FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER			100
+#define	FLASH_LED_HDRM_SNS_ENABLE				0x81
+#define	FLASH_LED_HDRM_SNS_DISABLE				0x01
+#define	FLASH_LED_UA_PER_MA					1000
+#define	FLASH_LED_MASK_MODULE_MASK2_ENABLE			0x20
+#define	FLASH_LED_MASK3_ENABLE_SHIFT				7
+#define	FLASH_LED_MODULE_CTRL_DEFAULT				0x60
+#define	FLASH_LED_CURRENT_READING_DELAY_MIN			5000
+#define	FLASH_LED_CURRENT_READING_DELAY_MAX			5001
+#define	FLASH_LED_OPEN_FAULT_DETECTED				0xC
+
+#define FLASH_UNLOCK_SECURE					0xA5
+#define FLASH_LED_TORCH_ENABLE					0x00
+#define FLASH_LED_TORCH_DISABLE					0x03
+#define FLASH_MODULE_ENABLE					0x80
+#define FLASH_LED0_TRIGGER					0x80
+#define FLASH_LED1_TRIGGER					0x40
+#define FLASH_LED0_ENABLEMENT					0x40
+#define FLASH_LED1_ENABLEMENT					0x20
+#define FLASH_LED_DISABLE					0x00
+#define	FLASH_LED_MIN_CURRENT_MA				13
+#define FLASH_SUBTYPE_DUAL					0x01
+#define FLASH_SUBTYPE_SINGLE					0x02
+
+/*
+ * ID represents physical LEDs for individual control purpose.
+ */
+enum flash_led_id {
+	FLASH_LED_0 = 0,
+	FLASH_LED_1,
+	FLASH_LED_SWITCH,
+};
+
+enum flash_led_type {
+	FLASH = 0,
+	TORCH,
+	SWITCH,
+};
+
+enum thermal_derate_rate {
+	RATE_1_PERCENT = 0,
+	RATE_1P25_PERCENT,
+	RATE_2_PERCENT,
+	RATE_2P5_PERCENT,
+	RATE_5_PERCENT,
+};
+
+enum current_ramp_steps {
+	RAMP_STEP_0P2_US = 0,
+	RAMP_STEP_0P4_US,
+	RAMP_STEP_0P8_US,
+	RAMP_STEP_1P6_US,
+	RAMP_STEP_3P3_US,
+	RAMP_STEP_6P7_US,
+	RAMP_STEP_13P5_US,
+	RAMP_STEP_27US,
+};
+
+struct flash_regulator_data {
+	struct regulator	*regs;
+	const char		*reg_name;
+	u32			max_volt_uv;
+};
+
+/*
+ * Configurations for each individual LED
+ */
+struct flash_node_data {
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct led_classdev		cdev;
+	struct work_struct		work;
+	struct flash_regulator_data	*reg_data;
+	u16				max_current;
+	u16				prgm_current;
+	u16				prgm_current2;
+	u16				duration;
+	u8				id;
+	u8				type;
+	u8				trigger;
+	u8				enable;
+	u8				num_regulators;
+	bool				flash_on;
+};
+
+/*
+ * Flash LED configuration read from device tree
+ */
+struct flash_led_platform_data {
+	unsigned int			temp_threshold_num;
+	unsigned int			temp_derate_curr_num;
+	unsigned int			*die_temp_derate_curr_ma;
+	unsigned int			*die_temp_threshold_degc;
+	u16				ramp_up_step;
+	u16				ramp_dn_step;
+	u16				vph_pwr_droop_threshold;
+	u16				headroom;
+	u16				clamp_current;
+	u8				thermal_derate_threshold;
+	u8				vph_pwr_droop_debounce_time;
+	u8				startup_dly;
+	u8				thermal_derate_rate;
+	bool				pmic_charger_support;
+	bool				self_check_en;
+	bool				thermal_derate_en;
+	bool				current_ramp_en;
+	bool				vph_pwr_droop_en;
+	bool				hdrm_sns_ch0_en;
+	bool				hdrm_sns_ch1_en;
+	bool				power_detect_en;
+	bool				mask3_en;
+	bool				follow_rb_disable;
+	bool				die_current_derate_en;
+};
+
+struct qpnp_flash_led_buffer {
+	struct		mutex debugfs_lock; /* Prevent thread concurrency */
+	size_t		rpos;
+	size_t		wpos;
+	size_t		len;
+	struct		qpnp_flash_led *led;
+	u32		buffer_cnt;
+	char		data[0];
+};
+
+/*
+ * Flash LED data structure containing flash LED attributes
+ */
+struct qpnp_flash_led {
+	struct pmic_revid_data		*revid_data;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct flash_led_platform_data	*pdata;
+	struct pinctrl			*pinctrl;
+	struct pinctrl_state		*gpio_state_active;
+	struct pinctrl_state		*gpio_state_suspend;
+	struct flash_node_data		*flash_node;
+	struct power_supply		*battery_psy;
+	struct workqueue_struct		*ordered_workq;
+	struct qpnp_vadc_chip		*vadc_dev;
+	struct mutex			flash_led_lock;
+	struct dentry			*dbgfs_root;
+	int				num_leds;
+	u16				base;
+	u16				current_addr;
+	u16				current2_addr;
+	u8				peripheral_type;
+	u8				fault_reg;
+	bool				gpio_enabled;
+	bool				charging_enabled;
+	bool				strobe_debug;
+	bool				dbg_feature_en;
+	bool				open_fault;
+};
+
+static u8 qpnp_flash_led_ctrl_dbg_regs[] = {
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+	0x4A, 0x4B, 0x4C, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A, 0x5C, 0x5D,
+};
+
+static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
+					struct file *file)
+{
+	struct qpnp_flash_led_buffer *log;
+	size_t logbufsize = SZ_4K;
+
+	log = kzalloc(logbufsize, GFP_KERNEL);
+	if (!log)
+		return -ENOMEM;
+
+	log->rpos = 0;
+	log->wpos = 0;
+	log->len = logbufsize - sizeof(*log);
+	mutex_init(&log->debugfs_lock);
+	log->led = led;
+
+	log->buffer_cnt = 1;
+	file->private_data = log;
+
+	return 0;
+}
+
+static int flash_led_dfs_open(struct inode *inode, struct file *file)
+{
+	struct qpnp_flash_led *led = inode->i_private;
+
+	return flash_led_dbgfs_file_open(led, file);
+}
+
+static int flash_led_dfs_close(struct inode *inode, struct file *file)
+{
+	struct qpnp_flash_led_buffer *log = file->private_data;
+
+	if (log) {
+		file->private_data = NULL;
+		mutex_destroy(&log->debugfs_lock);
+		kfree(log);
+	}
+
+	return 0;
+}
+
+#define MIN_BUFFER_WRITE_LEN		20
+static int print_to_log(struct qpnp_flash_led_buffer *log,
+					const char *fmt, ...)
+{
+	va_list args;
+	int cnt;
+	char *log_buf;
+	size_t size = log->len - log->wpos;
+
+	if (size < MIN_BUFFER_WRITE_LEN)
+		return 0;	/* not enough buffer left */
+
+	log_buf = &log->data[log->wpos];
+	va_start(args, fmt);
+	cnt = vscnprintf(log_buf, size, fmt, args);
+	va_end(args);
+
+	log->wpos += cnt;
+	return cnt;
+}
+
+static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
+					size_t count, loff_t *ppos) {
+	struct qpnp_flash_led_buffer *log = fp->private_data;
+	struct qpnp_flash_led *led;
+	uint val;
+	int rc = 0;
+	size_t len;
+	size_t ret;
+
+	if (!log) {
+		pr_err("error: file private data is NULL\n");
+		return -EFAULT;
+	}
+	led = log->led;
+
+	mutex_lock(&log->debugfs_lock);
+	if ((log->rpos >= log->wpos && log->buffer_cnt == 0) ||
+			((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
+		goto unlock_mutex;
+
+	rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"Unable to read from address %x, rc(%d)\n",
+				INT_LATCHED_STS(led->base), rc);
+		goto unlock_mutex;
+	}
+	log->buffer_cnt--;
+
+	rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
+	if (rc == 0)
+		goto unlock_mutex;
+
+	rc = print_to_log(log, "0x%02X ", val);
+	if (rc == 0)
+		goto unlock_mutex;
+
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+	len = min(count, log->wpos - log->rpos);
+
+	ret = copy_to_user(buf, &log->data[log->rpos], len);
+	if (ret) {
+		pr_err("error copy register value to user\n");
+		rc = -EFAULT;
+		goto unlock_mutex;
+	}
+
+	len -= ret;
+	*ppos += len;
+	log->rpos += len;
+
+	rc = len;
+
+unlock_mutex:
+	mutex_unlock(&log->debugfs_lock);
+	return rc;
+}
+
+static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
+					size_t count, loff_t *ppos) {
+	struct qpnp_flash_led_buffer *log = fp->private_data;
+	struct qpnp_flash_led *led;
+	int rc = 0;
+	size_t len;
+	size_t ret;
+
+	if (!log) {
+		pr_err("error: file private data is NULL\n");
+		return -EFAULT;
+	}
+	led = log->led;
+
+	mutex_lock(&log->debugfs_lock);
+	if ((log->rpos >= log->wpos && log->buffer_cnt == 0) ||
+			((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
+		goto unlock_mutex;
+
+	log->buffer_cnt--;
+
+	rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
+	if (rc == 0)
+		goto unlock_mutex;
+
+	rc = print_to_log(log, "0x%02X ", led->fault_reg);
+	if (rc == 0)
+		goto unlock_mutex;
+
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+	len = min(count, log->wpos - log->rpos);
+
+	ret = copy_to_user(buf, &log->data[log->rpos], len);
+	if (ret) {
+		pr_err("error copy register value to user\n");
+		rc = -EFAULT;
+		goto unlock_mutex;
+	}
+
+	len -= ret;
+	*ppos += len;
+	log->rpos += len;
+
+	rc = len;
+
+unlock_mutex:
+	mutex_unlock(&log->debugfs_lock);
+	return rc;
+}
+
+static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos) {
+
+	u8 *val;
+	int pos = 0;
+	int cnt = 0;
+	int data;
+	size_t ret = 0;
+
+	struct qpnp_flash_led_buffer *log = file->private_data;
+	struct qpnp_flash_led *led;
+	char *kbuf;
+
+	if (!log) {
+		pr_err("error: file private data is NULL\n");
+		return -EFAULT;
+	}
+	led = log->led;
+
+	mutex_lock(&log->debugfs_lock);
+	kbuf = kmalloc(count + 1, GFP_KERNEL);
+	if (!kbuf) {
+		ret = -ENOMEM;
+		goto unlock_mutex;
+	}
+
+	ret = copy_from_user(kbuf, buf, count);
+	if (!ret) {
+		pr_err("failed to copy data from user\n");
+		ret = -EFAULT;
+		goto free_buf;
+	}
+
+	count -= ret;
+	*ppos += count;
+	kbuf[count] = '\0';
+	val = kbuf;
+	while (sscanf(kbuf + pos, "%i", &data) == 1) {
+		pos++;
+		val[cnt++] = data & 0xff;
+	}
+
+	if (!cnt)
+		goto free_buf;
+
+	ret = count;
+	if (*val == 1)
+		led->strobe_debug = true;
+	else
+		led->strobe_debug = false;
+
+free_buf:
+	kfree(kbuf);
+unlock_mutex:
+	mutex_unlock(&log->debugfs_lock);
+	return ret;
+}
+
+static ssize_t flash_led_dfs_dbg_enable(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos) {
+
+	u8 *val;
+	int pos = 0;
+	int cnt = 0;
+	int data;
+	size_t ret = 0;
+	struct qpnp_flash_led_buffer *log = file->private_data;
+	struct qpnp_flash_led *led;
+	char *kbuf;
+
+	if (!log) {
+		pr_err("error: file private data is NULL\n");
+		return -EFAULT;
+	}
+	led = log->led;
+
+	mutex_lock(&log->debugfs_lock);
+	kbuf = kmalloc(count + 1, GFP_KERNEL);
+	if (!kbuf) {
+		ret = -ENOMEM;
+		goto unlock_mutex;
+	}
+
+	ret = copy_from_user(kbuf, buf, count);
+	if (ret == count) {
+		pr_err("failed to copy data from user\n");
+		ret = -EFAULT;
+		goto free_buf;
+	}
+	count -= ret;
+	*ppos += count;
+	kbuf[count] = '\0';
+	val = kbuf;
+	while (sscanf(kbuf + pos, "%i", &data) == 1) {
+		pos++;
+		val[cnt++] = data & 0xff;
+	}
+
+	if (!cnt)
+		goto free_buf;
+
+	ret = count;
+	if (*val == 1)
+		led->dbg_feature_en = true;
+	else
+		led->dbg_feature_en = false;
+
+free_buf:
+	kfree(kbuf);
+unlock_mutex:
+	mutex_unlock(&log->debugfs_lock);
+	return ret;
+}
+
+static const struct file_operations flash_led_dfs_latched_reg_fops = {
+	.open		= flash_led_dfs_open,
+	.release	= flash_led_dfs_close,
+	.read		= flash_led_dfs_latched_reg_read,
+};
+
+static const struct file_operations flash_led_dfs_strobe_reg_fops = {
+	.open		= flash_led_dfs_open,
+	.release	= flash_led_dfs_close,
+	.read		= flash_led_dfs_fault_reg_read,
+	.write		= flash_led_dfs_fault_reg_enable,
+};
+
+static const struct file_operations flash_led_dfs_dbg_feature_fops = {
+	.open		= flash_led_dfs_open,
+	.release	= flash_led_dfs_close,
+	.write		= flash_led_dfs_dbg_enable,
+};
+
+static int
+qpnp_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(led->regmap, addr, mask, val);
+	if (rc)
+		dev_err(&led->pdev->dev,
+			"Unable to update_bits to addr=%x, rc(%d)\n", addr, rc);
+
+	dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
+
+	return rc;
+}
+
+static int qpnp_flash_led_get_allowed_die_temp_curr(struct qpnp_flash_led *led,
+							int64_t die_temp_degc)
+{
+	int die_temp_curr_ma;
+
+	if (die_temp_degc >= led->pdata->die_temp_threshold_degc[0])
+		die_temp_curr_ma =  0;
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[1])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[0];
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[2])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[1];
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[3])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[2];
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[4])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[3];
+	else
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[4];
+
+	return die_temp_curr_ma;
+}
+
+static int64_t qpnp_flash_led_get_die_temp(struct qpnp_flash_led *led)
+{
+	struct qpnp_vadc_result die_temp_result;
+	int rc;
+
+	rc = qpnp_vadc_read(led->vadc_dev, SPARE2, &die_temp_result);
+	if (rc) {
+		pr_err("failed to read the die temp\n");
+		return -EINVAL;
+	}
+
+	return die_temp_result.physical;
+}
+
+static int qpnp_get_pmic_revid(struct qpnp_flash_led *led)
+{
+	struct device_node *revid_dev_node;
+
+	revid_dev_node = of_parse_phandle(led->pdev->dev.of_node,
+				"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		dev_err(&led->pdev->dev,
+			"qcom,pmic-revid property missing\n");
+		return -EINVAL;
+	}
+
+	led->revid_data = get_revid_data(revid_dev_node);
+	if (IS_ERR(led->revid_data)) {
+		pr_err("Couldn't get revid data rc = %ld\n",
+				PTR_ERR(led->revid_data));
+		return PTR_ERR(led->revid_data);
+	}
+
+	return 0;
+}
+
+static int
+qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node,
+					struct qpnp_flash_led *led)
+{
+	union power_supply_propval prop;
+	int64_t chg_temp_milidegc, die_temp_degc;
+	int max_curr_avail_ma = 2000;
+	int allowed_die_temp_curr_ma = 2000;
+	int rc;
+
+	if (led->pdata->power_detect_en) {
+		if (!led->battery_psy) {
+			dev_err(&led->pdev->dev,
+				"Failed to query power supply\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * When charging is enabled, enforce this new enablement
+		 * sequence to reduce fuel gauge reading resolution.
+		 */
+		if (led->charging_enabled) {
+			rc = qpnp_led_masked_write(led,
+				FLASH_MODULE_ENABLE_CTRL(led->base),
+				FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Module enable reg write failed\n");
+				return -EINVAL;
+			}
+
+			usleep_range(FLASH_LED_CURRENT_READING_DELAY_MIN,
+				FLASH_LED_CURRENT_READING_DELAY_MAX);
+		}
+
+		power_supply_get_property(led->battery_psy,
+				POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, &prop);
+		if (!prop.intval) {
+			dev_err(&led->pdev->dev,
+				"battery too low for flash\n");
+			return -EINVAL;
+		}
+
+		max_curr_avail_ma = (prop.intval / FLASH_LED_UA_PER_MA);
+	}
+
+	/*
+	 * When thermal mitigation is available, this logic will execute to
+	 * derate current based upon the PMIC die temperature.
+	 */
+	if (led->pdata->die_current_derate_en) {
+		chg_temp_milidegc = qpnp_flash_led_get_die_temp(led);
+		if (chg_temp_milidegc < 0)
+			return -EINVAL;
+
+		die_temp_degc = div_s64(chg_temp_milidegc, 1000);
+		allowed_die_temp_curr_ma =
+			qpnp_flash_led_get_allowed_die_temp_curr(led,
+								die_temp_degc);
+		if (allowed_die_temp_curr_ma < 0)
+			return -EINVAL;
+	}
+
+	max_curr_avail_ma = (max_curr_avail_ma >= allowed_die_temp_curr_ma)
+				? allowed_die_temp_curr_ma : max_curr_avail_ma;
+
+	return max_curr_avail_ma;
+}
+
+static ssize_t qpnp_flash_led_die_temp_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	unsigned long val;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	/*'0' for disable die_temp feature; non-zero to enable feature*/
+	if (val == 0)
+		led->pdata->die_current_derate_en = false;
+	else
+		led->pdata->die_current_derate_en = true;
+
+	return count;
+}
+
+static ssize_t qpnp_led_strobe_type_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct flash_node_data *flash_node;
+	unsigned long state;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = -EINVAL;
+
+	ret = kstrtoul(buf, 10, &state);
+	if (ret)
+		return ret;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+
+	/* '0' for sw strobe; '1' for hw strobe */
+	if (state == 1)
+		flash_node->trigger |= FLASH_LED_STROBE_TYPE_HW;
+	else
+		flash_node->trigger &= ~FLASH_LED_STROBE_TYPE_HW;
+
+	return count;
+}
+
+static ssize_t qpnp_flash_led_dump_regs_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	int rc, i, count = 0;
+	u16 addr;
+	uint val;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+	for (i = 0; i < ARRAY_SIZE(qpnp_flash_led_ctrl_dbg_regs); i++) {
+		addr = led->base + qpnp_flash_led_ctrl_dbg_regs[i];
+		rc = regmap_read(led->regmap, addr, &val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Unable to read from addr=%x, rc(%d)\n",
+				addr, rc);
+			return -EINVAL;
+		}
+
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				"REG_0x%x = 0x%02x\n", addr, val);
+
+		if (count >= PAGE_SIZE)
+			return PAGE_SIZE - 1;
+	}
+
+	return count;
+}
+
+static ssize_t qpnp_flash_led_current_derate_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	unsigned long val;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	/*'0' for disable derate feature; non-zero to enable derate feature */
+	if (val == 0)
+		led->pdata->power_detect_en = false;
+	else
+		led->pdata->power_detect_en = true;
+
+	return count;
+}
+
+static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	int max_curr_avail_ma = 0;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	if (led->flash_node[0].flash_on)
+		max_curr_avail_ma += led->flash_node[0].max_current;
+	if (led->flash_node[1].flash_on)
+		max_curr_avail_ma += led->flash_node[1].max_current;
+
+	if (led->pdata->power_detect_en ||
+			led->pdata->die_current_derate_en) {
+		max_curr_avail_ma =
+			qpnp_flash_led_get_max_avail_current(flash_node, led);
+
+		if (max_curr_avail_ma < 0)
+			return -EINVAL;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", max_curr_avail_ma);
+}
+
+static struct device_attribute qpnp_flash_led_attrs[] = {
+	__ATTR(strobe, 0664, NULL, qpnp_led_strobe_type_store),
+	__ATTR(reg_dump, 0664, qpnp_flash_led_dump_regs_show, NULL),
+	__ATTR(enable_current_derate, 0664, NULL,
+		qpnp_flash_led_current_derate_store),
+	__ATTR(max_allowed_current, 0664, qpnp_flash_led_max_current_show,
+		NULL),
+	__ATTR(enable_die_temp_current_derate, 0664, NULL,
+		qpnp_flash_led_die_temp_store),
+};
+
+static int qpnp_flash_led_get_thermal_derate_rate(const char *rate)
+{
+	/*
+	 * return 5% derate as default value if user specifies
+	 * a value un-supported
+	 */
+	if (strcmp(rate, "1_PERCENT") == 0)
+		return RATE_1_PERCENT;
+	else if (strcmp(rate, "1P25_PERCENT") == 0)
+		return RATE_1P25_PERCENT;
+	else if (strcmp(rate, "2_PERCENT") == 0)
+		return RATE_2_PERCENT;
+	else if (strcmp(rate, "2P5_PERCENT") == 0)
+		return RATE_2P5_PERCENT;
+	else if (strcmp(rate, "5_PERCENT") == 0)
+		return RATE_5_PERCENT;
+	else
+		return RATE_5_PERCENT;
+}
+
+static int qpnp_flash_led_get_ramp_step(const char *step)
+{
+	/*
+	 * return 27 us as default value if user specifies
+	 * a value un-supported
+	 */
+	if (strcmp(step, "0P2_US") == 0)
+		return RAMP_STEP_0P2_US;
+	else if (strcmp(step, "0P4_US") == 0)
+		return RAMP_STEP_0P4_US;
+	else if (strcmp(step, "0P8_US") == 0)
+		return RAMP_STEP_0P8_US;
+	else if (strcmp(step, "1P6_US") == 0)
+		return RAMP_STEP_1P6_US;
+	else if (strcmp(step, "3P3_US") == 0)
+		return RAMP_STEP_3P3_US;
+	else if (strcmp(step, "6P7_US") == 0)
+		return RAMP_STEP_6P7_US;
+	else if (strcmp(step, "13P5_US") == 0)
+		return RAMP_STEP_13P5_US;
+	else
+		return RAMP_STEP_27US;
+}
+
+static u8 qpnp_flash_led_get_droop_debounce_time(u8 val)
+{
+	/*
+	 * return 10 us as default value if user specifies
+	 * a value un-supported
+	 */
+	switch (val) {
+	case 0:
+		return 0;
+	case 10:
+		return 1;
+	case 32:
+		return 2;
+	case 64:
+		return 3;
+	default:
+		return 1;
+	}
+}
+
+static u8 qpnp_flash_led_get_startup_dly(u8 val)
+{
+	/*
+	 * return 128 us as default value if user specifies
+	 * a value un-supported
+	 */
+	switch (val) {
+	case 10:
+		return 0;
+	case 32:
+		return 1;
+	case 64:
+		return 2;
+	case 128:
+		return 3;
+	default:
+		return 3;
+	}
+}
+
+static int
+qpnp_flash_led_get_peripheral_type(struct qpnp_flash_led *led)
+{
+	int rc;
+	uint val;
+
+	rc = regmap_read(led->regmap,
+			 FLASH_LED_PERIPHERAL_SUBTYPE(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"Unable to read peripheral subtype\n");
+		return -EINVAL;
+	}
+
+	return val;
+}
+
+static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led,
+				struct flash_node_data *flash_node)
+{
+	union power_supply_propval psy_prop;
+	int rc;
+	uint val, tmp;
+
+	rc = regmap_read(led->regmap, FLASH_LED_STROBE_CTRL(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Unable to read strobe reg\n");
+		return -EINVAL;
+	}
+
+	tmp = (~flash_node->trigger) & val;
+	if (!tmp) {
+		if (flash_node->type == TORCH) {
+			rc = qpnp_led_masked_write(led,
+				FLASH_LED_UNLOCK_SECURE(led->base),
+				FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Secure reg write failed\n");
+				return -EINVAL;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_TORCH(led->base),
+				FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Torch reg write failed\n");
+				return -EINVAL;
+			}
+		}
+
+		if (led->battery_psy &&
+			led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
+						!led->revid_data->rev3) {
+			psy_prop.intval = false;
+			rc = power_supply_set_property(led->battery_psy,
+					POWER_SUPPLY_PROP_FLASH_TRIGGER,
+							&psy_prop);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Failed to enble charger i/p current limit\n");
+				return -EINVAL;
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+				FLASH_MODULE_ENABLE_CTRL(led->base),
+				FLASH_MODULE_ENABLE_MASK,
+				FLASH_LED_MODULE_CTRL_DEFAULT);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Module disable failed\n");
+			return -EINVAL;
+		}
+
+		if (led->pinctrl) {
+			rc = pinctrl_select_state(led->pinctrl,
+					led->gpio_state_suspend);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"failed to disable GPIO\n");
+				return -EINVAL;
+			}
+			led->gpio_enabled = false;
+		}
+
+		if (led->battery_psy) {
+			psy_prop.intval = false;
+			rc = power_supply_set_property(led->battery_psy,
+						POWER_SUPPLY_PROP_FLASH_ACTIVE,
+							&psy_prop);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Failed to setup OTG pulse skip enable\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (flash_node->trigger & FLASH_LED0_TRIGGER) {
+		rc = qpnp_led_masked_write(led,
+				led->current_addr,
+				FLASH_CURRENT_MASK, 0x00);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"current register write failed\n");
+			return -EINVAL;
+		}
+	}
+
+	if (flash_node->trigger & FLASH_LED1_TRIGGER) {
+		rc = qpnp_led_masked_write(led,
+				led->current2_addr,
+				FLASH_CURRENT_MASK, 0x00);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"current register write failed\n");
+			return -EINVAL;
+		}
+	}
+
+	if (flash_node->id == FLASH_LED_SWITCH)
+		flash_node->trigger &= FLASH_LED_STROBE_TYPE_HW;
+
+	return 0;
+}
+
+static enum
+led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev)
+{
+	return led_cdev->brightness;
+}
+
+static int flash_regulator_parse_dt(struct qpnp_flash_led *led,
+					struct flash_node_data *flash_node) {
+
+	int i = 0, rc;
+	struct device_node *node = flash_node->cdev.dev->of_node;
+	struct device_node *temp = NULL;
+	const char *temp_string;
+	u32 val;
+
+	flash_node->reg_data = devm_kzalloc(&led->pdev->dev,
+					sizeof(struct flash_regulator_data *) *
+						flash_node->num_regulators,
+						GFP_KERNEL);
+	if (!flash_node->reg_data) {
+		dev_err(&led->pdev->dev,
+				"Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for_each_child_of_node(node, temp) {
+		rc = of_property_read_string(temp, "regulator-name",
+							&temp_string);
+		if (!rc)
+			flash_node->reg_data[i].reg_name = temp_string;
+		else {
+			dev_err(&led->pdev->dev,
+					"Unable to read regulator name\n");
+			return rc;
+		}
+
+		rc = of_property_read_u32(temp, "max-voltage", &val);
+		if (!rc) {
+			flash_node->reg_data[i].max_volt_uv = val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+					"Unable to read max voltage\n");
+			return rc;
+		}
+
+		i++;
+	}
+
+	return 0;
+}
+
+static int flash_regulator_setup(struct qpnp_flash_led *led,
+				struct flash_node_data *flash_node, bool on)
+{
+	int i, rc = 0;
+
+	if (on == false) {
+		i = flash_node->num_regulators;
+		goto error_regulator_setup;
+	}
+
+	for (i = 0; i < flash_node->num_regulators; i++) {
+		flash_node->reg_data[i].regs =
+			regulator_get(flash_node->cdev.dev,
+					flash_node->reg_data[i].reg_name);
+		if (IS_ERR(flash_node->reg_data[i].regs)) {
+			rc = PTR_ERR(flash_node->reg_data[i].regs);
+			dev_err(&led->pdev->dev,
+					"Failed to get regulator\n");
+			goto error_regulator_setup;
+		}
+
+		if (regulator_count_voltages(flash_node->reg_data[i].regs)
+									> 0) {
+			rc = regulator_set_voltage(flash_node->reg_data[i].regs,
+					flash_node->reg_data[i].max_volt_uv,
+					flash_node->reg_data[i].max_volt_uv);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"regulator set voltage failed\n");
+				regulator_put(flash_node->reg_data[i].regs);
+				goto error_regulator_setup;
+			}
+		}
+	}
+
+	return rc;
+
+error_regulator_setup:
+	while (i--) {
+		if (regulator_count_voltages(flash_node->reg_data[i].regs)
+									> 0) {
+			regulator_set_voltage(flash_node->reg_data[i].regs,
+				0, flash_node->reg_data[i].max_volt_uv);
+		}
+
+		regulator_put(flash_node->reg_data[i].regs);
+	}
+
+	return rc;
+}
+
+static int flash_regulator_enable(struct qpnp_flash_led *led,
+				struct flash_node_data *flash_node, bool on)
+{
+	int i, rc = 0;
+
+	if (on == false) {
+		i = flash_node->num_regulators;
+		goto error_regulator_enable;
+	}
+
+	for (i = 0; i < flash_node->num_regulators; i++) {
+		rc = regulator_enable(flash_node->reg_data[i].regs);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"regulator enable failed\n");
+			goto error_regulator_enable;
+		}
+	}
+
+	return rc;
+
+error_regulator_enable:
+	while (i--)
+		regulator_disable(flash_node->reg_data[i].regs);
+
+	return rc;
+}
+
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+					int *max_current)
+{
+	struct led_classdev *led_cdev = trigger_to_lcdev(trig);
+	struct flash_node_data *flash_node;
+	struct qpnp_flash_led *led;
+	int rc;
+
+	if (!led_cdev) {
+		pr_err("Invalid led_trigger provided\n");
+		return -EINVAL;
+	}
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
+		dev_err(&led->pdev->dev, "Invalid options %d\n", options);
+		return -EINVAL;
+	}
+
+	if (options & ENABLE_REGULATOR) {
+		rc = flash_regulator_enable(led, flash_node, true);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"enable regulator failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (options & DISABLE_REGULATOR) {
+		rc = flash_regulator_enable(led, flash_node, false);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"disable regulator failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (options & QUERY_MAX_CURRENT) {
+		rc = qpnp_flash_led_get_max_avail_current(flash_node, led);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"query max current failed, rc=%d\n", rc);
+			return rc;
+		}
+		*max_current = rc;
+	}
+
+	return 0;
+}
+
+static void qpnp_flash_led_work(struct work_struct *work)
+{
+	struct flash_node_data *flash_node = container_of(work,
+				struct flash_node_data, work);
+	struct qpnp_flash_led *led = dev_get_drvdata(&flash_node->pdev->dev);
+	union power_supply_propval psy_prop;
+	int rc, brightness = flash_node->cdev.brightness;
+	int max_curr_avail_ma = 0;
+	int total_curr_ma = 0;
+	int i;
+	u8 val = 0;
+	uint temp;
+
+	mutex_lock(&led->flash_led_lock);
+
+	if (!brightness)
+		goto turn_off;
+
+	if (led->open_fault) {
+		dev_err(&led->pdev->dev, "Open fault detected\n");
+		mutex_unlock(&led->flash_led_lock);
+		return;
+	}
+
+	if (!flash_node->flash_on && flash_node->num_regulators > 0) {
+		rc = flash_regulator_enable(led, flash_node, true);
+		if (rc) {
+			mutex_unlock(&led->flash_led_lock);
+			return;
+		}
+	}
+
+	if (!led->gpio_enabled && led->pinctrl) {
+		rc = pinctrl_select_state(led->pinctrl,
+						led->gpio_state_active);
+		if (rc) {
+			dev_err(&led->pdev->dev, "failed to enable GPIO\n");
+			goto error_enable_gpio;
+		}
+		led->gpio_enabled = true;
+	}
+
+	if (led->dbg_feature_en) {
+		rc = qpnp_led_masked_write(led,
+						INT_SET_TYPE(led->base),
+						FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"INT_SET_TYPE write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					IN_POLARITY_HIGH(led->base),
+					FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"IN_POLARITY_HIGH write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					INT_EN_SET(led->base),
+					FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev, "INT_EN_SET write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					INT_LATCHED_CLR(led->base),
+					FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"INT_LATCHED_CLR write failed\n");
+			goto exit_flash_led_work;
+		}
+	}
+
+	if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
+					flash_node->id != FLASH_LED_SWITCH) {
+		led->flash_node[led->num_leds - 1].trigger |=
+						(0x80 >> flash_node->id);
+		if (flash_node->id == FLASH_LED_0)
+			led->flash_node[led->num_leds - 1].prgm_current =
+						flash_node->prgm_current;
+		else if (flash_node->id == FLASH_LED_1)
+			led->flash_node[led->num_leds - 1].prgm_current2 =
+						flash_node->prgm_current;
+	}
+
+	if (flash_node->type == TORCH) {
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_UNLOCK_SECURE(led->base),
+			FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Secure reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_TORCH(led->base),
+			FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Torch reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (flash_node->id == FLASH_LED_SWITCH) {
+			val = (u8)(flash_node->prgm_current *
+						FLASH_TORCH_MAX_LEVEL
+						/ flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+						led->current_addr,
+						FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Torch reg write failed\n");
+				goto exit_flash_led_work;
+			}
+
+			val = (u8)(flash_node->prgm_current2 *
+						FLASH_TORCH_MAX_LEVEL
+						/ flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+					led->current2_addr,
+					FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Torch reg write failed\n");
+				goto exit_flash_led_work;
+			}
+		} else {
+			val = (u8)(flash_node->prgm_current *
+						FLASH_TORCH_MAX_LEVEL /
+						flash_node->max_current);
+			if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(led,
+						led->current_addr,
+						FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			} else {
+				rc = qpnp_led_masked_write(led,
+						led->current2_addr,
+						FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_MAX_CURRENT(led->base),
+			FLASH_CURRENT_MASK, FLASH_TORCH_MAX_LEVEL);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Max current reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_MODULE_ENABLE_CTRL(led->base),
+			FLASH_MODULE_ENABLE_MASK, FLASH_MODULE_ENABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Module enable reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (led->pdata->hdrm_sns_ch0_en ||
+						led->pdata->hdrm_sns_ch1_en) {
+			if (flash_node->id == FLASH_LED_SWITCH) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					flash_node->trigger &
+					FLASH_LED0_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					flash_node->trigger &
+					FLASH_LED1_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_1) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			(flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
+						| FLASH_LED_STROBE_TYPE_HW
+							: flash_node->trigger |
+						FLASH_LED_STROBE_TYPE_HW),
+							flash_node->trigger);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Strobe reg write failed\n");
+			goto exit_flash_led_work;
+		}
+	} else if (flash_node->type == FLASH) {
+		if (flash_node->trigger & FLASH_LED0_TRIGGER)
+			max_curr_avail_ma += flash_node->max_current;
+		if (flash_node->trigger & FLASH_LED1_TRIGGER)
+			max_curr_avail_ma += flash_node->max_current;
+
+		psy_prop.intval = true;
+		rc = power_supply_set_property(led->battery_psy,
+						POWER_SUPPLY_PROP_FLASH_ACTIVE,
+								&psy_prop);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to setup OTG pulse skip enable\n");
+			goto exit_flash_led_work;
+		}
+
+		if (led->pdata->power_detect_en ||
+					led->pdata->die_current_derate_en) {
+			if (led->battery_psy) {
+				power_supply_get_property(led->battery_psy,
+					POWER_SUPPLY_PROP_STATUS,
+					&psy_prop);
+				if (psy_prop.intval < 0) {
+					dev_err(&led->pdev->dev,
+						"Invalid battery status\n");
+					goto exit_flash_led_work;
+				}
+
+				if (psy_prop.intval ==
+						POWER_SUPPLY_STATUS_CHARGING)
+					led->charging_enabled = true;
+				else if (psy_prop.intval ==
+					POWER_SUPPLY_STATUS_DISCHARGING
+					|| psy_prop.intval ==
+					POWER_SUPPLY_STATUS_NOT_CHARGING)
+					led->charging_enabled = false;
+			}
+			max_curr_avail_ma =
+				qpnp_flash_led_get_max_avail_current
+							(flash_node, led);
+			if (max_curr_avail_ma < 0) {
+				dev_err(&led->pdev->dev,
+					"Failed to get max avail curr\n");
+				goto exit_flash_led_work;
+			}
+		}
+
+		if (flash_node->id == FLASH_LED_SWITCH) {
+			if (flash_node->trigger & FLASH_LED0_TRIGGER)
+				total_curr_ma += flash_node->prgm_current;
+			if (flash_node->trigger & FLASH_LED1_TRIGGER)
+				total_curr_ma += flash_node->prgm_current2;
+
+			if (max_curr_avail_ma < total_curr_ma) {
+				flash_node->prgm_current =
+					(flash_node->prgm_current *
+					max_curr_avail_ma) / total_curr_ma;
+				flash_node->prgm_current2 =
+					(flash_node->prgm_current2 *
+					max_curr_avail_ma) / total_curr_ma;
+			}
+
+			val = (u8)(flash_node->prgm_current *
+				FLASH_MAX_LEVEL / flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+				led->current_addr, FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Current register write failed\n");
+				goto exit_flash_led_work;
+			}
+
+			val = (u8)(flash_node->prgm_current2 *
+				FLASH_MAX_LEVEL / flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+				led->current2_addr, FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Current register write failed\n");
+				goto exit_flash_led_work;
+			}
+		} else {
+			if (max_curr_avail_ma < flash_node->prgm_current) {
+				dev_err(&led->pdev->dev,
+					"battery only supprots %d mA\n",
+					max_curr_avail_ma);
+				flash_node->prgm_current =
+					 (u16)max_curr_avail_ma;
+			}
+
+			val = (u8)(flash_node->prgm_current *
+					 FLASH_MAX_LEVEL
+					/ flash_node->max_current);
+			if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(
+					led,
+					led->current_addr,
+					FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_1) {
+				rc = qpnp_led_masked_write(
+					led,
+					led->current2_addr,
+					FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		val = (u8)((flash_node->duration - FLASH_DURATION_DIVIDER)
+						/ FLASH_DURATION_DIVIDER);
+		rc = qpnp_led_masked_write(led,
+			FLASH_SAFETY_TIMER(led->base),
+			FLASH_SAFETY_TIMER_MASK, val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Safety timer reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_MAX_CURRENT(led->base),
+			FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Max current reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (!led->charging_enabled) {
+			rc = qpnp_led_masked_write(led,
+				FLASH_MODULE_ENABLE_CTRL(led->base),
+				FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Module enable reg write failed\n");
+				goto exit_flash_led_work;
+			}
+
+			usleep_range(FLASH_RAMP_UP_DELAY_US_MIN,
+						FLASH_RAMP_UP_DELAY_US_MAX);
+		}
+
+		if (led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
+						!led->revid_data->rev3) {
+			rc = power_supply_set_property(led->battery_psy,
+						POWER_SUPPLY_PROP_FLASH_TRIGGER,
+							&psy_prop);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Failed to disable charger i/p curr limit\n");
+				goto exit_flash_led_work;
+			}
+		}
+
+		if (led->pdata->hdrm_sns_ch0_en ||
+					led->pdata->hdrm_sns_ch1_en) {
+			if (flash_node->id == FLASH_LED_SWITCH) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					(flash_node->trigger &
+					FLASH_LED0_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE));
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					(flash_node->trigger &
+					FLASH_LED1_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE));
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_1) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			(flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
+						| FLASH_LED_STROBE_TYPE_HW
+							: flash_node->trigger |
+						FLASH_LED_STROBE_TYPE_HW),
+							flash_node->trigger);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Strobe reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (led->strobe_debug && led->dbg_feature_en) {
+			udelay(2000);
+			rc = regmap_read(led->regmap,
+					 FLASH_LED_FAULT_STATUS(led->base),
+					 &temp);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Unable to read from addr= %x, rc(%d)\n",
+				FLASH_LED_FAULT_STATUS(led->base), rc);
+				goto exit_flash_led_work;
+			}
+			led->fault_reg = temp;
+		}
+	} else {
+		pr_err("Both Torch and Flash cannot be select at same time\n");
+		for (i = 0; i < led->num_leds; i++)
+			led->flash_node[i].flash_on = false;
+		goto turn_off;
+	}
+
+	flash_node->flash_on = true;
+	mutex_unlock(&led->flash_led_lock);
+
+	return;
+
+turn_off:
+	if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
+					flash_node->id != FLASH_LED_SWITCH)
+		led->flash_node[led->num_leds - 1].trigger &=
+						~(0x80 >> flash_node->id);
+	if (flash_node->type == TORCH) {
+		/*
+		 * Checking LED fault status detects hardware open fault.
+		 * If fault occurs, all subsequent LED enablement requests
+		 * will be rejected to protect hardware.
+		 */
+		rc = regmap_read(led->regmap,
+			FLASH_LED_FAULT_STATUS(led->base), &temp);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to read out fault status register\n");
+			goto exit_flash_led_work;
+		}
+
+		led->open_fault |= (val & FLASH_LED_OPEN_FAULT_DETECTED);
+	}
+
+	rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			(flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
+						| FLASH_LED_STROBE_TYPE_HW
+						: flash_node->trigger
+						| FLASH_LED_STROBE_TYPE_HW),
+						FLASH_LED_DISABLE);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Strobe disable failed\n");
+		goto exit_flash_led_work;
+	}
+
+	usleep_range(FLASH_RAMP_DN_DELAY_US_MIN, FLASH_RAMP_DN_DELAY_US_MAX);
+exit_flash_hdrm_sns:
+	if (led->pdata->hdrm_sns_ch0_en) {
+		if (flash_node->id == FLASH_LED_0 ||
+				flash_node->id == FLASH_LED_SWITCH) {
+			rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_DISABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+				goto exit_flash_hdrm_sns;
+			}
+		}
+	}
+
+	if (led->pdata->hdrm_sns_ch1_en) {
+		if (flash_node->id == FLASH_LED_1 ||
+				flash_node->id == FLASH_LED_SWITCH) {
+			rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_DISABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+				goto exit_flash_hdrm_sns;
+			}
+		}
+	}
+exit_flash_led_work:
+	rc = qpnp_flash_led_module_disable(led, flash_node);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Module disable failed\n");
+		goto exit_flash_led_work;
+	}
+error_enable_gpio:
+	if (flash_node->flash_on && flash_node->num_regulators > 0)
+		flash_regulator_enable(led, flash_node, false);
+
+	flash_node->flash_on = false;
+	mutex_unlock(&led->flash_led_lock);
+}
+
+static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	struct flash_node_data *flash_node;
+	struct qpnp_flash_led *led;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	if (value < LED_OFF) {
+		pr_err("Invalid brightness value\n");
+		return;
+	}
+
+	if (value > flash_node->cdev.max_brightness)
+		value = flash_node->cdev.max_brightness;
+
+	flash_node->cdev.brightness = value;
+	if (led->flash_node[led->num_leds - 1].id ==
+						FLASH_LED_SWITCH) {
+		if (flash_node->type == TORCH)
+			led->flash_node[led->num_leds - 1].type = TORCH;
+		else if (flash_node->type == FLASH)
+			led->flash_node[led->num_leds - 1].type = FLASH;
+
+		led->flash_node[led->num_leds - 1].max_current
+						= flash_node->max_current;
+
+		if (flash_node->id == FLASH_LED_0 ||
+					 flash_node->id == FLASH_LED_1) {
+			if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
+				value = FLASH_LED_MIN_CURRENT_MA;
+
+			flash_node->prgm_current = value;
+			flash_node->flash_on = value ? true : false;
+		} else if (flash_node->id == FLASH_LED_SWITCH) {
+			if (!value) {
+				flash_node->prgm_current = 0;
+				flash_node->prgm_current2 = 0;
+			}
+		}
+	} else {
+		if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
+			value = FLASH_LED_MIN_CURRENT_MA;
+		flash_node->prgm_current = value;
+	}
+
+	queue_work(led->ordered_workq, &flash_node->work);
+}
+
+static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
+{
+	int rc;
+	u8 val, temp_val;
+	uint val_int;
+
+	rc = qpnp_led_masked_write(led,
+			FLASH_MODULE_ENABLE_CTRL(led->base),
+			FLASH_MODULE_ENABLE_MASK,
+			FLASH_LED_MODULE_CTRL_DEFAULT);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Module disable failed\n");
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			FLASH_STROBE_MASK, FLASH_LED_DISABLE);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Strobe disable failed\n");
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led,
+					FLASH_LED_TMR_CTRL(led->base),
+					FLASH_TMR_MASK, FLASH_TMR_SAFETY);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"LED timer ctrl reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	val = (u8)(led->pdata->headroom / FLASH_LED_HEADROOM_DIVIDER -
+						FLASH_LED_HEADROOM_OFFSET);
+	rc = qpnp_led_masked_write(led,
+						FLASH_HEADROOM(led->base),
+						FLASH_HEADROOM_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Headroom reg write failed\n");
+		return rc;
+	}
+
+	val = qpnp_flash_led_get_startup_dly(led->pdata->startup_dly);
+
+	rc = qpnp_led_masked_write(led,
+					FLASH_STARTUP_DELAY(led->base),
+						FLASH_STARTUP_DLY_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Startup delay reg write failed\n");
+		return rc;
+	}
+
+	val = (u8)(led->pdata->clamp_current * FLASH_MAX_LEVEL /
+						FLASH_LED_MAX_CURRENT_MA);
+	rc = qpnp_led_masked_write(led,
+					FLASH_CLAMP_CURRENT(led->base),
+						FLASH_CURRENT_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Clamp current reg write failed\n");
+		return rc;
+	}
+
+	if (led->pdata->pmic_charger_support)
+		val = FLASH_LED_FLASH_HW_VREG_OK;
+	else
+		val = FLASH_LED_FLASH_SW_VREG_OK;
+	rc = qpnp_led_masked_write(led,
+					FLASH_VREG_OK_FORCE(led->base),
+						FLASH_VREG_OK_FORCE_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "VREG OK force reg write failed\n");
+		return rc;
+	}
+
+	if (led->pdata->self_check_en)
+		val = FLASH_MODULE_ENABLE;
+	else
+		val = FLASH_LED_DISABLE;
+	rc = qpnp_led_masked_write(led,
+					FLASH_FAULT_DETECT(led->base),
+						FLASH_FAULT_DETECT_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Fault detect reg write failed\n");
+		return rc;
+	}
+
+	val = 0x0;
+	val |= led->pdata->mask3_en << FLASH_LED_MASK3_ENABLE_SHIFT;
+	val |= FLASH_LED_MASK_MODULE_MASK2_ENABLE;
+	rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
+				FLASH_MASK_MODULE_CONTRL_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Mask module enable failed\n");
+		return rc;
+	}
+
+	rc = regmap_read(led->regmap, FLASH_PERPH_RESET_CTRL(led->base),
+			&val_int);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read from address %x, rc(%d)\n",
+			FLASH_PERPH_RESET_CTRL(led->base), rc);
+		return -EINVAL;
+	}
+	val = (u8)val_int;
+
+	if (led->pdata->follow_rb_disable) {
+		rc = qpnp_led_masked_write(led,
+				FLASH_LED_UNLOCK_SECURE(led->base),
+				FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Secure reg write failed\n");
+			return -EINVAL;
+		}
+
+		val |= FLASH_FOLLOW_OTST2_RB_MASK;
+		rc = qpnp_led_masked_write(led,
+				FLASH_PERPH_RESET_CTRL(led->base),
+				FLASH_FOLLOW_OTST2_RB_MASK, val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"failed to reset OTST2_RB bit\n");
+			return rc;
+		}
+	} else {
+		rc = qpnp_led_masked_write(led,
+				FLASH_LED_UNLOCK_SECURE(led->base),
+				FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Secure reg write failed\n");
+			return -EINVAL;
+		}
+
+		val &= ~FLASH_FOLLOW_OTST2_RB_MASK;
+		rc = qpnp_led_masked_write(led,
+				FLASH_PERPH_RESET_CTRL(led->base),
+				FLASH_FOLLOW_OTST2_RB_MASK, val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"failed to reset OTST2_RB bit\n");
+			return rc;
+		}
+	}
+
+	if (!led->pdata->thermal_derate_en)
+		val = 0x0;
+	else {
+		val = led->pdata->thermal_derate_en << 7;
+		val |= led->pdata->thermal_derate_rate << 3;
+		val |= (led->pdata->thermal_derate_threshold -
+				FLASH_LED_THERMAL_THRESHOLD_MIN) /
+				FLASH_LED_THERMAL_DEVIDER;
+	}
+	rc = qpnp_led_masked_write(led,
+					FLASH_THERMAL_DRATE(led->base),
+					FLASH_THERMAL_DERATE_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Thermal derate reg write failed\n");
+		return rc;
+	}
+
+	if (!led->pdata->current_ramp_en)
+		val = 0x0;
+	else {
+		val = led->pdata->current_ramp_en << 7;
+		val |= led->pdata->ramp_up_step << 3;
+		val |= led->pdata->ramp_dn_step;
+	}
+	rc = qpnp_led_masked_write(led,
+						FLASH_CURRENT_RAMP(led->base),
+						FLASH_CURRENT_RAMP_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Current ramp reg write failed\n");
+		return rc;
+	}
+
+	if (!led->pdata->vph_pwr_droop_en)
+		val = 0x0;
+	else {
+		val = led->pdata->vph_pwr_droop_en << 7;
+		val |= ((led->pdata->vph_pwr_droop_threshold -
+				FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV) /
+				FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER) << 4;
+		temp_val =
+			qpnp_flash_led_get_droop_debounce_time(
+				led->pdata->vph_pwr_droop_debounce_time);
+		if (temp_val == 0xFF) {
+			dev_err(&led->pdev->dev, "Invalid debounce time\n");
+			return temp_val;
+		}
+
+		val |= temp_val;
+	}
+	rc = qpnp_led_masked_write(led,
+						FLASH_VPH_PWR_DROOP(led->base),
+						FLASH_VPH_PWR_DROOP_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "VPH PWR droop reg write failed\n");
+		return rc;
+	}
+
+	led->battery_psy = power_supply_get_by_name("battery");
+	if (!led->battery_psy) {
+		dev_err(&led->pdev->dev,
+			"Failed to get battery power supply\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
+					struct flash_node_data *flash_node)
+{
+	const char *temp_string;
+	struct device_node *node = flash_node->cdev.dev->of_node;
+	struct device_node *temp = NULL;
+	int rc = 0, num_regs = 0;
+	u32 val;
+
+	rc = of_property_read_string(node, "label", &temp_string);
+	if (!rc) {
+		if (strcmp(temp_string, "flash") == 0)
+			flash_node->type = FLASH;
+		else if (strcmp(temp_string, "torch") == 0)
+			flash_node->type = TORCH;
+		else if (strcmp(temp_string, "switch") == 0)
+			flash_node->type = SWITCH;
+		else {
+			dev_err(&led->pdev->dev, "Wrong flash LED type\n");
+			return -EINVAL;
+		}
+	} else if (rc < 0) {
+		dev_err(&led->pdev->dev, "Unable to read flash type\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,current", &val);
+	if (!rc) {
+		if (val < FLASH_LED_MIN_CURRENT_MA)
+			val = FLASH_LED_MIN_CURRENT_MA;
+		flash_node->prgm_current = val;
+	} else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read current\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,id", &val);
+	if (!rc)
+		flash_node->id = (u8)val;
+	else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read led ID\n");
+		return rc;
+	}
+
+	if (flash_node->type == SWITCH || flash_node->type == FLASH) {
+		rc = of_property_read_u32(node, "qcom,duration", &val);
+		if (!rc)
+			flash_node->duration = (u16)val;
+		else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev, "Unable to read duration\n");
+			return rc;
+		}
+	}
+
+	switch (led->peripheral_type) {
+	case FLASH_SUBTYPE_SINGLE:
+		flash_node->trigger = FLASH_LED0_TRIGGER;
+		break;
+	case FLASH_SUBTYPE_DUAL:
+		if (flash_node->id == FLASH_LED_0)
+			flash_node->trigger = FLASH_LED0_TRIGGER;
+		else if (flash_node->id == FLASH_LED_1)
+			flash_node->trigger = FLASH_LED1_TRIGGER;
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid peripheral type\n");
+	}
+
+	while ((temp = of_get_next_child(node, temp))) {
+		if (of_find_property(temp, "regulator-name", NULL))
+			num_regs++;
+	}
+
+	if (num_regs)
+		flash_node->num_regulators = num_regs;
+
+	return rc;
+}
+
+static int qpnp_flash_led_parse_common_dt(
+				struct qpnp_flash_led *led,
+						struct device_node *node)
+{
+	int rc;
+	u32 val, temp_val;
+	const char *temp;
+
+	led->pdata->headroom = FLASH_LED_HEADROOM_DEFAULT_MV;
+	rc = of_property_read_u32(node, "qcom,headroom", &val);
+	if (!rc)
+		led->pdata->headroom = (u16)val;
+	else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read headroom\n");
+		return rc;
+	}
+
+	led->pdata->startup_dly = FLASH_LED_STARTUP_DELAY_DEFAULT_US;
+	rc = of_property_read_u32(node, "qcom,startup-dly", &val);
+	if (!rc)
+		led->pdata->startup_dly = (u8)val;
+	else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read startup delay\n");
+		return rc;
+	}
+
+	led->pdata->clamp_current = FLASH_LED_CLAMP_CURRENT_DEFAULT_MA;
+	rc = of_property_read_u32(node, "qcom,clamp-current", &val);
+	if (!rc) {
+		if (val < FLASH_LED_MIN_CURRENT_MA)
+			val = FLASH_LED_MIN_CURRENT_MA;
+		led->pdata->clamp_current = (u16)val;
+	} else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read clamp current\n");
+		return rc;
+	}
+
+	led->pdata->pmic_charger_support =
+			of_property_read_bool(node,
+						"qcom,pmic-charger-support");
+
+	led->pdata->self_check_en =
+			of_property_read_bool(node, "qcom,self-check-enabled");
+
+	led->pdata->thermal_derate_en =
+			of_property_read_bool(node,
+						"qcom,thermal-derate-enabled");
+
+	if (led->pdata->thermal_derate_en) {
+		led->pdata->thermal_derate_rate =
+				FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT;
+		rc = of_property_read_string(node, "qcom,thermal-derate-rate",
+									&temp);
+		if (!rc) {
+			temp_val =
+				qpnp_flash_led_get_thermal_derate_rate(temp);
+			if (temp_val < 0) {
+				dev_err(&led->pdev->dev,
+					"Invalid thermal derate rate\n");
+				return -EINVAL;
+			}
+
+			led->pdata->thermal_derate_rate = (u8)temp_val;
+		} else {
+			dev_err(&led->pdev->dev,
+				"Unable to read thermal derate rate\n");
+			return -EINVAL;
+		}
+
+		led->pdata->thermal_derate_threshold =
+				FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C;
+		rc = of_property_read_u32(node, "qcom,thermal-derate-threshold",
+									&val);
+		if (!rc)
+			led->pdata->thermal_derate_threshold = (u8)val;
+		else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+				"Unable to read thermal derate threshold\n");
+			return rc;
+		}
+	}
+
+	led->pdata->current_ramp_en =
+			of_property_read_bool(node,
+						"qcom,current-ramp-enabled");
+	if (led->pdata->current_ramp_en) {
+		led->pdata->ramp_up_step = FLASH_LED_RAMP_UP_STEP_DEFAULT_US;
+		rc = of_property_read_string(node, "qcom,ramp_up_step", &temp);
+		if (!rc) {
+			temp_val = qpnp_flash_led_get_ramp_step(temp);
+			if (temp_val < 0) {
+				dev_err(&led->pdev->dev,
+					"Invalid ramp up step values\n");
+				return -EINVAL;
+			}
+			led->pdata->ramp_up_step = (u8)temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+					"Unable to read ramp up steps\n");
+			return rc;
+		}
+
+		led->pdata->ramp_dn_step = FLASH_LED_RAMP_DN_STEP_DEFAULT_US;
+		rc = of_property_read_string(node, "qcom,ramp_dn_step", &temp);
+		if (!rc) {
+			temp_val = qpnp_flash_led_get_ramp_step(temp);
+			if (temp_val < 0) {
+				dev_err(&led->pdev->dev,
+					"Invalid ramp down step values\n");
+				return rc;
+			}
+			led->pdata->ramp_dn_step = (u8)temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+					"Unable to read ramp down steps\n");
+			return rc;
+		}
+	}
+
+	led->pdata->vph_pwr_droop_en = of_property_read_bool(node,
+						"qcom,vph-pwr-droop-enabled");
+	if (led->pdata->vph_pwr_droop_en) {
+		led->pdata->vph_pwr_droop_threshold =
+				FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV;
+		rc = of_property_read_u32(node,
+					"qcom,vph-pwr-droop-threshold", &val);
+		if (!rc) {
+			led->pdata->vph_pwr_droop_threshold = (u16)val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+				"Unable to read VPH PWR droop threshold\n");
+			return rc;
+		}
+
+		led->pdata->vph_pwr_droop_debounce_time =
+			FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US;
+		rc = of_property_read_u32(node,
+				"qcom,vph-pwr-droop-debounce-time", &val);
+		if (!rc)
+			led->pdata->vph_pwr_droop_debounce_time = (u8)val;
+		else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+				"Unable to read VPH PWR droop debounce time\n");
+			return rc;
+		}
+	}
+
+	led->pdata->hdrm_sns_ch0_en = of_property_read_bool(node,
+						"qcom,headroom-sense-ch0-enabled");
+
+	led->pdata->hdrm_sns_ch1_en = of_property_read_bool(node,
+						"qcom,headroom-sense-ch1-enabled");
+
+	led->pdata->power_detect_en = of_property_read_bool(node,
+						"qcom,power-detect-enabled");
+
+	led->pdata->mask3_en = of_property_read_bool(node,
+						"qcom,otst2-module-enabled");
+
+	led->pdata->follow_rb_disable = of_property_read_bool(node,
+						"qcom,follow-otst2-rb-disabled");
+
+	led->pdata->die_current_derate_en = of_property_read_bool(node,
+					"qcom,die-current-derate-enabled");
+
+	if (led->pdata->die_current_derate_en) {
+		led->vadc_dev = qpnp_get_vadc(&led->pdev->dev, "die-temp");
+		if (IS_ERR(led->vadc_dev)) {
+			pr_err("VADC channel property Missing\n");
+			return -EINVAL;
+		}
+
+		if (of_find_property(node, "qcom,die-temp-threshold",
+				&led->pdata->temp_threshold_num)) {
+			if (led->pdata->temp_threshold_num > 0) {
+				led->pdata->die_temp_threshold_degc =
+				devm_kzalloc(&led->pdev->dev,
+						led->pdata->temp_threshold_num,
+						GFP_KERNEL);
+
+				if (led->pdata->die_temp_threshold_degc
+								== NULL) {
+					dev_err(&led->pdev->dev,
+					"failed to allocate die temp array\n");
+					return -ENOMEM;
+				}
+				led->pdata->temp_threshold_num /=
+							sizeof(unsigned int);
+
+				rc = of_property_read_u32_array(node,
+						"qcom,die-temp-threshold",
+				led->pdata->die_temp_threshold_degc,
+						led->pdata->temp_threshold_num);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"couldn't read temp threshold rc=%d\n",
+								rc);
+					return rc;
+				}
+			}
+		}
+
+		if (of_find_property(node, "qcom,die-temp-derate-current",
+					&led->pdata->temp_derate_curr_num)) {
+			if (led->pdata->temp_derate_curr_num > 0) {
+				led->pdata->die_temp_derate_curr_ma =
+					devm_kzalloc(&led->pdev->dev,
+					led->pdata->temp_derate_curr_num,
+					GFP_KERNEL);
+				if (led->pdata->die_temp_derate_curr_ma
+								== NULL) {
+					dev_err(&led->pdev->dev,
+						"failed to allocate die derate current array\n");
+					return -ENOMEM;
+				}
+				led->pdata->temp_derate_curr_num /=
+						sizeof(unsigned int);
+
+				rc = of_property_read_u32_array(node,
+						"qcom,die-temp-derate-current",
+				led->pdata->die_temp_derate_curr_ma,
+				led->pdata->temp_derate_curr_num);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"couldn't read temp limits rc =%d\n",
+								rc);
+					return rc;
+				}
+			}
+		}
+		if (led->pdata->temp_threshold_num !=
+					led->pdata->temp_derate_curr_num) {
+			pr_err("Both array size are not same\n");
+			return -EINVAL;
+		}
+	}
+
+	led->pinctrl = devm_pinctrl_get(&led->pdev->dev);
+	if (IS_ERR_OR_NULL(led->pinctrl)) {
+		dev_err(&led->pdev->dev, "Unable to acquire pinctrl\n");
+		led->pinctrl = NULL;
+		return 0;
+	}
+
+	led->gpio_state_active = pinctrl_lookup_state(led->pinctrl,
+							"flash_led_enable");
+	if (IS_ERR_OR_NULL(led->gpio_state_active)) {
+		dev_err(&led->pdev->dev, "Cannot lookup LED active state\n");
+		devm_pinctrl_put(led->pinctrl);
+		led->pinctrl = NULL;
+		return PTR_ERR(led->gpio_state_active);
+	}
+
+	led->gpio_state_suspend = pinctrl_lookup_state(led->pinctrl,
+							"flash_led_disable");
+	if (IS_ERR_OR_NULL(led->gpio_state_suspend)) {
+		dev_err(&led->pdev->dev, "Cannot lookup LED disable state\n");
+		devm_pinctrl_put(led->pinctrl);
+		led->pinctrl = NULL;
+		return PTR_ERR(led->gpio_state_suspend);
+	}
+
+	return 0;
+}
+
+static int qpnp_flash_led_probe(struct platform_device *pdev)
+{
+	struct qpnp_flash_led *led;
+	unsigned int base;
+	struct device_node *node, *temp;
+	struct dentry *root, *file;
+	int rc, i = 0, j, num_leds = 0;
+	u32 val;
+
+	root = NULL;
+	node = pdev->dev.of_node;
+	if (node == NULL) {
+		dev_info(&pdev->dev, "No flash device defined\n");
+		return -ENODEV;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+
+	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!led->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	led->base = base;
+	led->pdev = pdev;
+	led->current_addr = FLASH_LED0_CURRENT(led->base);
+	led->current2_addr = FLASH_LED1_CURRENT(led->base);
+
+	led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL);
+	if (!led->pdata)
+		return -ENOMEM;
+
+	led->peripheral_type = (u8)qpnp_flash_led_get_peripheral_type(led);
+	if (led->peripheral_type < 0) {
+		dev_err(&pdev->dev, "Failed to get peripheral type\n");
+		return rc;
+	}
+
+	rc = qpnp_flash_led_parse_common_dt(led, node);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"Failed to get common config for flash LEDs\n");
+		return rc;
+	}
+
+	rc = qpnp_flash_led_init_settings(led);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to initialize flash LED\n");
+		return rc;
+	}
+
+	rc = qpnp_get_pmic_revid(led);
+	if (rc)
+		return rc;
+
+	temp = NULL;
+	while ((temp = of_get_next_child(node, temp)))
+		num_leds++;
+
+	if (!num_leds)
+		return -ECHILD;
+
+	led->flash_node = devm_kzalloc(&pdev->dev,
+			(sizeof(struct flash_node_data) * num_leds),
+			GFP_KERNEL);
+	if (!led->flash_node) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&led->flash_led_lock);
+
+	led->ordered_workq = alloc_ordered_workqueue("flash_led_workqueue", 0);
+	if (!led->ordered_workq) {
+		dev_err(&pdev->dev, "Failed to allocate ordered workqueue\n");
+		return -ENOMEM;
+	}
+
+	for_each_child_of_node(node, temp) {
+		led->flash_node[i].cdev.brightness_set =
+						qpnp_flash_led_brightness_set;
+		led->flash_node[i].cdev.brightness_get =
+						qpnp_flash_led_brightness_get;
+		led->flash_node[i].pdev = pdev;
+
+		INIT_WORK(&led->flash_node[i].work, qpnp_flash_led_work);
+		rc = of_property_read_string(temp, "qcom,led-name",
+						&led->flash_node[i].cdev.name);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+					"Unable to read flash name\n");
+			return rc;
+		}
+
+		rc = of_property_read_string(temp, "qcom,default-led-trigger",
+				&led->flash_node[i].cdev.default_trigger);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+					"Unable to read trigger name\n");
+			return rc;
+		}
+
+		rc = of_property_read_u32(temp, "qcom,max-current", &val);
+		if (!rc) {
+			if (val < FLASH_LED_MIN_CURRENT_MA)
+				val = FLASH_LED_MIN_CURRENT_MA;
+			led->flash_node[i].max_current = (u16)val;
+			led->flash_node[i].cdev.max_brightness = val;
+		} else {
+			dev_err(&led->pdev->dev,
+					"Unable to read max current\n");
+			return rc;
+		}
+		rc = led_classdev_register(&pdev->dev,
+						&led->flash_node[i].cdev);
+		if (rc) {
+			dev_err(&pdev->dev, "Unable to register led\n");
+			goto error_led_register;
+		}
+
+		led->flash_node[i].cdev.dev->of_node = temp;
+
+		rc = qpnp_flash_led_parse_each_led_dt(led, &led->flash_node[i]);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"Failed to parse config for each LED\n");
+			goto error_led_register;
+		}
+
+		if (led->flash_node[i].num_regulators) {
+			rc = flash_regulator_parse_dt(led, &led->flash_node[i]);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"Unable to parse regulator data\n");
+				goto error_led_register;
+			}
+
+			rc = flash_regulator_setup(led, &led->flash_node[i],
+									true);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"Unable to set up regulator\n");
+				goto error_led_register;
+			}
+		}
+
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
+			rc =
+			sysfs_create_file(&led->flash_node[i].cdev.dev->kobj,
+					&qpnp_flash_led_attrs[j].attr);
+			if (rc)
+				goto error_led_register;
+		}
+
+		i++;
+	}
+
+	led->num_leds = i;
+
+	root = debugfs_create_dir("flashLED", NULL);
+	if (IS_ERR_OR_NULL(root)) {
+		pr_err("Error creating top level directory err%ld",
+			(long)root);
+		if (PTR_ERR(root) == -ENODEV)
+			pr_err("debugfs is not enabled in kernel");
+		goto error_led_debugfs;
+	}
+
+	led->dbgfs_root = root;
+	file = debugfs_create_file("enable_debug", 0600, root, led,
+					&flash_led_dfs_dbg_feature_fops);
+	if (!file) {
+		pr_err("error creating 'enable_debug' entry\n");
+		goto error_led_debugfs;
+	}
+
+	file = debugfs_create_file("latched", 0600, root, led,
+					&flash_led_dfs_latched_reg_fops);
+	if (!file) {
+		pr_err("error creating 'latched' entry\n");
+		goto error_led_debugfs;
+	}
+
+	file = debugfs_create_file("strobe", 0600, root, led,
+					&flash_led_dfs_strobe_reg_fops);
+	if (!file) {
+		pr_err("error creating 'strobe' entry\n");
+		goto error_led_debugfs;
+	}
+
+	dev_set_drvdata(&pdev->dev, led);
+
+	return 0;
+
+error_led_debugfs:
+	i = led->num_leds - 1;
+	j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
+error_led_register:
+	for (; i >= 0; i--) {
+		for (; j >= 0; j--)
+			sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
+						&qpnp_flash_led_attrs[j].attr);
+		j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
+		led_classdev_unregister(&led->flash_node[i].cdev);
+	}
+	debugfs_remove_recursive(root);
+	mutex_destroy(&led->flash_led_lock);
+	destroy_workqueue(led->ordered_workq);
+
+	return rc;
+}
+
+static int qpnp_flash_led_remove(struct platform_device *pdev)
+{
+	struct qpnp_flash_led *led  = dev_get_drvdata(&pdev->dev);
+	int i, j;
+
+	for (i = led->num_leds - 1; i >= 0; i--) {
+		if (led->flash_node[i].reg_data) {
+			if (led->flash_node[i].flash_on)
+				flash_regulator_enable(led,
+						&led->flash_node[i], false);
+			flash_regulator_setup(led, &led->flash_node[i],
+								false);
+		}
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
+			sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
+						&qpnp_flash_led_attrs[j].attr);
+		led_classdev_unregister(&led->flash_node[i].cdev);
+	}
+	debugfs_remove_recursive(led->dbgfs_root);
+	mutex_destroy(&led->flash_led_lock);
+	destroy_workqueue(led->ordered_workq);
+
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = "qcom,qpnp-flash-led",},
+	{ },
+};
+
+static struct platform_driver qpnp_flash_led_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-flash-led",
+		.of_match_table	= spmi_match_table,
+	},
+	.probe		= qpnp_flash_led_probe,
+	.remove		= qpnp_flash_led_remove,
+};
+
+static int __init qpnp_flash_led_init(void)
+{
+	return platform_driver_register(&qpnp_flash_led_driver);
+}
+late_initcall(qpnp_flash_led_init);
+
+static void __exit qpnp_flash_led_exit(void)
+{
+	platform_driver_unregister(&qpnp_flash_led_driver);
+}
+module_exit(qpnp_flash_led_exit);
+
+MODULE_DESCRIPTION("QPNP Flash LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qpnp-flash");
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
index dc041a7..749aa7f 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
@@ -182,7 +182,7 @@
 	struct sde_mdp_format_params *fmt;
 	u32 ot_lim;
 	u32 is_yuv;
-	u32 res;
+	u64 res;
 
 	ot_lim = (is_rd) ? mdata->default_ot_rd_limit :
 				mdata->default_ot_wr_limit;
@@ -198,7 +198,11 @@
 	if (false == test_bit(SDE_QOS_OTLIM, mdata->sde_qos_map))
 		goto exit;
 
+	width = min_t(u32, width, SDE_ROT_MAX_IMG_WIDTH);
+	height = min_t(u32, height, SDE_ROT_MAX_IMG_HEIGHT);
+
 	res = width * height;
+	res = res * fps;
 
 	fmt = sde_get_format_params(pixfmt);
 
@@ -209,17 +213,14 @@
 
 	is_yuv = sde_mdp_is_yuv_format(fmt);
 
-	SDEROT_DBG("w:%d h:%d fps:%d pixfmt:%8.8x yuv:%d res:%d rd:%d\n",
+	SDEROT_DBG("w:%d h:%d fps:%d pixfmt:%8.8x yuv:%d res:%llu rd:%d\n",
 		width, height, fps, pixfmt, is_yuv, res, is_rd);
 
-	if (!is_yuv)
-		goto exit;
-
-	if ((res <= RES_1080p) && (fps <= 30))
+	if (res <= (RES_1080p * 30))
 		ot_lim = 2;
-	else if ((res <= RES_1080p) && (fps <= 60))
+	else if (res <= (RES_1080p * 60))
 		ot_lim = 4;
-	else if ((res <= RES_UHD) && (fps <= 30))
+	else if (res <= (RES_UHD * 30))
 		ot_lim = 8;
 
 exit:
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index c7d1074..a455357 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -54,7 +54,7 @@
 #define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
 
 /* waiting for inline hw start */
-#define ROT_INLINE_START_TIMEOUT_IN_MS 2000
+#define ROT_INLINE_START_TIMEOUT_IN_MS	(10000 + 500)
 
 /* default pixel per clock ratio */
 #define ROT_PIXEL_PER_CLK_NUMERATOR	36
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
index 7585a6b..86e63c6 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_formats.c
@@ -84,6 +84,16 @@
 		.is_ubwc = isubwc,				\
 	}
 
+#define FMT_YUV10_COMMON(fmt)					\
+		.format = (fmt),				\
+		.is_yuv = 1,					\
+		.bits = {					\
+			[C2_R_Cr] = SDE_COLOR_8BIT,		\
+			[C0_G_Y] = SDE_COLOR_8BIT,		\
+			[C1_B_Cb] = SDE_COLOR_8BIT,		\
+		},						\
+		.alpha_enable = 0
+
 #define FMT_YUV_COMMON(fmt)					\
 		.format = (fmt),				\
 		.is_yuv = 1,					\
@@ -643,7 +653,7 @@
 		0, C2_R_Cr, C1_B_Cb, SDE_MDP_COMPRESS_NONE),
 
 	{
-		FMT_YUV_COMMON(SDE_PIX_FMT_Y_CBCR_H2V2_P010),
+		FMT_YUV10_COMMON(SDE_PIX_FMT_Y_CBCR_H2V2_P010),
 		.description = "SDE/Y_CBCR_H2V2_P010",
 		.flag = 0,
 		.fetch_planes = SDE_MDP_PLANE_PSEUDO_PLANAR,
@@ -658,6 +668,21 @@
 		.is_ubwc = SDE_MDP_COMPRESS_NONE,
 	},
 	{
+		FMT_YUV10_COMMON(SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS),
+		.description = "SDE/Y_CBCR_H2V2_P010_VENUS",
+		.flag = 0,
+		.fetch_planes = SDE_MDP_PLANE_PSEUDO_PLANAR,
+		.chroma_sample = SDE_MDP_CHROMA_420,
+		.unpack_count = 2,
+		.bpp = 2,
+		.frame_format = SDE_MDP_FMT_LINEAR,
+		.pixel_mode = SDE_MDP_PIXEL_10BIT,
+		.element = { C1_B_Cb, C2_R_Cr },
+		.unpack_tight = 0,
+		.unpack_align_msb = 1,
+		.is_ubwc = SDE_MDP_COMPRESS_NONE,
+	},
+	{
 		FMT_YUV_COMMON(SDE_PIX_FMT_Y_CBCR_H2V2_TP10),
 		.description = "SDE/Y_CBCR_H2V2_TP10",
 		.flag = 0,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 6ecec03..01aa1e4 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -312,6 +312,7 @@
 	SDE_PIX_FMT_RGBA_1010102_UBWC,
 	SDE_PIX_FMT_RGBX_1010102_UBWC,
 	SDE_PIX_FMT_Y_CBCR_H2V2_P010,
+	SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS,
 	SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
 	SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
 	SDE_PIX_FMT_Y_CBCR_H2V2_P010_UBWC,
@@ -392,6 +393,7 @@
 	SDE_PIX_FMT_RGBA_1010102_UBWC,
 	SDE_PIX_FMT_RGBX_1010102_UBWC,
 	SDE_PIX_FMT_Y_CBCR_H2V2_P010,
+	SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS,
 	SDE_PIX_FMT_Y_CBCR_H2V2_TP10,
 	SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC,
 	SDE_PIX_FMT_Y_CBCR_H2V2_P010_UBWC,
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
index ac4ab54..6eb2ab2 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -350,10 +350,27 @@
 			ps->plane_size[0] = w * h * bpp;
 			ps->ystride[0] = w * bpp;
 		} else if (fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_VENUS ||
-				fmt->format == SDE_PIX_FMT_Y_CRCB_H2V2_VENUS) {
+			fmt->format == SDE_PIX_FMT_Y_CRCB_H2V2_VENUS ||
+			fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS) {
 
-			int cf = (fmt->format == SDE_PIX_FMT_Y_CBCR_H2V2_VENUS)
-					? COLOR_FMT_NV12 : COLOR_FMT_NV21;
+			int cf;
+
+			switch (fmt->format) {
+			case SDE_PIX_FMT_Y_CBCR_H2V2_VENUS:
+				cf = COLOR_FMT_NV12;
+				break;
+			case SDE_PIX_FMT_Y_CRCB_H2V2_VENUS:
+				cf = COLOR_FMT_NV21;
+				break;
+			case SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS:
+				cf = COLOR_FMT_P010;
+				break;
+			default:
+				SDEROT_ERR("unknown color format %d\n",
+						fmt->format);
+				return -EINVAL;
+			}
+
 			ps->num_planes = 2;
 			ps->ystride[0] = VENUS_Y_STRIDE(cf, w);
 			ps->ystride[1] = VENUS_UV_STRIDE(cf, w);
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 9238176..1c9c91d 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -491,7 +491,7 @@
 	{
 		.name = "YCbCr Semiplanar 4:2:0 10bit",
 		.description = "Y/CbCr 4:2:0 10bit",
-		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010,
+		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS,
 		.get_frame_size = get_frame_size_p010,
 		.type = CAPTURE_PORT,
 	},
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index dd62fb7..ba49f24 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1275,7 +1275,7 @@
 	{
 		.name = "YCbCr Semiplanar 4:2:0 10bit",
 		.description = "Y/CbCr 4:2:0 10bit",
-		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010,
+		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS,
 		.get_frame_size = get_frame_size_p010,
 		.type = OUTPUT_PORT,
 	},
@@ -1868,7 +1868,7 @@
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME:
 		property_id = HAL_CONFIG_VENC_USELTRFRAME;
-		use_ltr.ref_ltr = 0x1 << ctrl->val;
+		use_ltr.ref_ltr = ctrl->val;
 		use_ltr.use_constraint = false;
 		use_ltr.frames = 0;
 		pdata = &use_ltr;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index dabe667..349b982 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -281,7 +281,7 @@
 	case V4L2_PIX_FMT_NV12_TP10_UBWC:
 		color_format = COLOR_FMT_NV12_BPP10_UBWC;
 		break;
-	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010:
+	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS:
 		color_format = COLOR_FMT_P010;
 		break;
 	default:
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 9dce3f9..4c000b7 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -961,7 +961,7 @@
 	case V4L2_PIX_FMT_NV12_TP10_UBWC:
 		format = HAL_COLOR_FORMAT_NV12_TP10_UBWC;
 		break;
-	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010:
+	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS:
 		format = HAL_COLOR_FORMAT_P010;
 		break;
 	default:
@@ -5761,8 +5761,9 @@
 					skip = true;
 			} else if (b->type ==
 					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
-				if (!i) /* yuv */
-					skip = true;
+				if (!i) { /* yuv */
+					/* all values are correct */
+				}
 			}
 		} else if (inst->session_type == MSM_VIDC_ENCODER) {
 			if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 5e7595c..0e48938 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1320,6 +1320,8 @@
 					descr = "Y/CbCr 4:2:0 TP10"; break;
 	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010:
 					descr = "Y/CbCr 4:2:0 P10"; break;
+	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS:
+					descr = "Y/CbCr 4:2:0 P10 Venus"; break;
 	case V4L2_PIX_FMT_NV12_TP10_UBWC:
 					descr = "Y/CbCr 4:2:0 TP10 UBWC"; break;
 	case V4L2_PIX_FMT_NV12_P010_UBWC:
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 538a8d9..e4af5c3 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3184,6 +3184,16 @@
 	return &mqrq->cmdq_req;
 }
 
+static void mmc_blk_cmdq_requeue_rw_rq(struct mmc_queue *mq,
+				struct request *req)
+{
+	struct mmc_card *card = mq->card;
+	struct mmc_host *host = card->host;
+
+	blk_requeue_request(req->q, req);
+	mmc_put_card(host->card);
+}
+
 static int mmc_blk_cmdq_issue_rw_rq(struct mmc_queue *mq, struct request *req)
 {
 	struct mmc_queue_req *active_mqrq;
@@ -3231,6 +3241,15 @@
 		wait_event_interruptible(ctx->queue_empty_wq,
 					(!ctx->active_reqs));
 
+	if (ret) {
+		/* clear pending request */
+		WARN_ON(!test_and_clear_bit(req->tag,
+				&host->cmdq_ctx.data_active_reqs));
+		WARN_ON(!test_and_clear_bit(req->tag,
+				&host->cmdq_ctx.active_reqs));
+		mmc_cmdq_clk_scaling_stop_busy(host, true, false);
+	}
+
 	return ret;
 }
 
@@ -4058,6 +4077,13 @@
 			ret = mmc_blk_cmdq_issue_flush_rq(mq, req);
 		} else {
 			ret = mmc_blk_cmdq_issue_rw_rq(mq, req);
+			/*
+			 * If issuing of the request fails with eitehr EBUSY or
+			 * EAGAIN error, re-queue the request.
+			 * This case would occur with ICE calls.
+			 */
+			if (ret == -EBUSY || ret == -EAGAIN)
+				mmc_blk_cmdq_requeue_rw_rq(mq, req);
 		}
 	}
 
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 30180af..c172be9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1249,9 +1249,11 @@
 	return err;
 }
 
-static void mmc_start_cmdq_request(struct mmc_host *host,
+static int mmc_start_cmdq_request(struct mmc_host *host,
 				   struct mmc_request *mrq)
 {
+	int ret = 0;
+
 	if (mrq->data) {
 		pr_debug("%s:     blksz %d blocks %d flags %08x tsac %lu ms nsac %d\n",
 			mmc_hostname(host), mrq->data->blksz,
@@ -1274,11 +1276,21 @@
 
 	mmc_host_clk_hold(host);
 	mmc_cmdq_check_retune(host);
-	if (likely(host->cmdq_ops->request))
-		host->cmdq_ops->request(host, mrq);
-	else
-		pr_err("%s: %s: issue request failed\n", mmc_hostname(host),
-				__func__);
+	if (likely(host->cmdq_ops->request)) {
+		ret = host->cmdq_ops->request(host, mrq);
+	} else {
+		ret = -ENOENT;
+		pr_err("%s: %s: cmdq request host op is not available\n",
+			mmc_hostname(host), __func__);
+	}
+
+	if (ret) {
+		mmc_host_clk_release(host);
+		pr_err("%s: %s: issue request failed, err=%d\n",
+			mmc_hostname(host), __func__, ret);
+	}
+
+	return ret;
 }
 
 /**
@@ -1810,8 +1822,7 @@
 		mrq->cmd->error = -ENOMEDIUM;
 		return -ENOMEDIUM;
 	}
-	mmc_start_cmdq_request(host, mrq);
-	return 0;
+	return mmc_start_cmdq_request(host, mrq);
 }
 EXPORT_SYMBOL(mmc_cmdq_start_req);
 
@@ -3461,6 +3472,9 @@
 	if (cd_irq && mmc_bus_manual_resume(host))
 		host->ignore_bus_resume_flags = true;
 
+	if (delayed_work_pending(&host->detect))
+		cancel_delayed_work(&host->detect);
+
 	mmc_schedule_delayed_work(&host->detect, delay);
 }
 
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 8a503b2..3e0ba75 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -737,19 +737,19 @@
 	mmc_get_card(host->card);
 
 	if (!value) {
-		/*turning off clock scaling*/
-		mmc_exit_clk_scaling(host);
+		/* Suspend the clock scaling and mask host capability */
+		if (host->clk_scaling.enable)
+			mmc_suspend_clk_scaling(host);
 		host->caps2 &= ~MMC_CAP2_CLK_SCALE;
 		host->clk_scaling.state = MMC_LOAD_HIGH;
 		/* Set to max. frequency when disabling */
 		mmc_clk_update_freq(host, host->card->clk_scaling_highest,
 					host->clk_scaling.state);
 	} else if (value) {
-		/* starting clock scaling, will restart in case started */
+		/* Unmask host capability and resume scaling */
 		host->caps2 |= MMC_CAP2_CLK_SCALE;
-		if (host->clk_scaling.enable)
-			mmc_exit_clk_scaling(host);
-		mmc_init_clk_scaling(host);
+		if (!host->clk_scaling.enable)
+			mmc_resume_clk_scaling(host);
 	}
 
 	mmc_put_card(host->card);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 10d55b8..e3bbc2c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1309,7 +1309,7 @@
 	while (retries) {
 		err = mmc_sd_init_card(host, host->card->ocr, host->card);
 
-		if (err) {
+		if (err && err != -ENOENT) {
 			printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n",
 			       mmc_hostname(host), err, retries);
 			retries--;
@@ -1324,6 +1324,12 @@
 #else
 	err = mmc_sd_init_card(host, host->card->ocr, host->card);
 #endif
+	if (err == -ENOENT) {
+		pr_debug("%s: %s: found a different card(%d), do detect change\n",
+			mmc_hostname(host), __func__, err);
+		mmc_card_set_removed(host->card);
+		mmc_detect_change(host, msecs_to_jiffies(200));
+	}
 	mmc_card_clr_suspended(host->card);
 
 	if (host->card->sdr104_blocked)
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index f16a999..55ce946 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -806,7 +806,7 @@
 			mmc->err_stats[MMC_ERR_ICE_CFG]++;
 			pr_err("%s: failed to configure crypto: err %d tag %d\n",
 					mmc_hostname(mmc), err, tag);
-			goto out;
+			goto ice_err;
 		}
 	}
 
@@ -824,7 +824,7 @@
 	if (err) {
 		pr_err("%s: %s: failed to setup tx desc: %d\n",
 		       mmc_hostname(mmc), __func__, err);
-		goto out;
+		goto desc_err;
 	}
 
 	cq_host->mrq_slot[tag] = mrq;
@@ -844,6 +844,22 @@
 	/* Commit the doorbell write immediately */
 	wmb();
 
+	return err;
+
+desc_err:
+	if (cq_host->ops->crypto_cfg_end) {
+		err = cq_host->ops->crypto_cfg_end(mmc, mrq);
+		if (err) {
+			pr_err("%s: failed to end ice config: err %d tag %d\n",
+					mmc_hostname(mmc), err, tag);
+		}
+	}
+	if (!(cq_host->caps & CMDQ_CAP_CRYPTO_SUPPORT) &&
+			cq_host->ops->crypto_cfg_reset)
+		cq_host->ops->crypto_cfg_reset(mmc, tag);
+ice_err:
+	if (err)
+		cmdq_runtime_pm_put(cq_host);
 out:
 	return err;
 }
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index e817a02..7880405 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1900,6 +1900,8 @@
 	u32 *ice_clk_table = NULL;
 	enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
 	const char *lower_bus_speed = NULL;
+	int bus_clk_table_len;
+	u32 *bus_clk_table = NULL;
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
@@ -1955,6 +1957,14 @@
 	pdata->sup_clk_table = clk_table;
 	pdata->sup_clk_cnt = clk_table_len;
 
+	if (!sdhci_msm_dt_get_array(dev, "qcom,bus-aggr-clk-rates",
+			&bus_clk_table, &bus_clk_table_len, 0)) {
+		if (bus_clk_table && bus_clk_table_len) {
+			pdata->bus_clk_table = bus_clk_table;
+			pdata->bus_clk_cnt = bus_clk_table_len;
+		}
+	}
+
 	if (msm_host->ice.pdev) {
 		if (sdhci_msm_dt_get_array(dev, "qcom,ice-clk-rates",
 				&ice_clk_table, &ice_clk_table_len, 0)) {
@@ -2962,6 +2972,34 @@
 	return sel_clk;
 }
 
+static long sdhci_msm_get_bus_aggr_clk_rate(struct sdhci_host *host,
+						u32 apps_clk)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	long sel_clk = -1;
+	unsigned char cnt;
+
+	if (msm_host->pdata->bus_clk_cnt != msm_host->pdata->sup_clk_cnt) {
+		pr_err("%s: %s: mismatch between bus_clk_cnt(%u) and apps_clk_cnt(%u)\n",
+				mmc_hostname(host->mmc), __func__,
+				(unsigned int)msm_host->pdata->bus_clk_cnt,
+				(unsigned int)msm_host->pdata->sup_clk_cnt);
+		return msm_host->pdata->bus_clk_table[0];
+	}
+	if (apps_clk == sdhci_msm_get_min_clock(host)) {
+		sel_clk = msm_host->pdata->bus_clk_table[0];
+		return sel_clk;
+	}
+
+	for (cnt = 0; cnt < msm_host->pdata->bus_clk_cnt; cnt++) {
+		if (msm_host->pdata->sup_clk_table[cnt] > apps_clk)
+			break;
+		sel_clk = msm_host->pdata->bus_clk_table[cnt];
+	}
+	return sel_clk;
+}
+
 static void sdhci_msm_registers_save(struct sdhci_host *host)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -3251,6 +3289,7 @@
 	struct mmc_card *card = host->mmc->card;
 	struct mmc_ios	curr_ios = host->mmc->ios;
 	u32 sup_clock, ddr_clock, dll_lock;
+	long bus_clk_rate;
 	bool curr_pwrsave;
 
 	if (!clock) {
@@ -3405,6 +3444,26 @@
 		msm_host->clk_rate = sup_clock;
 		host->clock = clock;
 
+		if (!IS_ERR(msm_host->bus_aggr_clk) &&
+				msm_host->pdata->bus_clk_cnt) {
+			bus_clk_rate = sdhci_msm_get_bus_aggr_clk_rate(host,
+					sup_clock);
+			if (bus_clk_rate >= 0) {
+				rc = clk_set_rate(msm_host->bus_aggr_clk,
+						bus_clk_rate);
+				if (rc) {
+					pr_err("%s: %s: Failed to set rate %ld for bus-aggr-clk : %d\n",
+						mmc_hostname(host->mmc),
+						__func__, bus_clk_rate, rc);
+					goto out;
+				}
+			} else {
+				pr_err("%s: %s: Unsupported apps clk rate %u for bus-aggr-clk, err: %ld\n",
+					mmc_hostname(host->mmc), __func__,
+					sup_clock, bus_clk_rate);
+			}
+		}
+
 		/* Configure pinctrl drive type according to
 		 * current clock rate
 		 */
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 6e15a73..7c737cc 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -159,6 +159,8 @@
 	u32 ice_clk_min;
 	u32 ddr_config;
 	bool rclk_wa;
+	u32 *bus_clk_table;
+	unsigned char bus_clk_cnt;
 };
 
 struct sdhci_msm_bus_vote {
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index aef0db2..6117d4d 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -112,6 +112,27 @@
 	help
 	  No-Data-Path BAM is used to improve BAM performance.
 
+config EP_PCIE
+	bool "PCIe Endpoint mode support"
+	select GENERIC_ALLOCATOR
+	help
+	  PCIe controller is in endpoint mode.
+	  It supports the APIs to clients as a service layer, and allows
+	  clients to enable/disable PCIe link, configure the address
+	  mapping for the access to host memory, trigger wake interrupt
+	  on host side to wake up host, and trigger MSI to host side.
+
+config EP_PCIE_HW
+	bool "PCIe Endpoint HW driver"
+	depends on EP_PCIE
+	help
+	  PCIe endpoint HW specific implementation.
+	  It supports:
+		1. link training with Root Complex.
+		2. Address mapping.
+		3. Sideband signaling.
+		4. Power management.
+
 config QPNP_COINCELL
 	tristate "QPNP coincell charger support"
 	depends on SPMI
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index 27179b9..bee32c2 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -7,6 +7,7 @@
 obj-$(CONFIG_SPS) += sps/
 obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o
 obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
+obj-$(CONFIG_EP_PCIE) += ep_pcie/
 obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/
 obj-$(CONFIG_USB_BAM) += usb_bam.o
 obj-$(CONFIG_MSM_11AD) += msm_11ad/
diff --git a/drivers/platform/msm/ep_pcie/Makefile b/drivers/platform/msm/ep_pcie/Makefile
new file mode 100644
index 0000000..0567e15
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_EP_PCIE) += ep_pcie.o
+obj-$(CONFIG_EP_PCIE_HW) += ep_pcie_core.o ep_pcie_phy.o ep_pcie_dbg.o
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie.c b/drivers/platform/msm/ep_pcie/ep_pcie.c
new file mode 100644
index 0000000..ecff4c4
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/ep_pcie.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * MSM PCIe endpoint service layer.
+ */
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include "ep_pcie_com.h"
+
+LIST_HEAD(head);
+
+int ep_pcie_register_drv(struct ep_pcie_hw *handle)
+{
+	struct ep_pcie_hw *present;
+	bool new = true;
+
+	if (!handle) {
+		pr_err("ep_pcie:%s: the input handle is NULL.",
+			__func__);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(present, &head, node) {
+		if (present->device_id == handle->device_id) {
+			new = false;
+			break;
+		}
+	}
+
+	if (new) {
+		list_add(&handle->node, &head);
+		pr_debug("ep_pcie:%s: register a new driver for device 0x%x.",
+			__func__, handle->device_id);
+		return 0;
+	}
+	pr_debug(
+		"ep_pcie:%s: driver to register for device 0x%x has already existed.",
+		__func__, handle->device_id);
+	return -EEXIST;
+}
+EXPORT_SYMBOL(ep_pcie_register_drv);
+
+int ep_pcie_deregister_drv(struct ep_pcie_hw *handle)
+{
+	struct ep_pcie_hw *present;
+	bool found = false;
+
+	if (!handle) {
+		pr_err("ep_pcie:%s: the input handle is NULL.",
+			__func__);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(present, &head, node) {
+		if (present->device_id == handle->device_id) {
+			found = true;
+			list_del(&handle->node);
+			break;
+		}
+	}
+
+	if (found) {
+		pr_debug("ep_pcie:%s: deregistered driver for device 0x%x.",
+			__func__, handle->device_id);
+		return 0;
+	}
+	pr_err("ep_pcie:%s: driver for device 0x%x does not exist.",
+		__func__, handle->device_id);
+	return -EEXIST;
+}
+EXPORT_SYMBOL(ep_pcie_deregister_drv);
+
+struct ep_pcie_hw *ep_pcie_get_phandle(u32 id)
+{
+	struct ep_pcie_hw *present;
+
+	list_for_each_entry(present, &head, node) {
+		if (present->device_id == id) {
+			pr_debug("ep_pcie:%s: found driver for device 0x%x.",
+				__func__, id);
+			return present;
+		}
+	}
+
+	pr_debug("ep_pcie:%s: driver for device 0x%x does not exist.",
+			__func__, id);
+	return NULL;
+}
+EXPORT_SYMBOL(ep_pcie_get_phandle);
+
+int ep_pcie_register_event(struct ep_pcie_hw *phandle,
+			struct ep_pcie_register_event *reg)
+{
+	if (phandle)
+		return phandle->register_event(reg);
+
+	return ep_pcie_core_register_event(reg);
+}
+EXPORT_SYMBOL(ep_pcie_register_event);
+
+int ep_pcie_deregister_event(struct ep_pcie_hw *phandle)
+{
+	if (phandle)
+		return phandle->deregister_event();
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_deregister_event);
+
+enum ep_pcie_link_status ep_pcie_get_linkstatus(struct ep_pcie_hw *phandle)
+{
+	if (phandle)
+		return phandle->get_linkstatus();
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_get_linkstatus);
+
+int ep_pcie_config_outbound_iatu(struct ep_pcie_hw *phandle,
+				struct ep_pcie_iatu entries[],
+				u32 num_entries)
+{
+	if (phandle)
+		return phandle->config_outbound_iatu(entries, num_entries);
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_config_outbound_iatu);
+
+int ep_pcie_get_msi_config(struct ep_pcie_hw *phandle,
+				struct ep_pcie_msi_config *cfg)
+{
+	if (phandle)
+		return phandle->get_msi_config(cfg);
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_get_msi_config);
+
+int ep_pcie_trigger_msi(struct ep_pcie_hw *phandle, u32 idx)
+{
+	if (phandle)
+		return phandle->trigger_msi(idx);
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_trigger_msi);
+
+int ep_pcie_wakeup_host(struct ep_pcie_hw *phandle)
+{
+	if (phandle)
+		return phandle->wakeup_host();
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_wakeup_host);
+
+int ep_pcie_config_db_routing(struct ep_pcie_hw *phandle,
+				struct ep_pcie_db_config chdb_cfg,
+				struct ep_pcie_db_config erdb_cfg)
+{
+	if (phandle)
+		return phandle->config_db_routing(chdb_cfg, erdb_cfg);
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_config_db_routing);
+
+int ep_pcie_enable_endpoint(struct ep_pcie_hw *phandle,
+				enum ep_pcie_options opt)
+{
+	if (phandle)
+		return phandle->enable_endpoint(opt);
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_enable_endpoint);
+
+int ep_pcie_disable_endpoint(struct ep_pcie_hw *phandle)
+{
+	if (phandle)
+		return phandle->disable_endpoint();
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.",
+		__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_disable_endpoint);
+
+int ep_pcie_mask_irq_event(struct ep_pcie_hw *phandle,
+				enum ep_pcie_irq_event event,
+				bool enable)
+{
+	if (phandle)
+		return phandle->mask_irq_event(event, enable);
+
+	pr_err("ep_pcie:%s: the input driver handle is NULL.", __func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(ep_pcie_mask_irq_event);
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h
new file mode 100644
index 0000000..7553a24
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h
@@ -0,0 +1,391 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __EP_PCIE_COM_H
+#define __EP_PCIE_COM_H
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/ipc_logging.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/msm_ep_pcie.h>
+
+#define PCIE20_PARF_SYS_CTRL           0x00
+#define PCIE20_PARF_DB_CTRL            0x10
+#define PCIE20_PARF_PM_CTRL            0x20
+#define PCIE20_PARF_PM_STTS            0x24
+#define PCIE20_PARF_PHY_CTRL           0x40
+#define PCIE20_PARF_PHY_REFCLK         0x4C
+#define PCIE20_PARF_CONFIG_BITS        0x50
+#define PCIE20_PARF_TEST_BUS           0xE4
+#define PCIE20_PARF_MHI_BASE_ADDR_LOWER 0x178
+#define PCIE20_PARF_MHI_BASE_ADDR_UPPER 0x17c
+#define PCIE20_PARF_MSI_GEN             0x188
+#define PCIE20_PARF_DEBUG_INT_EN        0x190
+#define PCIE20_PARF_MHI_IPA_DBS                0x198
+#define PCIE20_PARF_MHI_IPA_CDB_TARGET_LOWER   0x19C
+#define PCIE20_PARF_MHI_IPA_EDB_TARGET_LOWER   0x1A0
+#define PCIE20_PARF_AXI_MSTR_RD_HALT_NO_WRITES 0x1A4
+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT      0x1A8
+#define PCIE20_PARF_Q2A_FLUSH          0x1AC
+#define PCIE20_PARF_LTSSM              0x1B0
+#define PCIE20_PARF_CFG_BITS           0x210
+#define PCIE20_PARF_LTR_MSI_EXIT_L1SS  0x214
+#define PCIE20_PARF_INT_ALL_STATUS     0x224
+#define PCIE20_PARF_INT_ALL_CLEAR      0x228
+#define PCIE20_PARF_INT_ALL_MASK       0x22C
+#define PCIE20_PARF_SLV_ADDR_MSB_CTRL  0x2C0
+#define PCIE20_PARF_DBI_BASE_ADDR      0x350
+#define PCIE20_PARF_DBI_BASE_ADDR_HI   0x354
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE        0x358
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE_HI     0x35C
+#define PCIE20_PARF_DEVICE_TYPE        0x1000
+
+#define PCIE20_ELBI_VERSION            0x00
+#define PCIE20_ELBI_SYS_CTRL           0x04
+#define PCIE20_ELBI_SYS_STTS	       0x08
+#define PCIE20_ELBI_CS2_ENABLE         0xA4
+
+#define PCIE20_DEVICE_ID_VENDOR_ID     0x00
+#define PCIE20_COMMAND_STATUS          0x04
+#define PCIE20_CLASS_CODE_REVISION_ID  0x08
+#define PCIE20_BIST_HDR_TYPE           0x0C
+#define PCIE20_BAR0                    0x10
+#define PCIE20_SUBSYSTEM               0x2c
+#define PCIE20_CAP_ID_NXT_PTR          0x40
+#define PCIE20_CON_STATUS              0x44
+#define PCIE20_MSI_CAP_ID_NEXT_CTRL    0x50
+#define PCIE20_MSI_LOWER               0x54
+#define PCIE20_MSI_UPPER               0x58
+#define PCIE20_MSI_DATA                0x5C
+#define PCIE20_MSI_MASK                0x60
+#define PCIE20_DEVICE_CAPABILITIES     0x74
+#define PCIE20_MASK_EP_L1_ACCPT_LATENCY 0xE00
+#define PCIE20_MASK_EP_L0S_ACCPT_LATENCY 0x1C0
+#define PCIE20_LINK_CAPABILITIES       0x7C
+#define PCIE20_MASK_CLOCK_POWER_MAN    0x40000
+#define PCIE20_MASK_L1_EXIT_LATENCY    0x38000
+#define PCIE20_MASK_L0S_EXIT_LATENCY   0x7000
+#define PCIE20_CAP_LINKCTRLSTATUS      0x80
+#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98
+#define PCIE20_LINK_CONTROL2_LINK_STATUS2 0xA0
+#define PCIE20_L1SUB_CAPABILITY        0x154
+#define PCIE20_L1SUB_CONTROL1          0x158
+#define PCIE20_ACK_F_ASPM_CTRL_REG     0x70C
+#define PCIE20_MASK_ACK_N_FTS          0xff00
+#define PCIE20_MISC_CONTROL_1          0x8BC
+
+#define PCIE20_PLR_IATU_VIEWPORT       0x900
+#define PCIE20_PLR_IATU_CTRL1          0x904
+#define PCIE20_PLR_IATU_CTRL2          0x908
+#define PCIE20_PLR_IATU_LBAR           0x90C
+#define PCIE20_PLR_IATU_UBAR           0x910
+#define PCIE20_PLR_IATU_LAR            0x914
+#define PCIE20_PLR_IATU_LTAR           0x918
+#define PCIE20_PLR_IATU_UTAR           0x91c
+
+#define PCIE20_MHICFG                  0x110
+#define PCIE20_BHI_EXECENV             0x228
+
+#define PCIE20_AUX_CLK_FREQ_REG        0xB40
+
+#define PERST_TIMEOUT_US_MIN	              1000
+#define PERST_TIMEOUT_US_MAX	              1000
+#define PERST_CHECK_MAX_COUNT		      30000
+#define LINK_UP_TIMEOUT_US_MIN	              1000
+#define LINK_UP_TIMEOUT_US_MAX	              1000
+#define LINK_UP_CHECK_MAX_COUNT		      30000
+#define BME_TIMEOUT_US_MIN	              1000
+#define BME_TIMEOUT_US_MAX	              1000
+#define BME_CHECK_MAX_COUNT		      30000
+#define PHY_STABILIZATION_DELAY_US_MIN	      1000
+#define PHY_STABILIZATION_DELAY_US_MAX	      1000
+#define REFCLK_STABILIZATION_DELAY_US_MIN     1000
+#define REFCLK_STABILIZATION_DELAY_US_MAX     1000
+#define PHY_READY_TIMEOUT_COUNT               30000
+#define MSI_EXIT_L1SS_WAIT	              10
+#define MSI_EXIT_L1SS_WAIT_MAX_COUNT          100
+#define XMLH_LINK_UP                          0x400
+#define PARF_XMLH_LINK_UP                     0x40000000
+
+#define MAX_PROP_SIZE 32
+#define MAX_MSG_LEN 80
+#define MAX_NAME_LEN 80
+#define MAX_IATU_ENTRY_NUM 2
+
+#define EP_PCIE_LOG_PAGES 50
+#define EP_PCIE_MAX_VREG 2
+#define EP_PCIE_MAX_CLK 5
+#define EP_PCIE_MAX_PIPE_CLK 1
+#define EP_PCIE_MAX_RESET 2
+
+#define EP_PCIE_ERROR -30655
+#define EP_PCIE_LINK_DOWN 0xFFFFFFFF
+
+#define EP_PCIE_OATU_INDEX_MSI 1
+#define EP_PCIE_OATU_INDEX_CTRL 2
+#define EP_PCIE_OATU_INDEX_DATA 3
+
+#define EP_PCIE_OATU_UPPER 0x100
+
+#define EP_PCIE_GEN_DBG(x...) do { \
+	if (ep_pcie_get_debug_mask()) \
+		pr_alert(x); \
+	else \
+		pr_debug(x); \
+	} while (0)
+
+#define EP_PCIE_DBG(dev, fmt, arg...) do {			 \
+	if ((dev)->ipc_log_ful)   \
+		ipc_log_string((dev)->ipc_log_ful, "%s: " fmt, __func__, arg); \
+	if (ep_pcie_get_debug_mask())   \
+		pr_alert("%s: " fmt, __func__, arg);		  \
+	} while (0)
+
+#define EP_PCIE_DBG2(dev, fmt, arg...) do {			 \
+	if ((dev)->ipc_log_sel)   \
+		ipc_log_string((dev)->ipc_log_sel, \
+			"DBG1:%s: " fmt, __func__, arg); \
+	if ((dev)->ipc_log_ful)   \
+		ipc_log_string((dev)->ipc_log_ful, \
+			"DBG2:%s: " fmt, __func__, arg); \
+	if (ep_pcie_get_debug_mask())   \
+		pr_alert("%s: " fmt, __func__, arg); \
+	} while (0)
+
+#define EP_PCIE_DBG_FS(fmt, arg...) pr_alert("%s: " fmt, __func__, arg)
+
+#define EP_PCIE_DUMP(dev, fmt, arg...) do {			\
+	if ((dev)->ipc_log_dump) \
+		ipc_log_string((dev)->ipc_log_dump, \
+			"DUMP:%s: " fmt, __func__, arg); \
+	if (ep_pcie_get_debug_mask())   \
+		pr_alert("%s: " fmt, __func__, arg); \
+	} while (0)
+
+#define EP_PCIE_INFO(dev, fmt, arg...) do {			 \
+	if ((dev)->ipc_log_sel)   \
+		ipc_log_string((dev)->ipc_log_sel, \
+			"INFO:%s: " fmt, __func__, arg); \
+	if ((dev)->ipc_log_ful)   \
+		ipc_log_string((dev)->ipc_log_ful, "%s: " fmt, __func__, arg); \
+	pr_info("%s: " fmt, __func__, arg);  \
+	} while (0)
+
+#define EP_PCIE_ERR(dev, fmt, arg...) do {			 \
+	if ((dev)->ipc_log_sel)   \
+		ipc_log_string((dev)->ipc_log_sel, \
+			"ERR:%s: " fmt, __func__, arg); \
+	if ((dev)->ipc_log_ful)   \
+		ipc_log_string((dev)->ipc_log_ful, "%s: " fmt, __func__, arg); \
+	pr_err("%s: " fmt, __func__, arg);  \
+	} while (0)
+
+enum ep_pcie_res {
+	EP_PCIE_RES_PARF,
+	EP_PCIE_RES_PHY,
+	EP_PCIE_RES_MMIO,
+	EP_PCIE_RES_MSI,
+	EP_PCIE_RES_DM_CORE,
+	EP_PCIE_RES_ELBI,
+	EP_PCIE_MAX_RES,
+};
+
+enum ep_pcie_irq {
+	EP_PCIE_INT_PM_TURNOFF,
+	EP_PCIE_INT_DSTATE_CHANGE,
+	EP_PCIE_INT_L1SUB_TIMEOUT,
+	EP_PCIE_INT_LINK_UP,
+	EP_PCIE_INT_LINK_DOWN,
+	EP_PCIE_INT_BRIDGE_FLUSH_N,
+	EP_PCIE_INT_BME,
+	EP_PCIE_INT_GLOBAL,
+	EP_PCIE_MAX_IRQ,
+};
+
+enum ep_pcie_gpio {
+	EP_PCIE_GPIO_PERST,
+	EP_PCIE_GPIO_WAKE,
+	EP_PCIE_GPIO_CLKREQ,
+	EP_PCIE_GPIO_MDM2AP,
+	EP_PCIE_MAX_GPIO,
+};
+
+struct ep_pcie_gpio_info_t {
+	char  *name;
+	u32   num;
+	bool  out;
+	u32   on;
+	u32   init;
+};
+
+struct ep_pcie_vreg_info_t {
+	struct regulator  *hdl;
+	char              *name;
+	u32           max_v;
+	u32           min_v;
+	u32           opt_mode;
+	bool          required;
+};
+
+struct ep_pcie_clk_info_t {
+	struct clk  *hdl;
+	char        *name;
+	u32         freq;
+	bool        required;
+};
+
+struct ep_pcie_reset_info_t {
+	struct reset_control *hdl;
+	char *name;
+	bool required;
+};
+
+struct ep_pcie_res_info_t {
+	char            *name;
+	struct resource *resource;
+	void __iomem    *base;
+};
+
+struct ep_pcie_irq_info_t {
+	char         *name;
+	u32          num;
+};
+
+/* phy info structure */
+struct ep_pcie_phy_info_t {
+	u32	offset;
+	u32	val;
+	u32	delay;
+	u32	direction;
+};
+
+/* pcie endpoint device structure */
+struct ep_pcie_dev_t {
+	struct platform_device       *pdev;
+	struct regulator             *gdsc;
+	struct ep_pcie_vreg_info_t   vreg[EP_PCIE_MAX_VREG];
+	struct ep_pcie_gpio_info_t   gpio[EP_PCIE_MAX_GPIO];
+	struct ep_pcie_clk_info_t    clk[EP_PCIE_MAX_CLK];
+	struct ep_pcie_clk_info_t    pipeclk[EP_PCIE_MAX_PIPE_CLK];
+	struct ep_pcie_reset_info_t  reset[EP_PCIE_MAX_RESET];
+	struct ep_pcie_irq_info_t    irq[EP_PCIE_MAX_IRQ];
+	struct ep_pcie_res_info_t    res[EP_PCIE_MAX_RES];
+
+	void __iomem                 *parf;
+	void __iomem                 *phy;
+	void __iomem                 *mmio;
+	void __iomem                 *msi;
+	void __iomem                 *dm_core;
+	void __iomem                 *elbi;
+
+	struct msm_bus_scale_pdata   *bus_scale_table;
+	u32                          bus_client;
+	u32                          link_speed;
+	bool                         active_config;
+	bool                         aggregated_irq;
+	bool                         mhi_a7_irq;
+	u32                          dbi_base_reg;
+	u32                          slv_space_reg;
+	u32                          phy_status_reg;
+	u32                          phy_init_len;
+	struct ep_pcie_phy_info_t    *phy_init;
+	bool                         perst_enum;
+
+	u32                          rev;
+	u32                          phy_rev;
+	void                         *ipc_log_sel;
+	void                         *ipc_log_ful;
+	void                         *ipc_log_dump;
+	struct mutex                 setup_mtx;
+	struct mutex                 ext_mtx;
+	spinlock_t                   ext_lock;
+	unsigned long                ext_save_flags;
+
+	spinlock_t                   isr_lock;
+	unsigned long                isr_save_flags;
+	ulong                        linkdown_counter;
+	ulong                        linkup_counter;
+	ulong                        bme_counter;
+	ulong                        pm_to_counter;
+	ulong                        d0_counter;
+	ulong                        d3_counter;
+	ulong                        perst_ast_counter;
+	ulong                        perst_deast_counter;
+	ulong                        wake_counter;
+	ulong                        msi_counter;
+	ulong                        global_irq_counter;
+
+	bool                         dump_conf;
+
+	bool                         enumerated;
+	enum ep_pcie_link_status     link_status;
+	bool                         perst_deast;
+	bool                         power_on;
+	bool                         suspending;
+	bool                         l23_ready;
+	bool                         l1ss_enabled;
+	struct ep_pcie_msi_config    msi_cfg;
+
+	struct ep_pcie_register_event *event_reg;
+	struct work_struct	     handle_perst_work;
+	struct work_struct           handle_bme_work;
+};
+
+extern struct ep_pcie_dev_t ep_pcie_dev;
+extern struct ep_pcie_hw hw_drv;
+
+static inline void ep_pcie_write_mask(void __iomem *addr,
+				u32 clear_mask, u32 set_mask)
+{
+	u32 val;
+
+	val = (readl_relaxed(addr) & ~clear_mask) | set_mask;
+	writel_relaxed(val, addr);
+	/* ensure register write goes through before next regiser operation */
+	wmb();
+}
+
+static inline void ep_pcie_write_reg(void __iomem *base, u32 offset, u32 value)
+{
+	writel_relaxed(value, base + offset);
+	/* ensure register write goes through before next regiser operation */
+	wmb();
+}
+
+static inline void ep_pcie_write_reg_field(void __iomem *base, u32 offset,
+	const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = readl_relaxed(base + offset);
+
+	tmp &= ~mask; /* clear written bits */
+	val = tmp | (val << shift);
+	writel_relaxed(val, base + offset);
+	/* ensure register write goes through before next regiser operation */
+	wmb();
+}
+
+extern int ep_pcie_core_register_event(struct ep_pcie_register_event *reg);
+extern int ep_pcie_get_debug_mask(void);
+extern void ep_pcie_phy_init(struct ep_pcie_dev_t *dev);
+extern bool ep_pcie_phy_is_ready(struct ep_pcie_dev_t *dev);
+extern void ep_pcie_reg_dump(struct ep_pcie_dev_t *dev, u32 sel, bool linkdown);
+extern void ep_pcie_debugfs_init(struct ep_pcie_dev_t *ep_dev);
+extern void ep_pcie_debugfs_exit(void);
+
+#endif
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
new file mode 100644
index 0000000..88c03fc
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c
@@ -0,0 +1,2554 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * MSM PCIe endpoint core driver.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/of_gpio.h>
+#include <linux/clk/qcom.h>
+#include <linux/reset.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "ep_pcie_com.h"
+
+/* debug mask sys interface */
+static int ep_pcie_debug_mask;
+static int ep_pcie_debug_keep_resource;
+static u32 ep_pcie_bar0_address;
+module_param_named(debug_mask, ep_pcie_debug_mask,
+			int, 0664);
+module_param_named(debug_keep_resource, ep_pcie_debug_keep_resource,
+			int, 0664);
+module_param_named(bar0_address, ep_pcie_bar0_address,
+			int, 0664);
+
+struct ep_pcie_dev_t ep_pcie_dev = {0};
+
+static struct ep_pcie_vreg_info_t ep_pcie_vreg_info[EP_PCIE_MAX_VREG] = {
+	{NULL, "vreg-1.8", 1800000, 1800000, 14000, true},
+	{NULL, "vreg-0.9", 1000000, 1000000, 40000, true}
+};
+
+static struct ep_pcie_gpio_info_t ep_pcie_gpio_info[EP_PCIE_MAX_GPIO] = {
+	{"perst-gpio",      0, 0, 0, 1},
+	{"wake-gpio",       0, 1, 0, 1},
+	{"clkreq-gpio",     0, 1, 0, 0},
+	{"mdm2apstatus-gpio",    0, 1, 1, 0}
+};
+
+static struct ep_pcie_clk_info_t
+	ep_pcie_clk_info[EP_PCIE_MAX_CLK] = {
+	{NULL, "pcie_0_cfg_ahb_clk", 0, true},
+	{NULL, "pcie_0_mstr_axi_clk", 0, true},
+	{NULL, "pcie_0_slv_axi_clk", 0, true},
+	{NULL, "pcie_0_aux_clk", 1000000, true},
+	{NULL, "pcie_0_ldo", 0, true},
+};
+
+static struct ep_pcie_clk_info_t
+	ep_pcie_pipe_clk_info[EP_PCIE_MAX_PIPE_CLK] = {
+	{NULL, "pcie_0_pipe_clk", 62500000, true}
+};
+
+static struct ep_pcie_reset_info_t
+	ep_pcie_reset_info[EP_PCIE_MAX_RESET] = {
+	{NULL, "pcie_0_core_reset", false},
+	{NULL, "pcie_0_phy_reset", false},
+};
+
+static const struct ep_pcie_res_info_t ep_pcie_res_info[EP_PCIE_MAX_RES] = {
+	{"parf",	0, 0},
+	{"phy",		0, 0},
+	{"mmio",	0, 0},
+	{"msi",		0, 0},
+	{"dm_core",	0, 0},
+	{"elbi",	0, 0}
+};
+
+static const struct ep_pcie_irq_info_t ep_pcie_irq_info[EP_PCIE_MAX_IRQ] = {
+	{"int_pm_turnoff",	0},
+	{"int_dstate_change",		0},
+	{"int_l1sub_timeout",	0},
+	{"int_link_up",	0},
+	{"int_link_down",	0},
+	{"int_bridge_flush_n",	0},
+	{"int_bme",	0},
+	{"int_global",	0}
+};
+
+int ep_pcie_get_debug_mask(void)
+{
+	return ep_pcie_debug_mask;
+}
+
+static bool ep_pcie_confirm_linkup(struct ep_pcie_dev_t *dev,
+				bool check_sw_stts)
+{
+	u32 val;
+
+	if (check_sw_stts && (dev->link_status != EP_PCIE_LINK_ENABLED)) {
+		EP_PCIE_DBG(dev, "PCIe V%d: The link is not enabled.\n",
+			dev->rev);
+		return false;
+	}
+
+	val = readl_relaxed(dev->dm_core);
+	EP_PCIE_DBG(dev, "PCIe V%d: device ID and vender ID are 0x%x.\n",
+		dev->rev, val);
+	if (val == EP_PCIE_LINK_DOWN) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: The link is not really up; device ID and vender ID are 0x%x.\n",
+			dev->rev, val);
+		return false;
+	}
+
+	return true;
+}
+
+static int ep_pcie_gpio_init(struct ep_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct ep_pcie_gpio_info_t *info;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = 0; i < EP_PCIE_MAX_GPIO; i++) {
+		info = &dev->gpio[i];
+
+		if (!info->num) {
+			if (i == EP_PCIE_GPIO_MDM2AP) {
+				EP_PCIE_DBG(dev,
+					"PCIe V%d: gpio %s does not exist.\n",
+					dev->rev, info->name);
+				continue;
+			} else {
+				EP_PCIE_ERR(dev,
+					"PCIe V%d:  the number of gpio %s is invalid\n",
+					dev->rev, info->name);
+				rc = -EINVAL;
+				break;
+			}
+		}
+
+		rc = gpio_request(info->num, info->name);
+		if (rc) {
+			EP_PCIE_ERR(dev, "PCIe V%d:  can't get gpio %s; %d\n",
+				dev->rev, info->name, rc);
+			break;
+		}
+
+		if (info->out)
+			rc = gpio_direction_output(info->num, info->init);
+		else
+			rc = gpio_direction_input(info->num);
+		if (rc) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d:  can't set direction for GPIO %s:%d\n",
+				dev->rev, info->name, rc);
+			gpio_free(info->num);
+			break;
+		}
+	}
+
+	if (rc)
+		while (i--)
+			gpio_free(dev->gpio[i].num);
+
+	return rc;
+}
+
+static void ep_pcie_gpio_deinit(struct ep_pcie_dev_t *dev)
+{
+	int i;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = 0; i < EP_PCIE_MAX_GPIO; i++)
+		gpio_free(dev->gpio[i].num);
+}
+
+static int ep_pcie_vreg_init(struct ep_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct regulator *vreg;
+	struct ep_pcie_vreg_info_t *info;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = 0; i < EP_PCIE_MAX_VREG; i++) {
+		info = &dev->vreg[i];
+		vreg = info->hdl;
+
+		if (!vreg) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d:  handle of Vreg %s is NULL\n",
+				dev->rev, info->name);
+			rc = -EINVAL;
+			break;
+		}
+
+		EP_PCIE_DBG(dev, "PCIe V%d: Vreg %s is being enabled\n",
+			dev->rev, info->name);
+		if (info->max_v) {
+			rc = regulator_set_voltage(vreg,
+						   info->min_v, info->max_v);
+			if (rc) {
+				EP_PCIE_ERR(dev,
+					"PCIe V%d:  can't set voltage for %s: %d\n",
+					dev->rev, info->name, rc);
+				break;
+			}
+		}
+
+		if (info->opt_mode) {
+			rc = regulator_set_load(vreg, info->opt_mode);
+			if (rc < 0) {
+				EP_PCIE_ERR(dev,
+					"PCIe V%d:  can't set mode for %s: %d\n",
+					dev->rev, info->name, rc);
+				break;
+			}
+		}
+
+		rc = regulator_enable(vreg);
+		if (rc) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d:  can't enable regulator %s: %d\n",
+				dev->rev, info->name, rc);
+			break;
+		}
+	}
+
+	if (rc)
+		while (i--) {
+			struct regulator *hdl = dev->vreg[i].hdl;
+
+			if (hdl)
+				regulator_disable(hdl);
+		}
+
+	return rc;
+}
+
+static void ep_pcie_vreg_deinit(struct ep_pcie_dev_t *dev)
+{
+	int i;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = EP_PCIE_MAX_VREG - 1; i >= 0; i--) {
+		if (dev->vreg[i].hdl) {
+			EP_PCIE_DBG(dev, "Vreg %s is being disabled\n",
+				dev->vreg[i].name);
+			regulator_disable(dev->vreg[i].hdl);
+		}
+	}
+}
+
+static int ep_pcie_clk_init(struct ep_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct ep_pcie_clk_info_t *info;
+	struct ep_pcie_reset_info_t *reset_info;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	rc = regulator_enable(dev->gdsc);
+
+	if (rc) {
+		EP_PCIE_ERR(dev, "PCIe V%d: fail to enable GDSC for %s\n",
+			dev->rev, dev->pdev->name);
+		return rc;
+	}
+
+	if (dev->bus_client) {
+		rc = msm_bus_scale_client_update_request(dev->bus_client, 1);
+		if (rc) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: fail to set bus bandwidth:%d.\n",
+				dev->rev, rc);
+			return rc;
+		}
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: set bus bandwidth.\n",
+			dev->rev);
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_CLK; i++) {
+		info = &dev->clk[i];
+
+		if (!info->hdl) {
+			EP_PCIE_DBG(dev,
+				"PCIe V%d:  handle of Clock %s is NULL\n",
+				dev->rev, info->name);
+			continue;
+		}
+
+		if (info->freq) {
+			rc = clk_set_rate(info->hdl, info->freq);
+			if (rc) {
+				EP_PCIE_ERR(dev,
+					"PCIe V%d: can't set rate for clk %s: %d.\n",
+					dev->rev, info->name, rc);
+				break;
+			}
+			EP_PCIE_DBG(dev,
+				"PCIe V%d: set rate for clk %s.\n",
+				dev->rev, info->name);
+		}
+
+		rc = clk_prepare_enable(info->hdl);
+
+		if (rc)
+			EP_PCIE_ERR(dev, "PCIe V%d:  failed to enable clk %s\n",
+				dev->rev, info->name);
+		else
+			EP_PCIE_DBG(dev, "PCIe V%d:  enable clk %s.\n",
+				dev->rev, info->name);
+	}
+
+	if (rc) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: disable clocks for error handling.\n",
+			dev->rev);
+		while (i--) {
+			struct clk *hdl = dev->clk[i].hdl;
+
+			if (hdl)
+				clk_disable_unprepare(hdl);
+		}
+
+		regulator_disable(dev->gdsc);
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_RESET; i++) {
+		reset_info = &dev->reset[i];
+		if (reset_info->hdl) {
+			rc = reset_control_assert(reset_info->hdl);
+			if (rc)
+				EP_PCIE_ERR(dev,
+					"PCIe V%d: failed to assert reset for %s.\n",
+					dev->rev, reset_info->name);
+			else
+				EP_PCIE_DBG(dev,
+					"PCIe V%d: successfully asserted reset for %s.\n",
+					dev->rev, reset_info->name);
+
+			/* add a 1ms delay to ensure the reset is asserted */
+			usleep_range(1000, 1005);
+
+			rc = reset_control_deassert(reset_info->hdl);
+			if (rc)
+				EP_PCIE_ERR(dev,
+					"PCIe V%d: failed to deassert reset for %s.\n",
+					dev->rev, reset_info->name);
+			else
+				EP_PCIE_DBG(dev,
+					"PCIe V%d: successfully deasserted reset for %s.\n",
+					dev->rev, reset_info->name);
+		}
+	}
+
+	return rc;
+}
+
+static void ep_pcie_clk_deinit(struct ep_pcie_dev_t *dev)
+{
+	int i;
+	int rc;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = EP_PCIE_MAX_CLK - 1; i >= 0; i--)
+		if (dev->clk[i].hdl)
+			clk_disable_unprepare(dev->clk[i].hdl);
+
+	if (dev->bus_client) {
+		rc = msm_bus_scale_client_update_request(dev->bus_client, 0);
+		if (rc)
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: fail to relinquish bus bandwidth:%d.\n",
+				dev->rev, rc);
+		else
+			EP_PCIE_DBG(dev,
+				"PCIe V%d: relinquish bus bandwidth.\n",
+				dev->rev);
+	}
+
+	regulator_disable(dev->gdsc);
+}
+
+static int ep_pcie_pipe_clk_init(struct ep_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct ep_pcie_clk_info_t *info;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = 0; i < EP_PCIE_MAX_PIPE_CLK; i++) {
+		info = &dev->pipeclk[i];
+
+		if (!info->hdl) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d:  handle of Pipe Clock %s is NULL\n",
+				dev->rev, info->name);
+			rc = -EINVAL;
+			break;
+		}
+
+		if (info->freq) {
+			rc = clk_set_rate(info->hdl, info->freq);
+			if (rc) {
+				EP_PCIE_ERR(dev,
+					"PCIe V%d: can't set rate for clk %s: %d.\n",
+					dev->rev, info->name, rc);
+				break;
+			}
+			EP_PCIE_DBG(dev,
+				"PCIe V%d: set rate for clk %s\n",
+				dev->rev, info->name);
+		}
+
+		rc = clk_prepare_enable(info->hdl);
+
+		if (rc)
+			EP_PCIE_ERR(dev, "PCIe V%d: failed to enable clk %s.\n",
+				dev->rev, info->name);
+		else
+			EP_PCIE_DBG(dev, "PCIe V%d: enabled pipe clk %s.\n",
+				dev->rev, info->name);
+	}
+
+	if (rc) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: disable pipe clocks for error handling.\n",
+			dev->rev);
+		while (i--)
+			if (dev->pipeclk[i].hdl)
+				clk_disable_unprepare(dev->pipeclk[i].hdl);
+	}
+
+	return rc;
+}
+
+static void ep_pcie_pipe_clk_deinit(struct ep_pcie_dev_t *dev)
+{
+	int i;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	for (i = 0; i < EP_PCIE_MAX_PIPE_CLK; i++)
+		if (dev->pipeclk[i].hdl)
+			clk_disable_unprepare(
+				dev->pipeclk[i].hdl);
+}
+
+static void ep_pcie_bar_init(struct ep_pcie_dev_t *dev)
+{
+	struct resource *res = dev->res[EP_PCIE_RES_MMIO].resource;
+	u32 mask = res->end - res->start;
+	u32 properties = 0x4;
+
+	EP_PCIE_DBG(dev, "PCIe V%d: BAR mask to program is 0x%x\n",
+			dev->rev, mask);
+
+	/* Configure BAR mask via CS2 */
+	ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_CS2_ENABLE, 0, BIT(0));
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0, mask);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x4, 0);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x8, mask);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0xc, 0);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x10, 0);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x14, 0);
+	ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_CS2_ENABLE, BIT(0), 0);
+
+	/* Configure BAR properties via CS */
+	ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0, BIT(0));
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0, properties);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x8, properties);
+	ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), 0);
+}
+
+static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured)
+{
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	/* enable debug IRQ */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_DEBUG_INT_EN,
+			0, BIT(3) | BIT(2) | BIT(1));
+
+	if (!configured) {
+		/* Configure PCIe to endpoint mode */
+		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);
+
+		/* Configure PCIe core to support 1GB aperture */
+		if (dev->slv_space_reg)
+			ep_pcie_write_reg(dev->parf, dev->slv_space_reg,
+				0x40000000);
+		else
+			ep_pcie_write_reg(dev->parf,
+				PCIE20_PARF_SLV_ADDR_SPACE_SIZE, 0x40000000);
+
+		/* Configure link speed */
+		ep_pcie_write_mask(dev->dm_core +
+				PCIE20_LINK_CONTROL2_LINK_STATUS2,
+				0xf, dev->link_speed);
+	}
+
+	/* Read halts write */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_AXI_MSTR_RD_HALT_NO_WRITES,
+			0, BIT(0));
+
+	/* Write after write halt */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
+			0, BIT(31));
+
+	/* Q2A flush disable */
+	writel_relaxed(0, dev->parf + PCIE20_PARF_Q2A_FLUSH);
+
+	/* Disable the DBI Wakeup */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, BIT(11), 0);
+
+	/* Disable the debouncers */
+	ep_pcie_write_reg(dev->parf, PCIE20_PARF_DB_CTRL, 0x73);
+
+	/* Disable core clock CGC */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, 0, BIT(6));
+
+	/* Set AUX power to be on */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, 0, BIT(4));
+
+	/* Request to exit from L1SS for MSI and LTR MSG */
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_CFG_BITS, 0, BIT(1));
+
+	EP_PCIE_DBG(dev,
+		"Initial: CLASS_CODE_REVISION_ID:0x%x; HDR_TYPE:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_CLASS_CODE_REVISION_ID),
+		readl_relaxed(dev->dm_core + PCIE20_BIST_HDR_TYPE));
+
+	if (!configured) {
+		/* Enable CS for RO(CS) register writes */
+		ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0,
+			BIT(0));
+
+		/* Set class code and revision ID */
+		ep_pcie_write_reg(dev->dm_core, PCIE20_CLASS_CODE_REVISION_ID,
+			0xff000000);
+
+		/* Set header type */
+		ep_pcie_write_reg(dev->dm_core, PCIE20_BIST_HDR_TYPE, 0x10);
+
+		/* Set Subsystem ID and Subsystem Vendor ID */
+		ep_pcie_write_reg(dev->dm_core, PCIE20_SUBSYSTEM, 0xa01f17cb);
+
+		/* Set the PMC Register - to support PME in D0/D3hot/D3cold */
+		ep_pcie_write_mask(dev->dm_core + PCIE20_CAP_ID_NXT_PTR, 0,
+						BIT(31)|BIT(30)|BIT(27));
+
+		/* Set the Endpoint L0s Acceptable Latency to 1us (max) */
+		ep_pcie_write_reg_field(dev->dm_core,
+			PCIE20_DEVICE_CAPABILITIES,
+			PCIE20_MASK_EP_L0S_ACCPT_LATENCY, 0x7);
+
+		/* Set the Endpoint L1 Acceptable Latency to 2 us (max) */
+		ep_pcie_write_reg_field(dev->dm_core,
+			PCIE20_DEVICE_CAPABILITIES,
+			PCIE20_MASK_EP_L1_ACCPT_LATENCY, 0x7);
+
+		/* Set the L0s Exit Latency to 2us-4us = 0x6 */
+		ep_pcie_write_reg_field(dev->dm_core, PCIE20_LINK_CAPABILITIES,
+			PCIE20_MASK_L1_EXIT_LATENCY, 0x6);
+
+		/* Set the L1 Exit Latency to be 32us-64 us = 0x6 */
+		ep_pcie_write_reg_field(dev->dm_core, PCIE20_LINK_CAPABILITIES,
+			PCIE20_MASK_L0S_EXIT_LATENCY, 0x6);
+
+		/* L1ss is supported */
+		ep_pcie_write_mask(dev->dm_core + PCIE20_L1SUB_CAPABILITY, 0,
+			0x1f);
+
+		/* Enable Clock Power Management */
+		ep_pcie_write_reg_field(dev->dm_core, PCIE20_LINK_CAPABILITIES,
+			PCIE20_MASK_CLOCK_POWER_MAN, 0x1);
+
+		/* Disable CS for RO(CS) register writes */
+		ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0),
+			0);
+
+		/* Set FTS value to match the PHY setting */
+		ep_pcie_write_reg_field(dev->dm_core,
+			PCIE20_ACK_F_ASPM_CTRL_REG,
+			PCIE20_MASK_ACK_N_FTS, 0x80);
+
+		EP_PCIE_DBG(dev,
+			"After program: CLASS_CODE_REVISION_ID:0x%x; HDR_TYPE:0x%x; L1SUB_CAPABILITY:0x%x; PARF_SYS_CTRL:0x%x\n",
+			readl_relaxed(dev->dm_core +
+				PCIE20_CLASS_CODE_REVISION_ID),
+			readl_relaxed(dev->dm_core + PCIE20_BIST_HDR_TYPE),
+			readl_relaxed(dev->dm_core + PCIE20_L1SUB_CAPABILITY),
+			readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL));
+
+		/* Configure BARs */
+		ep_pcie_bar_init(dev);
+
+		ep_pcie_write_reg(dev->mmio, PCIE20_MHICFG, 0x02800880);
+		ep_pcie_write_reg(dev->mmio, PCIE20_BHI_EXECENV, 0x2);
+	}
+
+	/* Configure IRQ events */
+	if (dev->aggregated_irq) {
+		ep_pcie_write_reg(dev->parf, PCIE20_PARF_INT_ALL_MASK, 0);
+		ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK, 0,
+			BIT(EP_PCIE_INT_EVT_LINK_DOWN) |
+			BIT(EP_PCIE_INT_EVT_BME) |
+			BIT(EP_PCIE_INT_EVT_PM_TURNOFF) |
+			BIT(EP_PCIE_INT_EVT_DSTATE_CHANGE) |
+			BIT(EP_PCIE_INT_EVT_LINK_UP));
+		if (!dev->mhi_a7_irq)
+			ep_pcie_write_mask(dev->parf +
+				PCIE20_PARF_INT_ALL_MASK, 0,
+				BIT(EP_PCIE_INT_EVT_MHI_A7));
+
+		EP_PCIE_DBG(dev, "PCIe V%d: PCIE20_PARF_INT_ALL_MASK:0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK));
+	}
+
+	if (dev->active_config) {
+		ep_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x14);
+
+		EP_PCIE_DBG2(dev, "PCIe V%d: Enable L1.\n", dev->rev);
+		ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, BIT(5), 0);
+	}
+}
+
+static void ep_pcie_config_inbound_iatu(struct ep_pcie_dev_t *dev)
+{
+	struct resource *mmio = dev->res[EP_PCIE_RES_MMIO].resource;
+	u32 lower, limit, bar;
+
+	lower = mmio->start;
+	limit = mmio->end;
+	bar = readl_relaxed(dev->dm_core + PCIE20_BAR0);
+
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: BAR0 is 0x%x; MMIO[0x%x-0x%x]\n",
+		dev->rev, bar, lower, limit);
+
+	ep_pcie_write_reg(dev->parf, PCIE20_PARF_MHI_BASE_ADDR_LOWER, lower);
+	ep_pcie_write_reg(dev->parf, PCIE20_PARF_MHI_BASE_ADDR_UPPER, 0x0);
+
+	/* program inbound address translation using region 0 */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_VIEWPORT, 0x80000000);
+	/* set region to mem type */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_CTRL1, 0x0);
+	/* setup target address registers */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LTAR, lower);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_UTAR, 0x0);
+	/* use BAR match mode for BAR0 and enable region 0 */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_CTRL2, 0xc0000000);
+
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_VIEWPORT:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL1:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LTAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_UTAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL2:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2));
+}
+
+static void ep_pcie_config_outbound_iatu_entry(struct ep_pcie_dev_t *dev,
+					u32 region, u32 lower, u32 upper,
+					u32 limit, u32 tgt_lower, u32 tgt_upper)
+{
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: region:%d; lower:0x%x; limit:0x%x; target_lower:0x%x; target_upper:0x%x\n",
+		dev->rev, region, lower, limit, tgt_lower, tgt_upper);
+
+	/* program outbound address translation using an input region */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_VIEWPORT, region);
+	/* set region to mem type */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_CTRL1, 0x0);
+	/* setup source address registers */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LBAR, lower);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_UBAR, upper);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LAR, limit);
+	/* setup target address registers */
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LTAR, tgt_lower);
+	ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_UTAR, tgt_upper);
+	/* use DMA bypass mode and enable the region */
+	ep_pcie_write_mask(dev->dm_core + PCIE20_PLR_IATU_CTRL2, 0,
+				BIT(31) | BIT(27));
+
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_VIEWPORT:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL1:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LBAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LBAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_UBAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UBAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LTAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_UTAR:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR));
+	EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL2:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2));
+}
+
+static void ep_pcie_notify_event(struct ep_pcie_dev_t *dev,
+					enum ep_pcie_event event)
+{
+	if (dev->event_reg && dev->event_reg->callback &&
+		(dev->event_reg->events & event)) {
+		struct ep_pcie_notify *notify =	&dev->event_reg->notify;
+
+		notify->event = event;
+		notify->user = dev->event_reg->user;
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: Callback client for event %d.\n",
+			dev->rev, event);
+		dev->event_reg->callback(notify);
+	} else {
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: Client does not register for event %d.\n",
+			dev->rev, event);
+	}
+}
+
+static int ep_pcie_get_resources(struct ep_pcie_dev_t *dev,
+					struct platform_device *pdev)
+{
+	int i, len, cnt, ret = 0, size = 0;
+	struct ep_pcie_vreg_info_t *vreg_info;
+	struct ep_pcie_gpio_info_t *gpio_info;
+	struct ep_pcie_clk_info_t  *clk_info;
+	struct ep_pcie_reset_info_t *reset_info;
+	struct resource *res;
+	struct ep_pcie_res_info_t *res_info;
+	struct ep_pcie_irq_info_t *irq_info;
+	char prop_name[MAX_PROP_SIZE];
+	const __be32 *prop;
+	u32 *clkfreq = NULL;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	of_get_property(pdev->dev.of_node, "qcom,phy-init", &size);
+	if (size) {
+		dev->phy_init = (struct ep_pcie_phy_info_t *)
+			devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+
+		if (dev->phy_init) {
+			dev->phy_init_len =
+				size / sizeof(*dev->phy_init);
+			EP_PCIE_DBG(dev,
+					"PCIe V%d: phy init length is 0x%x.\n",
+					dev->rev, dev->phy_init_len);
+
+			of_property_read_u32_array(pdev->dev.of_node,
+				"qcom,phy-init",
+				(unsigned int *)dev->phy_init,
+				size / sizeof(dev->phy_init->offset));
+		} else {
+			EP_PCIE_ERR(dev,
+					"PCIe V%d: Could not allocate memory for phy init sequence.\n",
+					dev->rev);
+			return -ENOMEM;
+		}
+	} else {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PHY V%d: phy init sequence is not present in DT.\n",
+			dev->rev, dev->phy_rev);
+	}
+
+	cnt = of_property_count_strings((&pdev->dev)->of_node,
+			"clock-names");
+	if (cnt > 0) {
+		size_t size = cnt * sizeof(*clkfreq);
+
+		clkfreq = kzalloc(size,	GFP_KERNEL);
+		if (!clkfreq) {
+			EP_PCIE_ERR(dev, "PCIe V%d: memory alloc failed\n",
+					dev->rev);
+			return -ENOMEM;
+		}
+		ret = of_property_read_u32_array(
+			(&pdev->dev)->of_node,
+			"max-clock-frequency-hz", clkfreq, cnt);
+		if (ret) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: invalid max-clock-frequency-hz property:%d\n",
+				dev->rev, ret);
+			goto out;
+		}
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_VREG; i++) {
+		vreg_info = &dev->vreg[i];
+		vreg_info->hdl =
+			devm_regulator_get(&pdev->dev, vreg_info->name);
+
+		if (PTR_ERR(vreg_info->hdl) == -EPROBE_DEFER) {
+			EP_PCIE_DBG(dev, "EPROBE_DEFER for VReg:%s\n",
+				vreg_info->name);
+			ret = PTR_ERR(vreg_info->hdl);
+			goto out;
+		}
+
+		if (IS_ERR(vreg_info->hdl)) {
+			if (vreg_info->required) {
+				EP_PCIE_ERR(dev, "Vreg %s doesn't exist\n",
+					vreg_info->name);
+				ret = PTR_ERR(vreg_info->hdl);
+				goto out;
+			} else {
+				EP_PCIE_DBG(dev,
+					"Optional Vreg %s doesn't exist\n",
+					vreg_info->name);
+				vreg_info->hdl = NULL;
+			}
+		} else {
+			snprintf(prop_name, MAX_PROP_SIZE,
+				"qcom,%s-voltage-level", vreg_info->name);
+			prop = of_get_property((&pdev->dev)->of_node,
+						prop_name, &len);
+			if (!prop || (len != (3 * sizeof(__be32)))) {
+				EP_PCIE_DBG(dev, "%s %s property\n",
+					prop ? "invalid format" :
+					"no", prop_name);
+			} else {
+				vreg_info->max_v = be32_to_cpup(&prop[0]);
+				vreg_info->min_v = be32_to_cpup(&prop[1]);
+				vreg_info->opt_mode =
+					be32_to_cpup(&prop[2]);
+			}
+		}
+	}
+
+	dev->gdsc = devm_regulator_get(&pdev->dev, "gdsc-vdd");
+
+	if (IS_ERR(dev->gdsc)) {
+		EP_PCIE_ERR(dev, "PCIe V%d:  Failed to get %s GDSC:%ld\n",
+			dev->rev, dev->pdev->name, PTR_ERR(dev->gdsc));
+		if (PTR_ERR(dev->gdsc) == -EPROBE_DEFER)
+			EP_PCIE_DBG(dev, "PCIe V%d: EPROBE_DEFER for %s GDSC\n",
+			dev->rev, dev->pdev->name);
+		ret = PTR_ERR(dev->gdsc);
+		goto out;
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_GPIO; i++) {
+		gpio_info = &dev->gpio[i];
+		ret = of_get_named_gpio((&pdev->dev)->of_node,
+					gpio_info->name, 0);
+		if (ret >= 0) {
+			gpio_info->num = ret;
+			ret = 0;
+			EP_PCIE_DBG(dev, "GPIO num for %s is %d\n",
+				gpio_info->name, gpio_info->num);
+		} else {
+			EP_PCIE_DBG(dev,
+				"GPIO %s is not supported in this configuration.\n",
+				gpio_info->name);
+			ret = 0;
+		}
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_CLK; i++) {
+		clk_info = &dev->clk[i];
+
+		clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name);
+
+		if (IS_ERR(clk_info->hdl)) {
+			if (clk_info->required) {
+				EP_PCIE_ERR(dev,
+					"Clock %s isn't available:%ld\n",
+					clk_info->name, PTR_ERR(clk_info->hdl));
+				ret = PTR_ERR(clk_info->hdl);
+				goto out;
+			} else {
+				EP_PCIE_DBG(dev, "Ignoring Clock %s\n",
+					clk_info->name);
+				clk_info->hdl = NULL;
+			}
+		} else {
+			if (clkfreq != NULL) {
+				clk_info->freq = clkfreq[i +
+					EP_PCIE_MAX_PIPE_CLK];
+				EP_PCIE_DBG(dev, "Freq of Clock %s is:%d\n",
+					clk_info->name, clk_info->freq);
+			}
+		}
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_PIPE_CLK; i++) {
+		clk_info = &dev->pipeclk[i];
+
+		clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name);
+
+		if (IS_ERR(clk_info->hdl)) {
+			if (clk_info->required) {
+				EP_PCIE_ERR(dev,
+					"Clock %s isn't available:%ld\n",
+					clk_info->name, PTR_ERR(clk_info->hdl));
+				ret = PTR_ERR(clk_info->hdl);
+				goto out;
+			} else {
+				EP_PCIE_DBG(dev, "Ignoring Clock %s\n",
+					clk_info->name);
+				clk_info->hdl = NULL;
+			}
+		} else {
+			if (clkfreq != NULL) {
+				clk_info->freq = clkfreq[i];
+				EP_PCIE_DBG(dev, "Freq of Clock %s is:%d\n",
+					clk_info->name, clk_info->freq);
+			}
+		}
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_RESET; i++) {
+		reset_info = &dev->reset[i];
+
+		reset_info->hdl = devm_reset_control_get(&pdev->dev,
+						reset_info->name);
+
+		if (IS_ERR(reset_info->hdl)) {
+			if (reset_info->required) {
+				EP_PCIE_ERR(dev,
+					"Reset %s isn't available:%ld\n",
+					reset_info->name,
+					PTR_ERR(reset_info->hdl));
+
+				ret = PTR_ERR(reset_info->hdl);
+				reset_info->hdl = NULL;
+				goto out;
+			} else {
+				EP_PCIE_DBG(dev, "Ignoring Reset %s\n",
+					reset_info->name);
+				reset_info->hdl = NULL;
+			}
+		}
+	}
+
+	dev->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+	if (!dev->bus_scale_table) {
+		EP_PCIE_DBG(dev, "PCIe V%d: No bus scale table for %s\n",
+			dev->rev, dev->pdev->name);
+		dev->bus_client = 0;
+	} else {
+		dev->bus_client =
+			msm_bus_scale_register_client(dev->bus_scale_table);
+		if (!dev->bus_client) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: Failed to register bus client for %s\n",
+				dev->rev, dev->pdev->name);
+			msm_bus_cl_clear_pdata(dev->bus_scale_table);
+			ret = -ENODEV;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_RES; i++) {
+		res_info = &dev->res[i];
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							res_info->name);
+
+		if (!res) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: can't get resource for %s.\n",
+				dev->rev, res_info->name);
+			ret = -ENOMEM;
+			goto out;
+		} else {
+			EP_PCIE_DBG(dev, "start addr for %s is %pa.\n",
+				res_info->name,	&res->start);
+		}
+
+		res_info->base = devm_ioremap(&pdev->dev,
+						res->start, resource_size(res));
+		if (!res_info->base) {
+			EP_PCIE_ERR(dev, "PCIe V%d: can't remap %s.\n",
+				dev->rev, res_info->name);
+			ret = -ENOMEM;
+			goto out;
+		}
+		res_info->resource = res;
+	}
+
+	for (i = 0; i < EP_PCIE_MAX_IRQ; i++) {
+		irq_info = &dev->irq[i];
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							irq_info->name);
+
+		if (!res) {
+			EP_PCIE_DBG2(dev, "PCIe V%d: can't find IRQ # for %s\n",
+				dev->rev, irq_info->name);
+		} else {
+			irq_info->num = res->start;
+			EP_PCIE_DBG2(dev, "IRQ # for %s is %d.\n",
+				irq_info->name,	irq_info->num);
+		}
+	}
+
+	dev->parf = dev->res[EP_PCIE_RES_PARF].base;
+	dev->phy = dev->res[EP_PCIE_RES_PHY].base;
+	dev->mmio = dev->res[EP_PCIE_RES_MMIO].base;
+	dev->msi = dev->res[EP_PCIE_RES_MSI].base;
+	dev->dm_core = dev->res[EP_PCIE_RES_DM_CORE].base;
+	dev->elbi = dev->res[EP_PCIE_RES_ELBI].base;
+
+out:
+	kfree(clkfreq);
+	return ret;
+}
+
+static void ep_pcie_release_resources(struct ep_pcie_dev_t *dev)
+{
+	dev->parf = NULL;
+	dev->elbi = NULL;
+	dev->dm_core = NULL;
+	dev->phy = NULL;
+	dev->mmio = NULL;
+	dev->msi = NULL;
+
+	if (dev->bus_client) {
+		msm_bus_scale_unregister_client(dev->bus_client);
+		dev->bus_client = 0;
+	}
+}
+
+static void ep_pcie_enumeration_complete(struct ep_pcie_dev_t *dev)
+{
+	dev->enumerated = true;
+	dev->link_status = EP_PCIE_LINK_ENABLED;
+
+	if (dev->gpio[EP_PCIE_GPIO_MDM2AP].num) {
+		/* assert MDM2AP Status GPIO */
+		EP_PCIE_DBG2(dev, "PCIe V%d: assert MDM2AP Status.\n",
+				dev->rev);
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: MDM2APStatus GPIO initial:%d.\n",
+			dev->rev,
+			gpio_get_value(
+			dev->gpio[EP_PCIE_GPIO_MDM2AP].num));
+		gpio_set_value(dev->gpio[EP_PCIE_GPIO_MDM2AP].num,
+			dev->gpio[EP_PCIE_GPIO_MDM2AP].on);
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: MDM2APStatus GPIO after assertion:%d.\n",
+			dev->rev,
+			gpio_get_value(
+			dev->gpio[EP_PCIE_GPIO_MDM2AP].num));
+	}
+
+	hw_drv.device_id = readl_relaxed(dev->dm_core);
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: register driver for device 0x%x.\n",
+		ep_pcie_dev.rev, hw_drv.device_id);
+	ep_pcie_register_drv(&hw_drv);
+	ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP);
+}
+
+int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt)
+{
+	int ret = 0;
+	u32 val = 0;
+	u32 retries = 0;
+	u32 bme = 0;
+	bool ltssm_en = false;
+	struct ep_pcie_dev_t *dev = &ep_pcie_dev;
+
+	EP_PCIE_DBG(dev, "PCIe V%d: options input are 0x%x.\n", dev->rev, opt);
+
+	mutex_lock(&dev->setup_mtx);
+
+	if (dev->link_status == EP_PCIE_LINK_ENABLED) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: link is already enabled.\n",
+			dev->rev);
+		goto out;
+	}
+
+	if (dev->link_status == EP_PCIE_LINK_UP)
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: link is already up, let's proceed with the voting for the resources.\n",
+			dev->rev);
+
+	if (dev->power_on && (opt & EP_PCIE_OPT_POWER_ON)) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: request to turn on the power when link is already powered on.\n",
+			dev->rev);
+		goto out;
+	}
+
+	if (opt & EP_PCIE_OPT_POWER_ON) {
+		/* enable power */
+		ret = ep_pcie_vreg_init(dev);
+		if (ret) {
+			EP_PCIE_ERR(dev, "PCIe V%d: failed to enable Vreg\n",
+				dev->rev);
+			goto out;
+		}
+
+		/* enable clocks */
+		ret = ep_pcie_clk_init(dev);
+		if (ret) {
+			EP_PCIE_ERR(dev, "PCIe V%d: failed to enable clocks\n",
+				dev->rev);
+			goto clk_fail;
+		}
+
+		/* enable pipe clock */
+		ret = ep_pcie_pipe_clk_init(dev);
+		if (ret) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: failed to enable pipe clock\n",
+				dev->rev);
+			goto pipe_clk_fail;
+		}
+
+		dev->power_on = true;
+	}
+
+	if (!(opt & EP_PCIE_OPT_ENUM))
+		goto out;
+
+	/* check link status during initial bootup */
+	if (!dev->enumerated) {
+		val = readl_relaxed(dev->parf + PCIE20_PARF_PM_STTS);
+		val = val & PARF_XMLH_LINK_UP;
+		EP_PCIE_DBG(dev, "PCIe V%d: Link status is 0x%x.\n", dev->rev,
+				val);
+		if (val) {
+			EP_PCIE_INFO(dev,
+				"PCIe V%d: link initialized by bootloader for LE PCIe endpoint; skip link training in HLOS.\n",
+				dev->rev);
+			ep_pcie_core_init(dev, true);
+			dev->link_status = EP_PCIE_LINK_UP;
+			dev->l23_ready = false;
+			goto checkbme;
+		} else {
+			ltssm_en = readl_relaxed(dev->parf
+					+ PCIE20_PARF_LTSSM) & BIT(8);
+
+			if (ltssm_en) {
+				EP_PCIE_ERR(dev,
+					"PCIe V%d: link is not up when LTSSM has already enabled by bootloader.\n",
+					dev->rev);
+				ret = EP_PCIE_ERROR;
+				goto link_fail;
+			} else {
+				EP_PCIE_DBG(dev,
+					"PCIe V%d: Proceed with regular link training.\n",
+					dev->rev);
+			}
+		}
+	}
+
+	if (opt & EP_PCIE_OPT_AST_WAKE) {
+		/* assert PCIe WAKE# */
+		EP_PCIE_INFO(dev, "PCIe V%d: assert PCIe WAKE#.\n",
+			dev->rev);
+		EP_PCIE_DBG(dev, "PCIe V%d: WAKE GPIO initial:%d.\n",
+			dev->rev,
+			gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num));
+		gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+				1 - dev->gpio[EP_PCIE_GPIO_WAKE].on);
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: WAKE GPIO after deassertion:%d.\n",
+			dev->rev,
+			gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num));
+		gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+				dev->gpio[EP_PCIE_GPIO_WAKE].on);
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: WAKE GPIO after assertion:%d.\n",
+			dev->rev,
+			gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num));
+	}
+
+	/* wait for host side to deassert PERST */
+	retries = 0;
+	do {
+		if (gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num) == 1)
+			break;
+		retries++;
+		usleep_range(PERST_TIMEOUT_US_MIN, PERST_TIMEOUT_US_MAX);
+	} while (retries < PERST_CHECK_MAX_COUNT);
+
+	EP_PCIE_DBG(dev, "PCIe V%d: number of PERST retries:%d.\n",
+		dev->rev, retries);
+
+	if (retries == PERST_CHECK_MAX_COUNT) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: PERST is not de-asserted by host\n",
+			dev->rev);
+		ret = EP_PCIE_ERROR;
+		goto link_fail;
+	} else {
+		dev->perst_deast = true;
+		if (opt & EP_PCIE_OPT_AST_WAKE) {
+			/* deassert PCIe WAKE# */
+			EP_PCIE_DBG(dev,
+				"PCIe V%d: deassert PCIe WAKE# after PERST# is deasserted.\n",
+				dev->rev);
+			gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+				1 - dev->gpio[EP_PCIE_GPIO_WAKE].on);
+		}
+	}
+
+	/* init PCIe PHY */
+	ep_pcie_phy_init(dev);
+
+	EP_PCIE_DBG(dev, "PCIe V%d: waiting for phy ready...\n", dev->rev);
+	retries = 0;
+	do {
+		if (ep_pcie_phy_is_ready(dev))
+			break;
+		retries++;
+		if (retries % 100 == 0)
+			EP_PCIE_DBG(dev,
+				"PCIe V%d: current number of PHY retries:%d.\n",
+				dev->rev, retries);
+		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
+				REFCLK_STABILIZATION_DELAY_US_MAX);
+	} while (retries < PHY_READY_TIMEOUT_COUNT);
+
+	EP_PCIE_DBG(dev, "PCIe V%d: number of PHY retries:%d.\n",
+		dev->rev, retries);
+
+	if (retries == PHY_READY_TIMEOUT_COUNT) {
+		EP_PCIE_ERR(dev, "PCIe V%d: PCIe PHY  failed to come up!\n",
+			dev->rev);
+		ret = EP_PCIE_ERROR;
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_PHY), false);
+		goto link_fail;
+	} else {
+		EP_PCIE_INFO(dev, "PCIe V%d: PCIe  PHY is ready!\n", dev->rev);
+	}
+
+	ep_pcie_core_init(dev, false);
+	ep_pcie_config_inbound_iatu(dev);
+
+	/* enable link training */
+	if (dev->phy_rev >= 3)
+		ep_pcie_write_mask(dev->parf + PCIE20_PARF_LTSSM, 0, BIT(8));
+	else
+		ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
+
+	EP_PCIE_DBG(dev, "PCIe V%d: check if link is up\n", dev->rev);
+
+	/* Wait for up to 100ms for the link to come up */
+	retries = 0;
+	do {
+		usleep_range(LINK_UP_TIMEOUT_US_MIN, LINK_UP_TIMEOUT_US_MAX);
+		val =  readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS);
+		retries++;
+		if (retries % 100 == 0)
+			EP_PCIE_DBG(dev, "PCIe V%d: LTSSM_STATE:0x%x.\n",
+					dev->rev, (val >> 0xC) & 0x3f);
+	} while ((!(val & XMLH_LINK_UP) ||
+		!ep_pcie_confirm_linkup(dev, false))
+		&& (retries < LINK_UP_CHECK_MAX_COUNT));
+
+	if (retries == LINK_UP_CHECK_MAX_COUNT) {
+		EP_PCIE_ERR(dev, "PCIe V%d: link initialization failed\n",
+			dev->rev);
+		ret = EP_PCIE_ERROR;
+		goto link_fail;
+	} else {
+		dev->link_status = EP_PCIE_LINK_UP;
+		dev->l23_ready = false;
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: link is up after %d checkings (%d ms)\n",
+			dev->rev, retries,
+			LINK_UP_TIMEOUT_US_MIN * retries / 1000);
+		EP_PCIE_INFO(dev,
+			"PCIe V%d: link initialized for LE PCIe endpoint\n",
+			dev->rev);
+	}
+
+checkbme:
+	if (dev->active_config) {
+		ep_pcie_write_mask(dev->parf + PCIE20_PARF_SLV_ADDR_MSB_CTRL,
+					0, BIT(0));
+		ep_pcie_write_reg(dev->parf, PCIE20_PARF_SLV_ADDR_SPACE_SIZE_HI,
+					0x200);
+		ep_pcie_write_reg(dev->parf, PCIE20_PARF_SLV_ADDR_SPACE_SIZE,
+					0x0);
+		ep_pcie_write_reg(dev->parf, PCIE20_PARF_DBI_BASE_ADDR_HI,
+					0x100);
+		ep_pcie_write_reg(dev->parf, PCIE20_PARF_DBI_BASE_ADDR,
+					0x7FFFE000);
+	}
+
+	if (!(opt & EP_PCIE_OPT_ENUM_ASYNC)) {
+		/* Wait for up to 1000ms for BME to be set */
+		retries = 0;
+
+		bme = readl_relaxed(dev->dm_core +
+		PCIE20_COMMAND_STATUS) & BIT(2);
+		while (!bme && (retries < BME_CHECK_MAX_COUNT)) {
+			retries++;
+			usleep_range(BME_TIMEOUT_US_MIN, BME_TIMEOUT_US_MAX);
+			bme = readl_relaxed(dev->dm_core +
+				PCIE20_COMMAND_STATUS) & BIT(2);
+		}
+	} else {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: EP_PCIE_OPT_ENUM_ASYNC is true.\n",
+			dev->rev);
+		bme = readl_relaxed(dev->dm_core +
+			PCIE20_COMMAND_STATUS) & BIT(2);
+	}
+
+	if (bme) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PCIe link is up and BME is enabled after %d checkings (%d ms).\n",
+			dev->rev, retries,
+			BME_TIMEOUT_US_MIN * retries / 1000);
+		ep_pcie_enumeration_complete(dev);
+		/* expose BAR to user space to identify modem */
+		ep_pcie_bar0_address =
+			readl_relaxed(dev->dm_core + PCIE20_BAR0);
+	} else {
+		if (!(opt & EP_PCIE_OPT_ENUM_ASYNC))
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: PCIe link is up but BME is still disabled after max waiting time.\n",
+				dev->rev);
+		if (!ep_pcie_debug_keep_resource &&
+				!(opt&EP_PCIE_OPT_ENUM_ASYNC)) {
+			ret = EP_PCIE_ERROR;
+			dev->link_status = EP_PCIE_LINK_DISABLED;
+			goto link_fail;
+		}
+	}
+
+	dev->suspending = false;
+	goto out;
+
+link_fail:
+	dev->power_on = false;
+	if (!ep_pcie_debug_keep_resource)
+		ep_pcie_pipe_clk_deinit(dev);
+pipe_clk_fail:
+	if (!ep_pcie_debug_keep_resource)
+		ep_pcie_clk_deinit(dev);
+clk_fail:
+	if (!ep_pcie_debug_keep_resource)
+		ep_pcie_vreg_deinit(dev);
+	else
+		ret = 0;
+out:
+	mutex_unlock(&dev->setup_mtx);
+
+	return ret;
+}
+
+int ep_pcie_core_disable_endpoint(void)
+{
+	int rc = 0;
+	struct ep_pcie_dev_t *dev = &ep_pcie_dev;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	mutex_lock(&dev->setup_mtx);
+
+	if (!dev->power_on) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: the link is already power down.\n",
+			dev->rev);
+		goto out;
+	}
+
+	dev->link_status = EP_PCIE_LINK_DISABLED;
+	dev->power_on = false;
+
+	EP_PCIE_DBG(dev, "PCIe V%d: shut down the link.\n",
+		dev->rev);
+
+	ep_pcie_pipe_clk_deinit(dev);
+	ep_pcie_clk_deinit(dev);
+	ep_pcie_vreg_deinit(dev);
+out:
+	mutex_unlock(&dev->setup_mtx);
+	return rc;
+}
+
+int ep_pcie_core_mask_irq_event(enum ep_pcie_irq_event event,
+				bool enable)
+{
+	int rc = 0;
+	struct ep_pcie_dev_t *dev = &ep_pcie_dev;
+	unsigned long irqsave_flags;
+	u32 mask = 0;
+
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: Client askes to %s IRQ event 0x%x.\n",
+		dev->rev,
+		enable ? "enable" : "disable",
+		event);
+
+	spin_lock_irqsave(&dev->ext_lock, irqsave_flags);
+
+	if (dev->aggregated_irq) {
+		mask = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK);
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: current PCIE20_PARF_INT_ALL_MASK:0x%x\n",
+			dev->rev, mask);
+		if (enable)
+			ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK,
+						0, BIT(event));
+		else
+			ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK,
+						BIT(event), 0);
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: new PCIE20_PARF_INT_ALL_MASK:0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK));
+	} else {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Client askes to %s IRQ event 0x%x when aggregated IRQ is not supported.\n",
+			dev->rev,
+			enable ? "enable" : "disable",
+			event);
+		rc = EP_PCIE_ERROR;
+	}
+
+	spin_unlock_irqrestore(&dev->ext_lock, irqsave_flags);
+	return rc;
+}
+
+static irqreturn_t ep_pcie_handle_bme_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+
+	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
+
+	dev->bme_counter++;
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: No. %ld BME IRQ.\n", dev->rev, dev->bme_counter);
+
+	if (readl_relaxed(dev->dm_core + PCIE20_COMMAND_STATUS) & BIT(2)) {
+		/* BME has been enabled */
+		if (!dev->enumerated) {
+			EP_PCIE_DBG(dev,
+				"PCIe V%d:BME is set. Enumeration is complete\n",
+				dev->rev);
+			schedule_work(&dev->handle_bme_work);
+		} else {
+			EP_PCIE_DBG(dev,
+				"PCIe V%d:BME is set again after the enumeration has completed; callback client for link ready.\n",
+				dev->rev);
+			ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP);
+		}
+	} else {
+		EP_PCIE_DBG(dev,
+				"PCIe V%d:BME is still disabled\n", dev->rev);
+	}
+
+	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ep_pcie_handle_linkdown_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+
+	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
+
+	dev->linkdown_counter++;
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: No. %ld linkdown IRQ.\n",
+		dev->rev, dev->linkdown_counter);
+
+	if (!dev->enumerated || dev->link_status == EP_PCIE_LINK_DISABLED) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d:Linkdown IRQ happened when the link is disabled.\n",
+			dev->rev);
+	} else if (dev->suspending) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d:Linkdown IRQ happened when the link is suspending.\n",
+			dev->rev);
+	} else {
+		dev->link_status = EP_PCIE_LINK_DISABLED;
+		EP_PCIE_ERR(dev, "PCIe V%d:PCIe link is down for %ld times\n",
+			dev->rev, dev->linkdown_counter);
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_PHY) |
+				BIT(EP_PCIE_RES_PARF), true);
+		ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKDOWN);
+	}
+
+	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ep_pcie_handle_linkup_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+
+	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
+
+	dev->linkup_counter++;
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: No. %ld linkup IRQ.\n",
+		dev->rev, dev->linkup_counter);
+
+	dev->link_status = EP_PCIE_LINK_UP;
+
+	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ep_pcie_handle_pm_turnoff_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+
+	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
+
+	dev->pm_to_counter++;
+	EP_PCIE_DBG2(dev,
+		"PCIe V%d: No. %ld PM_TURNOFF is received.\n",
+		dev->rev, dev->pm_to_counter);
+	EP_PCIE_DBG2(dev, "PCIe V%d: Put the link into L23.\n",	dev->rev);
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(2));
+
+	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+	u32 dstate;
+
+	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
+
+	dstate = readl_relaxed(dev->dm_core +
+			PCIE20_CON_STATUS) & 0x3;
+
+	if (dev->dump_conf)
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_DM_CORE), false);
+
+	if (dstate == 3) {
+		dev->l23_ready = true;
+		dev->d3_counter++;
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: No. %ld change to D3 state.\n",
+			dev->rev, dev->d3_counter);
+		ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(1));
+		ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT);
+	} else if (dstate == 0) {
+		dev->l23_ready = false;
+		dev->d0_counter++;
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: No. %ld change to D0 state.\n",
+			dev->rev, dev->d0_counter);
+		ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D0);
+	} else {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d:invalid D state change to 0x%x.\n",
+			dev->rev, dstate);
+	}
+
+	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static int ep_pcie_enumeration(struct ep_pcie_dev_t *dev)
+{
+	int ret = 0;
+
+	if (!dev) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: the input handler is NULL.\n",
+			ep_pcie_dev.rev);
+		return EP_PCIE_ERROR;
+	}
+
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: start PCIe link enumeration per host side.\n",
+		dev->rev);
+
+	ret = ep_pcie_core_enable_endpoint(EP_PCIE_OPT_ALL);
+
+	if (ret) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: PCIe link enumeration failed.\n",
+			ep_pcie_dev.rev);
+	} else {
+		if (dev->link_status == EP_PCIE_LINK_ENABLED) {
+			EP_PCIE_INFO(&ep_pcie_dev,
+				"PCIe V%d: PCIe link enumeration is successful with host side.\n",
+				ep_pcie_dev.rev);
+		} else if (dev->link_status == EP_PCIE_LINK_UP) {
+			EP_PCIE_INFO(&ep_pcie_dev,
+				"PCIe V%d: PCIe link training is successful with host side. Waiting for enumeration to complete.\n",
+				ep_pcie_dev.rev);
+		} else {
+			EP_PCIE_ERR(&ep_pcie_dev,
+				"PCIe V%d: PCIe link is in the unexpected status: %d\n",
+				ep_pcie_dev.rev, dev->link_status);
+		}
+	}
+
+	return ret;
+}
+
+static void handle_perst_func(struct work_struct *work)
+{
+	struct ep_pcie_dev_t *dev = container_of(work, struct ep_pcie_dev_t,
+					handle_perst_work);
+
+	ep_pcie_enumeration(dev);
+}
+
+static void handle_bme_func(struct work_struct *work)
+{
+	struct ep_pcie_dev_t *dev = container_of(work,
+			struct ep_pcie_dev_t, handle_bme_work);
+
+	ep_pcie_enumeration_complete(dev);
+}
+
+static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+	u32 perst;
+
+	spin_lock_irqsave(&dev->isr_lock, irqsave_flags);
+
+	perst = gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num);
+
+	if (!dev->enumerated) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PCIe is not enumerated yet; PERST is %sasserted.\n",
+			dev->rev, perst ? "de" : "");
+		if ((!dev->perst_enum) || !perst)
+			goto out;
+		/* start work for link enumeration with the host side */
+		schedule_work(&dev->handle_perst_work);
+
+		goto out;
+	}
+
+	if (perst) {
+		dev->perst_deast = true;
+		dev->perst_deast_counter++;
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: No. %ld PERST deassertion.\n",
+			dev->rev, dev->perst_deast_counter);
+		ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_RST_DEAST);
+	} else {
+		dev->perst_deast = false;
+		dev->perst_ast_counter++;
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: No. %ld PERST assertion.\n",
+			dev->rev, dev->perst_ast_counter);
+		ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_COLD);
+	}
+
+out:
+	spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ep_pcie_handle_global_irq(int irq, void *data)
+{
+	struct ep_pcie_dev_t *dev = data;
+	int i;
+	u32 status = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_STATUS);
+	u32 mask = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK);
+
+	ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_CLEAR, 0, status);
+
+	dev->global_irq_counter++;
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: No. %ld Global IRQ %d received; status:0x%x; mask:0x%x.\n",
+		dev->rev, dev->global_irq_counter, irq, status, mask);
+	status &= mask;
+
+	for (i = 1; i <= EP_PCIE_INT_EVT_MAX; i++) {
+		if (status & BIT(i)) {
+			switch (i) {
+			case EP_PCIE_INT_EVT_LINK_DOWN:
+				EP_PCIE_DUMP(dev,
+					"PCIe V%d: handle linkdown event.\n",
+					dev->rev);
+				ep_pcie_handle_linkdown_irq(irq, data);
+				break;
+			case EP_PCIE_INT_EVT_BME:
+				EP_PCIE_DUMP(dev,
+					"PCIe V%d: handle BME event.\n",
+					dev->rev);
+				ep_pcie_handle_bme_irq(irq, data);
+				break;
+			case EP_PCIE_INT_EVT_PM_TURNOFF:
+				EP_PCIE_DUMP(dev,
+					"PCIe V%d: handle PM Turn-off event.\n",
+					dev->rev);
+				ep_pcie_handle_pm_turnoff_irq(irq, data);
+				break;
+			case EP_PCIE_INT_EVT_MHI_A7:
+				EP_PCIE_DUMP(dev,
+					"PCIe V%d: handle MHI A7 event.\n",
+					dev->rev);
+				ep_pcie_notify_event(dev, EP_PCIE_EVENT_MHI_A7);
+				break;
+			case EP_PCIE_INT_EVT_DSTATE_CHANGE:
+				EP_PCIE_DUMP(dev,
+					"PCIe V%d: handle D state chagge event.\n",
+					dev->rev);
+				ep_pcie_handle_dstate_change_irq(irq, data);
+				break;
+			case EP_PCIE_INT_EVT_LINK_UP:
+				EP_PCIE_DUMP(dev,
+					"PCIe V%d: handle linkup event.\n",
+					dev->rev);
+				ep_pcie_handle_linkup_irq(irq, data);
+				break;
+			default:
+				EP_PCIE_ERR(dev,
+					"PCIe V%d: Unexpected event %d is caught!\n",
+					dev->rev, i);
+			}
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+int32_t ep_pcie_irq_init(struct ep_pcie_dev_t *dev)
+{
+	int ret;
+	struct device *pdev = &dev->pdev->dev;
+	u32 perst_irq;
+
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	/* Initialize all works to be performed before registering for IRQs*/
+	INIT_WORK(&dev->handle_perst_work, handle_perst_func);
+	INIT_WORK(&dev->handle_bme_work, handle_bme_func);
+
+	if (dev->aggregated_irq) {
+		ret = devm_request_irq(pdev,
+			dev->irq[EP_PCIE_INT_GLOBAL].num,
+			ep_pcie_handle_global_irq,
+			IRQF_TRIGGER_HIGH, dev->irq[EP_PCIE_INT_GLOBAL].name,
+			dev);
+		if (ret) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: Unable to request global interrupt %d\n",
+				dev->rev, dev->irq[EP_PCIE_INT_GLOBAL].num);
+			return ret;
+		}
+
+		ret = enable_irq_wake(dev->irq[EP_PCIE_INT_GLOBAL].num);
+		if (ret) {
+			EP_PCIE_ERR(dev,
+				"PCIe V%d: Unable to enable wake for Global interrupt\n",
+				dev->rev);
+			return ret;
+		}
+
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: request global interrupt %d\n",
+			dev->rev, dev->irq[EP_PCIE_INT_GLOBAL].num);
+		goto perst_irq;
+	}
+
+	/* register handler for BME interrupt */
+	ret = devm_request_irq(pdev,
+		dev->irq[EP_PCIE_INT_BME].num,
+		ep_pcie_handle_bme_irq,
+		IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_BME].name,
+		dev);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to request BME interrupt %d\n",
+			dev->rev, dev->irq[EP_PCIE_INT_BME].num);
+		return ret;
+	}
+
+	ret = enable_irq_wake(dev->irq[EP_PCIE_INT_BME].num);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to enable wake for BME interrupt\n",
+			dev->rev);
+		return ret;
+	}
+
+	/* register handler for linkdown interrupt */
+	ret = devm_request_irq(pdev,
+		dev->irq[EP_PCIE_INT_LINK_DOWN].num,
+		ep_pcie_handle_linkdown_irq,
+		IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_LINK_DOWN].name,
+		dev);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to request linkdown interrupt %d\n",
+			dev->rev, dev->irq[EP_PCIE_INT_LINK_DOWN].num);
+		return ret;
+	}
+
+	/* register handler for linkup interrupt */
+	ret = devm_request_irq(pdev,
+		dev->irq[EP_PCIE_INT_LINK_UP].num, ep_pcie_handle_linkup_irq,
+		IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_LINK_UP].name,
+		dev);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to request linkup interrupt %d\n",
+			dev->rev, dev->irq[EP_PCIE_INT_LINK_UP].num);
+		return ret;
+	}
+
+	/* register handler for PM_TURNOFF interrupt */
+	ret = devm_request_irq(pdev,
+		dev->irq[EP_PCIE_INT_PM_TURNOFF].num,
+		ep_pcie_handle_pm_turnoff_irq,
+		IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_PM_TURNOFF].name,
+		dev);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to request PM_TURNOFF interrupt %d\n",
+			dev->rev, dev->irq[EP_PCIE_INT_PM_TURNOFF].num);
+		return ret;
+	}
+
+	/* register handler for D state change interrupt */
+	ret = devm_request_irq(pdev,
+		dev->irq[EP_PCIE_INT_DSTATE_CHANGE].num,
+		ep_pcie_handle_dstate_change_irq,
+		IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_DSTATE_CHANGE].name,
+		dev);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to request D state change interrupt %d\n",
+			dev->rev, dev->irq[EP_PCIE_INT_DSTATE_CHANGE].num);
+		return ret;
+	}
+
+perst_irq:
+	/* register handler for PERST interrupt */
+	perst_irq = gpio_to_irq(dev->gpio[EP_PCIE_GPIO_PERST].num);
+	ret = devm_request_irq(pdev, perst_irq,
+		ep_pcie_handle_perst_irq,
+		IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+		"ep_pcie_perst", dev);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to request PERST interrupt %d\n",
+			dev->rev, perst_irq);
+		return ret;
+	}
+
+	ret = enable_irq_wake(perst_irq);
+	if (ret) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unable to enable PERST interrupt %d\n",
+			dev->rev, perst_irq);
+		return ret;
+	}
+
+	return 0;
+}
+
+void ep_pcie_irq_deinit(struct ep_pcie_dev_t *dev)
+{
+	EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
+
+	disable_irq(gpio_to_irq(dev->gpio[EP_PCIE_GPIO_PERST].num));
+}
+
+int ep_pcie_core_register_event(struct ep_pcie_register_event *reg)
+{
+	if (!reg) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: Event registration is NULL\n",
+			ep_pcie_dev.rev);
+		return -ENODEV;
+	}
+
+	if (!reg->user) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: User of event registration is NULL\n",
+			ep_pcie_dev.rev);
+		return -ENODEV;
+	}
+
+	ep_pcie_dev.event_reg = reg;
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: Event 0x%x is registered\n",
+		ep_pcie_dev.rev, reg->events);
+
+	return 0;
+}
+
+int ep_pcie_core_deregister_event(void)
+{
+	if (ep_pcie_dev.event_reg) {
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: current registered events:0x%x; events are deregistered.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.event_reg->events);
+		ep_pcie_dev.event_reg = NULL;
+	} else {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: Event registration is NULL\n",
+			ep_pcie_dev.rev);
+	}
+
+	return 0;
+}
+
+enum ep_pcie_link_status ep_pcie_core_get_linkstatus(void)
+{
+	struct ep_pcie_dev_t *dev = &ep_pcie_dev;
+	u32 bme;
+
+	if (!dev->power_on || (dev->link_status == EP_PCIE_LINK_DISABLED)) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PCIe endpoint is not powered on.\n",
+			dev->rev);
+		return EP_PCIE_LINK_DISABLED;
+	}
+
+	bme = readl_relaxed(dev->dm_core +
+		PCIE20_COMMAND_STATUS) & BIT(2);
+	if (bme) {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PCIe link is up and BME is enabled; current SW link status:%d.\n",
+			dev->rev, dev->link_status);
+		dev->link_status = EP_PCIE_LINK_ENABLED;
+	} else {
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PCIe link is up but BME is disabled; current SW link status:%d.\n",
+			dev->rev, dev->link_status);
+		dev->link_status = EP_PCIE_LINK_UP;
+	}
+	return dev->link_status;
+}
+
+int ep_pcie_core_config_outbound_iatu(struct ep_pcie_iatu entries[],
+				u32 num_entries)
+{
+	u32 data_start = 0;
+	u32 data_end = 0;
+	u32 data_tgt_lower = 0;
+	u32 data_tgt_upper = 0;
+	u32 ctrl_start = 0;
+	u32 ctrl_end = 0;
+	u32 ctrl_tgt_lower = 0;
+	u32 ctrl_tgt_upper = 0;
+	u32 upper = 0;
+	bool once = true;
+
+	if (ep_pcie_dev.active_config) {
+		upper = EP_PCIE_OATU_UPPER;
+		if (once) {
+			once = false;
+			EP_PCIE_DBG2(&ep_pcie_dev,
+				"PCIe V%d: No outbound iATU config is needed since active config is enabled.\n",
+				ep_pcie_dev.rev);
+		}
+	}
+
+	if ((num_entries > MAX_IATU_ENTRY_NUM) || !num_entries) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: Wrong iATU entry number %d.\n",
+			ep_pcie_dev.rev, num_entries);
+		return EP_PCIE_ERROR;
+	}
+
+	data_start = entries[0].start;
+	data_end = entries[0].end;
+	data_tgt_lower = entries[0].tgt_lower;
+	data_tgt_upper = entries[0].tgt_upper;
+
+	if (num_entries > 1) {
+		ctrl_start = entries[1].start;
+		ctrl_end = entries[1].end;
+		ctrl_tgt_lower = entries[1].tgt_lower;
+		ctrl_tgt_upper = entries[1].tgt_upper;
+	}
+
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: data_start:0x%x; data_end:0x%x; data_tgt_lower:0x%x; data_tgt_upper:0x%x; ctrl_start:0x%x; ctrl_end:0x%x; ctrl_tgt_lower:0x%x; ctrl_tgt_upper:0x%x.\n",
+		ep_pcie_dev.rev, data_start, data_end, data_tgt_lower,
+		data_tgt_upper, ctrl_start, ctrl_end, ctrl_tgt_lower,
+		ctrl_tgt_upper);
+
+
+	if ((ctrl_end < data_start) || (data_end < ctrl_start)) {
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: iATU configuration case No. 1: detached.\n",
+			ep_pcie_dev.rev);
+		ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_DATA,
+					data_start, upper, data_end,
+					data_tgt_lower, data_tgt_upper);
+		ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_CTRL,
+					ctrl_start, upper, ctrl_end,
+					ctrl_tgt_lower, ctrl_tgt_upper);
+	} else if ((data_start <= ctrl_start) && (ctrl_end <= data_end)) {
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: iATU configuration case No. 2: included.\n",
+			ep_pcie_dev.rev);
+		ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_DATA,
+					data_start, upper, data_end,
+					data_tgt_lower, data_tgt_upper);
+	} else {
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: iATU configuration case No. 3: overlap.\n",
+			ep_pcie_dev.rev);
+		ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_CTRL,
+					ctrl_start, upper, ctrl_end,
+					ctrl_tgt_lower, ctrl_tgt_upper);
+		ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_DATA,
+					data_start, upper, data_end,
+					data_tgt_lower, data_tgt_upper);
+	}
+
+	return 0;
+}
+
+int ep_pcie_core_get_msi_config(struct ep_pcie_msi_config *cfg)
+{
+	u32 cap, lower, upper, data, ctrl_reg;
+	static u32 changes;
+
+	if (ep_pcie_dev.link_status == EP_PCIE_LINK_DISABLED) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: PCIe link is currently disabled.\n",
+			ep_pcie_dev.rev);
+		return EP_PCIE_ERROR;
+	}
+
+	cap = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_CAP_ID_NEXT_CTRL);
+	EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: MSI CAP:0x%x\n",
+			ep_pcie_dev.rev, cap);
+
+	if (!(cap & BIT(16))) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: MSI is not enabled yet.\n",
+			ep_pcie_dev.rev);
+		return EP_PCIE_ERROR;
+	}
+
+	lower = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_LOWER);
+	upper = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_UPPER);
+	data = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_DATA);
+	ctrl_reg = readl_relaxed(ep_pcie_dev.dm_core +
+					PCIE20_MSI_CAP_ID_NEXT_CTRL);
+
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: MSI info: lower:0x%x; upper:0x%x; data:0x%x.\n",
+		ep_pcie_dev.rev, lower, upper, data);
+
+	if (ctrl_reg & BIT(16)) {
+		struct resource *msi =
+				ep_pcie_dev.res[EP_PCIE_RES_MSI].resource;
+		if (ep_pcie_dev.active_config)
+			ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_MSI,
+					msi->start, EP_PCIE_OATU_UPPER,
+					msi->end, lower, upper);
+		else
+			ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev,
+					EP_PCIE_OATU_INDEX_MSI,
+					msi->start, 0, msi->end,
+					lower, upper);
+
+		if (ep_pcie_dev.active_config) {
+			cfg->lower = lower;
+			cfg->upper = upper;
+		} else {
+			cfg->lower = msi->start + (lower & 0xfff);
+			cfg->upper = 0;
+		}
+		cfg->data = data;
+		cfg->msg_num = (cap >> 20) & 0x7;
+		if ((lower != ep_pcie_dev.msi_cfg.lower)
+			|| (upper != ep_pcie_dev.msi_cfg.upper)
+			|| (data != ep_pcie_dev.msi_cfg.data)
+			|| (cfg->msg_num != ep_pcie_dev.msi_cfg.msg_num)) {
+			changes++;
+			EP_PCIE_DBG(&ep_pcie_dev,
+				"PCIe V%d: MSI config has been changed by host side for %d time(s).\n",
+				ep_pcie_dev.rev, changes);
+			EP_PCIE_DBG(&ep_pcie_dev,
+				"PCIe V%d: old MSI cfg: lower:0x%x; upper:0x%x; data:0x%x; msg_num:0x%x.\n",
+				ep_pcie_dev.rev, ep_pcie_dev.msi_cfg.lower,
+				ep_pcie_dev.msi_cfg.upper,
+				ep_pcie_dev.msi_cfg.data,
+				ep_pcie_dev.msi_cfg.msg_num);
+			ep_pcie_dev.msi_cfg.lower = lower;
+			ep_pcie_dev.msi_cfg.upper = upper;
+			ep_pcie_dev.msi_cfg.data = data;
+			ep_pcie_dev.msi_cfg.msg_num = cfg->msg_num;
+		}
+		return 0;
+	}
+
+	EP_PCIE_ERR(&ep_pcie_dev,
+		"PCIe V%d: Wrong MSI info found when MSI is enabled: lower:0x%x; data:0x%x.\n",
+		ep_pcie_dev.rev, lower, data);
+	return EP_PCIE_ERROR;
+}
+
+int ep_pcie_core_trigger_msi(u32 idx)
+{
+	u32 addr, data, ctrl_reg;
+	int max_poll = MSI_EXIT_L1SS_WAIT_MAX_COUNT;
+
+	if (ep_pcie_dev.link_status == EP_PCIE_LINK_DISABLED) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: PCIe link is currently disabled.\n",
+			ep_pcie_dev.rev);
+		return EP_PCIE_ERROR;
+	}
+
+	addr = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_LOWER);
+	data = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_DATA);
+	ctrl_reg = readl_relaxed(ep_pcie_dev.dm_core +
+					PCIE20_MSI_CAP_ID_NEXT_CTRL);
+
+	if (ctrl_reg & BIT(16)) {
+		ep_pcie_dev.msi_counter++;
+		EP_PCIE_DUMP(&ep_pcie_dev,
+			"PCIe V%d: No. %ld MSI fired for IRQ %d; index from client:%d; active-config is %s enabled.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.msi_counter,
+			data + idx, idx,
+			ep_pcie_dev.active_config ? "" : "not");
+
+		if (ep_pcie_dev.active_config) {
+			u32 status;
+
+			if (ep_pcie_dev.msi_counter % 2) {
+				EP_PCIE_DBG2(&ep_pcie_dev,
+					"PCIe V%d: try to trigger MSI by PARF_MSI_GEN.\n",
+					ep_pcie_dev.rev);
+				ep_pcie_write_reg(ep_pcie_dev.parf,
+					PCIE20_PARF_MSI_GEN, idx);
+				status = readl_relaxed(ep_pcie_dev.parf +
+					PCIE20_PARF_LTR_MSI_EXIT_L1SS);
+				while ((status & BIT(1)) && (max_poll-- > 0)) {
+					udelay(MSI_EXIT_L1SS_WAIT);
+					status = readl_relaxed(ep_pcie_dev.parf
+						+
+						PCIE20_PARF_LTR_MSI_EXIT_L1SS);
+				}
+				if (max_poll == 0)
+					EP_PCIE_DBG2(&ep_pcie_dev,
+						"PCIe V%d: MSI_EXIT_L1SS is not cleared yet.\n",
+						ep_pcie_dev.rev);
+				else
+					EP_PCIE_DBG2(&ep_pcie_dev,
+						"PCIe V%d: MSI_EXIT_L1SS has been cleared.\n",
+						ep_pcie_dev.rev);
+			} else {
+				EP_PCIE_DBG2(&ep_pcie_dev,
+						"PCIe V%d: try to trigger MSI by direct address write as well.\n",
+						ep_pcie_dev.rev);
+				ep_pcie_write_reg(ep_pcie_dev.msi, addr & 0xfff,
+							data + idx);
+			}
+		} else {
+			ep_pcie_write_reg(ep_pcie_dev.msi, addr & 0xfff, data
+						+ idx);
+		}
+		return 0;
+	}
+
+	EP_PCIE_ERR(&ep_pcie_dev,
+		"PCIe V%d: MSI is not enabled yet. MSI addr:0x%x; data:0x%x; index from client:%d.\n",
+		ep_pcie_dev.rev, addr, data, idx);
+	return EP_PCIE_ERROR;
+}
+
+int ep_pcie_core_wakeup_host(void)
+{
+	struct ep_pcie_dev_t *dev = &ep_pcie_dev;
+
+	if (dev->perst_deast && !dev->l23_ready) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: request to assert WAKE# when PERST is de-asserted and D3hot is not received.\n",
+			dev->rev);
+		return EP_PCIE_ERROR;
+	}
+
+	dev->wake_counter++;
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: No. %ld to assert PCIe WAKE#; perst is %s de-asserted; D3hot is %s received.\n",
+		dev->rev, dev->wake_counter,
+		dev->perst_deast ? "" : "not",
+		dev->l23_ready ? "" : "not");
+	gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+			1 - dev->gpio[EP_PCIE_GPIO_WAKE].on);
+	gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+			dev->gpio[EP_PCIE_GPIO_WAKE].on);
+	return 0;
+}
+
+int ep_pcie_core_config_db_routing(struct ep_pcie_db_config chdb_cfg,
+				struct ep_pcie_db_config erdb_cfg)
+{
+	u32 dbs = (erdb_cfg.end << 24) | (erdb_cfg.base << 16) |
+			(chdb_cfg.end << 8) | chdb_cfg.base;
+
+	ep_pcie_write_reg(ep_pcie_dev.parf, PCIE20_PARF_MHI_IPA_DBS, dbs);
+	ep_pcie_write_reg(ep_pcie_dev.parf,
+			PCIE20_PARF_MHI_IPA_CDB_TARGET_LOWER,
+			chdb_cfg.tgt_addr);
+	ep_pcie_write_reg(ep_pcie_dev.parf,
+			PCIE20_PARF_MHI_IPA_EDB_TARGET_LOWER,
+			erdb_cfg.tgt_addr);
+
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: DB routing info: chdb_cfg.base:0x%x; chdb_cfg.end:0x%x; erdb_cfg.base:0x%x; erdb_cfg.end:0x%x; chdb_cfg.tgt_addr:0x%x; erdb_cfg.tgt_addr:0x%x.\n",
+		ep_pcie_dev.rev, chdb_cfg.base, chdb_cfg.end, erdb_cfg.base,
+		erdb_cfg.end, chdb_cfg.tgt_addr, erdb_cfg.tgt_addr);
+
+	return 0;
+}
+
+struct ep_pcie_hw hw_drv = {
+	.register_event	= ep_pcie_core_register_event,
+	.deregister_event = ep_pcie_core_deregister_event,
+	.get_linkstatus = ep_pcie_core_get_linkstatus,
+	.config_outbound_iatu = ep_pcie_core_config_outbound_iatu,
+	.get_msi_config = ep_pcie_core_get_msi_config,
+	.trigger_msi = ep_pcie_core_trigger_msi,
+	.wakeup_host = ep_pcie_core_wakeup_host,
+	.config_db_routing = ep_pcie_core_config_db_routing,
+	.enable_endpoint = ep_pcie_core_enable_endpoint,
+	.disable_endpoint = ep_pcie_core_disable_endpoint,
+	.mask_irq_event = ep_pcie_core_mask_irq_event,
+};
+
+static int ep_pcie_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	pr_debug("%s\n", __func__);
+
+	ep_pcie_dev.link_speed = 1;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,pcie-link-speed",
+				&ep_pcie_dev.link_speed);
+	if (ret)
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: pcie-link-speed does not exist.\n",
+			ep_pcie_dev.rev);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-link-speed:%d.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.link_speed);
+
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,dbi-base-reg",
+				&ep_pcie_dev.dbi_base_reg);
+	if (ret)
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: dbi-base-reg does not exist.\n",
+			ep_pcie_dev.rev);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: dbi-base-reg:0x%x.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.dbi_base_reg);
+
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,slv-space-reg",
+				&ep_pcie_dev.slv_space_reg);
+	if (ret)
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: slv-space-reg does not exist.\n",
+			ep_pcie_dev.rev);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: slv-space-reg:0x%x.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.slv_space_reg);
+
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,phy-status-reg",
+				&ep_pcie_dev.phy_status_reg);
+	if (ret)
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: phy-status-reg does not exist.\n",
+			ep_pcie_dev.rev);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: phy-status-reg:0x%x.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.phy_status_reg);
+
+	ep_pcie_dev.phy_rev = 1;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,pcie-phy-ver",
+				&ep_pcie_dev.phy_rev);
+	if (ret)
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: pcie-phy-ver does not exist.\n",
+			ep_pcie_dev.rev);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-phy-ver:%d.\n",
+			ep_pcie_dev.rev, ep_pcie_dev.phy_rev);
+
+	ep_pcie_dev.active_config = of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,pcie-active-config");
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: active config is %s enabled.\n",
+		ep_pcie_dev.rev, ep_pcie_dev.active_config ? "" : "not");
+
+	ep_pcie_dev.aggregated_irq =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,pcie-aggregated-irq");
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: aggregated IRQ is %s enabled.\n",
+		ep_pcie_dev.rev, ep_pcie_dev.aggregated_irq ? "" : "not");
+
+	ep_pcie_dev.mhi_a7_irq =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,pcie-mhi-a7-irq");
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: Mhi a7 IRQ is %s enabled.\n",
+		ep_pcie_dev.rev, ep_pcie_dev.mhi_a7_irq ? "" : "not");
+
+	ep_pcie_dev.perst_enum = of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,pcie-perst-enum");
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: enum by PERST is %s enabled.\n",
+		ep_pcie_dev.rev, ep_pcie_dev.perst_enum ? "" : "not");
+
+	ep_pcie_dev.rev = 1711211;
+	ep_pcie_dev.pdev = pdev;
+	memcpy(ep_pcie_dev.vreg, ep_pcie_vreg_info,
+				sizeof(ep_pcie_vreg_info));
+	memcpy(ep_pcie_dev.gpio, ep_pcie_gpio_info,
+				sizeof(ep_pcie_gpio_info));
+	memcpy(ep_pcie_dev.clk, ep_pcie_clk_info,
+				sizeof(ep_pcie_clk_info));
+	memcpy(ep_pcie_dev.pipeclk, ep_pcie_pipe_clk_info,
+				sizeof(ep_pcie_pipe_clk_info));
+	memcpy(ep_pcie_dev.reset, ep_pcie_reset_info,
+				sizeof(ep_pcie_reset_info));
+	memcpy(ep_pcie_dev.res, ep_pcie_res_info,
+				sizeof(ep_pcie_res_info));
+	memcpy(ep_pcie_dev.irq, ep_pcie_irq_info,
+				sizeof(ep_pcie_irq_info));
+
+	ret = ep_pcie_get_resources(&ep_pcie_dev,
+				ep_pcie_dev.pdev);
+	if (ret) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: failed to get resources.\n",
+			ep_pcie_dev.rev);
+		goto res_failure;
+	}
+
+	ret = ep_pcie_gpio_init(&ep_pcie_dev);
+	if (ret) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: failed to init GPIO.\n",
+			ep_pcie_dev.rev);
+		ep_pcie_release_resources(&ep_pcie_dev);
+		goto gpio_failure;
+	}
+
+	ret = ep_pcie_irq_init(&ep_pcie_dev);
+	if (ret) {
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: failed to init IRQ.\n",
+			ep_pcie_dev.rev);
+		ep_pcie_release_resources(&ep_pcie_dev);
+		ep_pcie_gpio_deinit(&ep_pcie_dev);
+		goto irq_failure;
+	}
+
+	if (ep_pcie_dev.perst_enum &&
+		!gpio_get_value(ep_pcie_dev.gpio[EP_PCIE_GPIO_PERST].num)) {
+		EP_PCIE_DBG2(&ep_pcie_dev,
+			"PCIe V%d: %s probe is done; link will be trained when PERST is deasserted.\n",
+		ep_pcie_dev.rev, dev_name(&(pdev->dev)));
+		return 0;
+	}
+
+	EP_PCIE_DBG(&ep_pcie_dev,
+		"PCIe V%d: %s got resources successfully; start turning on the link.\n",
+		ep_pcie_dev.rev, dev_name(&(pdev->dev)));
+
+	ret = ep_pcie_enumeration(&ep_pcie_dev);
+
+	if (!ret || ep_pcie_debug_keep_resource)
+		return 0;
+
+	ep_pcie_irq_deinit(&ep_pcie_dev);
+irq_failure:
+	ep_pcie_gpio_deinit(&ep_pcie_dev);
+gpio_failure:
+	ep_pcie_release_resources(&ep_pcie_dev);
+res_failure:
+	EP_PCIE_ERR(&ep_pcie_dev, "PCIe V%d: Driver probe failed:%d\n",
+		ep_pcie_dev.rev, ret);
+
+	return ret;
+}
+
+static int __exit ep_pcie_remove(struct platform_device *pdev)
+{
+	pr_debug("%s\n", __func__);
+
+	ep_pcie_irq_deinit(&ep_pcie_dev);
+	ep_pcie_vreg_deinit(&ep_pcie_dev);
+	ep_pcie_pipe_clk_deinit(&ep_pcie_dev);
+	ep_pcie_clk_deinit(&ep_pcie_dev);
+	ep_pcie_gpio_deinit(&ep_pcie_dev);
+	ep_pcie_release_resources(&ep_pcie_dev);
+	ep_pcie_deregister_drv(&hw_drv);
+
+	return 0;
+}
+
+static const struct of_device_id ep_pcie_match[] = {
+	{	.compatible = "qcom,pcie-ep",
+	},
+	{}
+};
+
+static struct platform_driver ep_pcie_driver = {
+	.probe	= ep_pcie_probe,
+	.remove	= ep_pcie_remove,
+	.driver	= {
+		.name		= "pcie-ep",
+		.owner		= THIS_MODULE,
+		.of_match_table	= ep_pcie_match,
+	},
+};
+
+static int __init ep_pcie_init(void)
+{
+	int ret;
+	char logname[MAX_NAME_LEN];
+
+	pr_debug("%s\n", __func__);
+
+	snprintf(logname, MAX_NAME_LEN, "ep-pcie-long");
+	ep_pcie_dev.ipc_log_sel =
+		ipc_log_context_create(EP_PCIE_LOG_PAGES, logname, 0);
+	if (ep_pcie_dev.ipc_log_sel == NULL)
+		pr_err("%s: unable to create IPC selected log for %s\n",
+			__func__, logname);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: IPC selected logging is enable for %s\n",
+			ep_pcie_dev.rev, logname);
+
+	snprintf(logname, MAX_NAME_LEN, "ep-pcie-short");
+	ep_pcie_dev.ipc_log_ful =
+		ipc_log_context_create(EP_PCIE_LOG_PAGES * 2, logname, 0);
+	if (ep_pcie_dev.ipc_log_ful == NULL)
+		pr_err("%s: unable to create IPC detailed log for %s\n",
+			__func__, logname);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: IPC detailed logging is enable for %s\n",
+			ep_pcie_dev.rev, logname);
+
+	snprintf(logname, MAX_NAME_LEN, "ep-pcie-dump");
+	ep_pcie_dev.ipc_log_dump =
+		ipc_log_context_create(EP_PCIE_LOG_PAGES, logname, 0);
+	if (ep_pcie_dev.ipc_log_dump == NULL)
+		pr_err("%s: unable to create IPC dump log for %s\n",
+			__func__, logname);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: IPC dump logging is enable for %s\n",
+			ep_pcie_dev.rev, logname);
+
+	mutex_init(&ep_pcie_dev.setup_mtx);
+	mutex_init(&ep_pcie_dev.ext_mtx);
+	spin_lock_init(&ep_pcie_dev.ext_lock);
+	spin_lock_init(&ep_pcie_dev.isr_lock);
+
+	ep_pcie_debugfs_init(&ep_pcie_dev);
+
+	ret = platform_driver_register(&ep_pcie_driver);
+
+	if (ret)
+		EP_PCIE_ERR(&ep_pcie_dev,
+			"PCIe V%d: failed register platform driver:%d\n",
+			ep_pcie_dev.rev, ret);
+	else
+		EP_PCIE_DBG(&ep_pcie_dev,
+			"PCIe V%d: platform driver is registered.\n",
+			ep_pcie_dev.rev);
+
+	return ret;
+}
+
+static void __exit ep_pcie_exit(void)
+{
+	pr_debug("%s\n", __func__);
+
+	ipc_log_context_destroy(ep_pcie_dev.ipc_log_sel);
+	ipc_log_context_destroy(ep_pcie_dev.ipc_log_ful);
+	ipc_log_context_destroy(ep_pcie_dev.ipc_log_dump);
+
+	ep_pcie_debugfs_exit();
+
+	platform_driver_unregister(&ep_pcie_driver);
+}
+
+module_init(ep_pcie_init);
+module_exit(ep_pcie_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM PCIe Endpoint Driver");
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c b/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c
new file mode 100644
index 0000000..1f09a88
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c
@@ -0,0 +1,459 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Debugging enhancement in MSM PCIe endpoint driver.
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include "ep_pcie_com.h"
+#include "ep_pcie_phy.h"
+
+static struct dentry *dent_ep_pcie;
+static struct dentry *dfile_case;
+static struct ep_pcie_dev_t *dev;
+
+static void ep_ep_pcie_phy_dump_pcs_debug_bus(struct ep_pcie_dev_t *dev,
+					u32 cntrl4, u32 cntrl5,
+					u32 cntrl6, u32 cntrl7)
+{
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL4, cntrl4);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL5, cntrl5);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL6, cntrl6);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL7, cntrl7);
+
+	if (!cntrl4 && !cntrl5 && !cntrl6 && !cntrl7) {
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: zero out test control registers.\n\n",
+			dev->rev);
+		return;
+	}
+
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_TEST_CONTROL4: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL4));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_TEST_CONTROL5: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL5));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_TEST_CONTROL6: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL6));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_TEST_CONTROL7: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL7));
+
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_DEBUG_BUS_0_STATUS: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_0_STATUS));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_DEBUG_BUS_1_STATUS: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_1_STATUS));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_DEBUG_BUS_2_STATUS: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_2_STATUS));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_DEBUG_BUS_3_STATUS: 0x%x\n\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_3_STATUS));
+}
+
+static void ep_ep_pcie_phy_dump_pcs_misc_debug_bus(struct ep_pcie_dev_t *dev,
+					u32 b0, u32 b1,	u32 b2, u32 b3)
+{
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX, b0);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX, b1);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX, b2);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX, b3);
+
+	if (!b0 && !b1 && !b2 && !b3) {
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: zero out misc debug bus byte index registers.\n\n",
+			dev->rev);
+		return;
+	}
+
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX: 0x%x\n",
+		dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX: 0x%x\n",
+		dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX: 0x%x\n",
+		dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX: 0x%x\n",
+		dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX));
+
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_0_STATUS: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_0_STATUS));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_1_STATUS: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_1_STATUS));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_2_STATUS: 0x%x\n", dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_2_STATUS));
+	EP_PCIE_DUMP(dev,
+		"PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_3_STATUS: 0x%x\n\n",
+		dev->rev,
+		readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_3_STATUS));
+}
+
+static void ep_pcie_phy_dump(struct ep_pcie_dev_t *dev)
+{
+	int i;
+	u32 write_val;
+
+	EP_PCIE_DUMP(dev, "PCIe V%d: Beginning of PHY debug dump.\n\n",
+			dev->rev);
+
+	EP_PCIE_DUMP(dev, "PCIe V%d: PCS Debug Signals.\n\n", dev->rev);
+
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x01, 0x02, 0x03, 0x0A);
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x0E, 0x0F, 0x12, 0x13);
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x18, 0x19, 0x1A, 0x1B);
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x1C, 0x1D, 0x1E, 0x1F);
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x20, 0x21, 0x22, 0x23);
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0, 0, 0, 0);
+
+	EP_PCIE_DUMP(dev, "PCIe V%d: PCS Misc Debug Signals.\n\n", dev->rev);
+
+	ep_ep_pcie_phy_dump_pcs_misc_debug_bus(dev, 0x1, 0x2, 0x3, 0x4);
+	ep_ep_pcie_phy_dump_pcs_misc_debug_bus(dev, 0x5, 0x6, 0x7, 0x8);
+	ep_ep_pcie_phy_dump_pcs_misc_debug_bus(dev, 0, 0, 0, 0);
+
+	EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES COM Debug Signals.\n\n", dev->rev);
+
+	for (i = 0; i < 2; i++) {
+		write_val = 0x2 + i;
+
+		ep_pcie_write_reg(dev->phy, QSERDES_COM_DEBUG_BUS_SEL,
+			write_val);
+
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: to QSERDES_COM_DEBUG_BUS_SEL: 0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS_SEL));
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: QSERDES_COM_DEBUG_BUS0: 0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS0));
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: QSERDES_COM_DEBUG_BUS1: 0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS1));
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: QSERDES_COM_DEBUG_BUS2: 0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS2));
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: QSERDES_COM_DEBUG_BUS3: 0x%x\n\n",
+			dev->rev,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS3));
+	}
+
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_DEBUG_BUS_SEL, 0);
+
+	EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES LANE Debug Signals.\n\n",
+			dev->rev);
+
+	for (i = 0; i < 3; i++) {
+		write_val = 0x1 + i;
+		ep_pcie_write_reg(dev->phy,
+			QSERDES_TX_DEBUG_BUS_SEL, write_val);
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: QSERDES_TX_DEBUG_BUS_SEL: 0x%x\n",
+			dev->rev,
+			readl_relaxed(dev->phy + QSERDES_TX_DEBUG_BUS_SEL));
+
+		ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x30, 0x31, 0x32, 0x33);
+	}
+
+	ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0, 0, 0, 0);
+
+	EP_PCIE_DUMP(dev, "PCIe V%d: End of PHY debug dump.\n\n", dev->rev);
+
+}
+
+void ep_pcie_reg_dump(struct ep_pcie_dev_t *dev, u32 sel, bool linkdown)
+{
+	int r, i;
+	u32 original;
+	u32 size;
+
+	EP_PCIE_DBG(dev,
+		"PCIe V%d: Dump PCIe reg for 0x%x %s linkdown.\n",
+		dev->rev, sel, linkdown ? "with" : "without");
+
+	if (!dev->power_on) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: the power is already down; can't dump registers.\n",
+			dev->rev);
+		return;
+	}
+
+	if (linkdown) {
+		EP_PCIE_DUMP(dev,
+			"PCIe V%d: dump PARF registers for linkdown case.\n",
+			dev->rev);
+
+		original = readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL);
+		for (i = 1; i <= 0x1A; i++) {
+			ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL,
+				0xFF0000, i << 16);
+			EP_PCIE_DUMP(dev,
+				"PCIe V%d: PARF_SYS_CTRL:0x%x PARF_TEST_BUS:0x%x\n",
+				dev->rev,
+				readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL),
+				readl_relaxed(dev->parf +
+							PCIE20_PARF_TEST_BUS));
+		}
+		ep_pcie_write_reg(dev->parf, PCIE20_PARF_SYS_CTRL, original);
+	}
+
+	for (r = 0; r < EP_PCIE_MAX_RES; r++) {
+		if (!(sel & BIT(r)))
+			continue;
+
+		if ((r == EP_PCIE_RES_PHY) && (dev->phy_rev > 3))
+			ep_pcie_phy_dump(dev);
+
+		size = resource_size(dev->res[r].resource);
+		EP_PCIE_DUMP(dev,
+			"\nPCIe V%d: dump registers of %s.\n\n",
+			dev->rev, dev->res[r].name);
+
+		for (i = 0; i < size; i += 32) {
+			EP_PCIE_DUMP(dev,
+				"0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+				i, readl_relaxed(dev->res[r].base + i),
+				readl_relaxed(dev->res[r].base + (i + 4)),
+				readl_relaxed(dev->res[r].base + (i + 8)),
+				readl_relaxed(dev->res[r].base + (i + 12)),
+				readl_relaxed(dev->res[r].base + (i + 16)),
+				readl_relaxed(dev->res[r].base + (i + 20)),
+				readl_relaxed(dev->res[r].base + (i + 24)),
+				readl_relaxed(dev->res[r].base + (i + 28)));
+		}
+	}
+}
+
+static void ep_pcie_show_status(struct ep_pcie_dev_t *dev)
+{
+	EP_PCIE_DBG_FS("PCIe: is %s enumerated\n",
+		dev->enumerated ? "" : "not");
+	EP_PCIE_DBG_FS("PCIe: link is %s\n",
+		(dev->link_status == EP_PCIE_LINK_ENABLED)
+		? "enabled" : "disabled");
+	EP_PCIE_DBG_FS("the link is %s suspending\n",
+		dev->suspending ? "" : "not");
+	EP_PCIE_DBG_FS("the power is %s on\n",
+		dev->power_on ? "" : "not");
+	EP_PCIE_DBG_FS("bus_client: %d\n",
+		dev->bus_client);
+	EP_PCIE_DBG_FS("linkdown_counter: %lu\n",
+		dev->linkdown_counter);
+	EP_PCIE_DBG_FS("linkup_counter: %lu\n",
+		dev->linkup_counter);
+	EP_PCIE_DBG_FS("wake_counter: %lu\n",
+		dev->wake_counter);
+	EP_PCIE_DBG_FS("d0_counter: %lu\n",
+		dev->d0_counter);
+	EP_PCIE_DBG_FS("d3_counter: %lu\n",
+		dev->d3_counter);
+	EP_PCIE_DBG_FS("perst_ast_counter: %lu\n",
+		dev->perst_ast_counter);
+	EP_PCIE_DBG_FS("perst_deast_counter: %lu\n",
+		dev->perst_deast_counter);
+}
+
+static ssize_t ep_pcie_cmd_debug(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	unsigned int testcase = 0;
+	struct ep_pcie_msi_config msi_cfg;
+	int i;
+	struct ep_pcie_hw *phandle = NULL;
+	struct ep_pcie_iatu entries[2] = {
+		{0x80000000, 0xbe7fffff, 0, 0},
+		{0xb1440000, 0xb144ae1e, 0x31440000, 0}
+	};
+	struct ep_pcie_db_config chdb_cfg = {0x64, 0x6b, 0xfd4fa000};
+	struct ep_pcie_db_config erdb_cfg = {0x64, 0x6b, 0xfd4fa080};
+
+	phandle = ep_pcie_get_phandle(hw_drv.device_id);
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		testcase = (testcase * 10) + (str[i] - '0');
+
+	EP_PCIE_DBG_FS("PCIe: TEST: %d\n", testcase);
+
+
+	switch (testcase) {
+	case 0: /* output status */
+		ep_pcie_show_status(dev);
+		break;
+	case 1: /* output PHY and PARF registers */
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_PHY) |
+				BIT(EP_PCIE_RES_PARF), true);
+		break;
+	case 2: /* output core registers */
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_DM_CORE), false);
+		break;
+	case 3: /* output MMIO registers */
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_MMIO), false);
+		break;
+	case 4: /* output ELBI registers */
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_ELBI), false);
+		break;
+	case 5: /* output MSI registers */
+		ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_MSI), false);
+		break;
+	case 6: /* turn on link */
+		ep_pcie_enable_endpoint(phandle, EP_PCIE_OPT_ALL);
+		break;
+	case 7: /* enumeration */
+		ep_pcie_enable_endpoint(phandle, EP_PCIE_OPT_ENUM);
+		break;
+	case 8: /* turn off link */
+		ep_pcie_disable_endpoint(phandle);
+		break;
+	case 9: /* check MSI */
+		ep_pcie_get_msi_config(phandle, &msi_cfg);
+		break;
+	case 10: /* trigger MSI */
+		ep_pcie_trigger_msi(phandle, 0);
+		break;
+	case 11: /* indicate the status of PCIe link */
+		EP_PCIE_DBG_FS("\nPCIe: link status is %d.\n\n",
+			ep_pcie_get_linkstatus(phandle));
+		break;
+	case 12: /* configure outbound iATU */
+		ep_pcie_config_outbound_iatu(phandle, entries, 2);
+		break;
+	case 13: /* wake up the host */
+		ep_pcie_wakeup_host(phandle);
+		break;
+	case 14: /* Configure routing of doorbells */
+		ep_pcie_config_db_routing(phandle, chdb_cfg, erdb_cfg);
+		break;
+	case 21: /* write D3 */
+		EP_PCIE_DBG_FS("\nPCIe Testcase %d: write D3 to EP\n\n",
+			testcase);
+		EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x before change\n\n",
+			readl_relaxed(dev->dm_core + 0x44));
+		ep_pcie_write_mask(dev->dm_core + 0x44, 0, 0x3);
+		EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x now\n\n",
+			readl_relaxed(dev->dm_core + 0x44));
+		break;
+	case 22: /* write D0 */
+		EP_PCIE_DBG_FS("\nPCIe Testcase %d: write D0 to EP\n\n",
+			testcase);
+		EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x before change\n\n",
+			readl_relaxed(dev->dm_core + 0x44));
+		ep_pcie_write_mask(dev->dm_core + 0x44, 0x3, 0);
+		EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x now\n\n",
+			readl_relaxed(dev->dm_core + 0x44));
+		break;
+	case 23: /* assert wake */
+		EP_PCIE_DBG_FS("\nPCIe Testcase %d: assert wake\n\n",
+			testcase);
+		gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+			dev->gpio[EP_PCIE_GPIO_WAKE].on);
+		break;
+	case 24: /* deassert wake */
+		EP_PCIE_DBG_FS("\nPCIe Testcase %d: deassert wake\n\n",
+			testcase);
+		gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num,
+			1 - dev->gpio[EP_PCIE_GPIO_WAKE].on);
+		break;
+	case 25: /* output PERST# status */
+		EP_PCIE_DBG_FS("\nPCIe: PERST# is %d.\n\n",
+			gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num));
+		break;
+	case 26: /* output WAKE# status */
+		EP_PCIE_DBG_FS("\nPCIe: WAKE# is %d.\n\n",
+			gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num));
+		break;
+	case 31: /* output core registers when D3 hot is set by host*/
+		dev->dump_conf = true;
+		break;
+	case 32: /* do not output core registers when D3 hot is set by host*/
+		dev->dump_conf = false;
+		break;
+	default:
+		EP_PCIE_DBG_FS("PCIe: Invalid testcase: %d.\n", testcase);
+		break;
+	}
+
+	if (ret == 0)
+		return count;
+	else
+		return -EFAULT;
+}
+
+const struct file_operations ep_pcie_cmd_debug_ops = {
+	.write = ep_pcie_cmd_debug,
+};
+
+void ep_pcie_debugfs_init(struct ep_pcie_dev_t *ep_dev)
+{
+	dev = ep_dev;
+	dent_ep_pcie = debugfs_create_dir("pcie-ep", 0);
+	if (IS_ERR(dent_ep_pcie)) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: fail to create the folder for debug_fs.\n",
+			dev->rev);
+		return;
+	}
+
+	dfile_case = debugfs_create_file("case", 0664,
+					dent_ep_pcie, 0,
+					&ep_pcie_cmd_debug_ops);
+	if (!dfile_case || IS_ERR(dfile_case)) {
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: fail to create the file for case.\n",
+			dev->rev);
+		goto case_error;
+	}
+
+	EP_PCIE_DBG2(dev,
+		"PCIe V%d: debugfs is enabled.\n",
+		dev->rev);
+
+	return;
+
+case_error:
+	debugfs_remove(dent_ep_pcie);
+}
+
+void ep_pcie_debugfs_exit(void)
+{
+	debugfs_remove(dfile_case);
+	debugfs_remove(dent_ep_pcie);
+}
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c
new file mode 100644
index 0000000..776ef08
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c
@@ -0,0 +1,160 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * MSM PCIe PHY endpoint mode
+ */
+
+#include "ep_pcie_com.h"
+#include "ep_pcie_phy.h"
+
+void ep_pcie_phy_init(struct ep_pcie_dev_t *dev)
+{
+	switch (dev->phy_rev) {
+	case 3:
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PHY V%d: Initializing 20nm QMP phy - 100MHz\n",
+			dev->rev, dev->phy_rev);
+		break;
+	case 4:
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PHY V%d: Initializing 14nm QMP phy - 100MHz\n",
+			dev->rev, dev->phy_rev);
+		break;
+	case 5:
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PHY V%d: Initializing 10nm QMP phy - 100MHz\n",
+			dev->rev, dev->phy_rev);
+		break;
+	default:
+		EP_PCIE_ERR(dev,
+			"PCIe V%d: Unexpected phy version %d is caught!\n",
+			dev->rev, dev->phy_rev);
+	}
+
+	if (dev->phy_init_len && dev->phy_init) {
+		int i;
+		struct ep_pcie_phy_info_t *phy_init;
+
+		EP_PCIE_DBG(dev,
+			"PCIe V%d: PHY V%d: process the sequence specified by DT.\n",
+			dev->rev, dev->phy_rev);
+
+		i =  dev->phy_init_len;
+		phy_init = dev->phy_init;
+		while (i--) {
+			ep_pcie_write_reg(dev->phy,
+				phy_init->offset,
+				phy_init->val);
+			if (phy_init->delay)
+				usleep_range(phy_init->delay,
+					phy_init->delay + 1);
+			phy_init++;
+		}
+		return;
+	}
+
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_SW_RESET, 0x01);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x01);
+
+	/* Common block settings */
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CLK_ENABLE1, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_BG_TRIM, 0x0F);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP_EN, 0x01);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER1, 0xFF);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER2, 0x1F);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CMN_CONFIG, 0x06);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_IVCO, 0x0F);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_HSCLK_SEL, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CORE_CLK_EN, 0x20);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CORECLK_DIV, 0x0A);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_RESETSM_CNTRL, 0x20);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_BG_TIMER, 0x01);
+
+	/* PLL Config Settings */
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_EN_SEL, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_DEC_START_MODE0, 0x19);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP3_MODE0, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x02);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x7F);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CLK_SELECT, 0x30);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_SYS_CLK_CTRL, 0x06);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CP_CTRL_MODE0, 0x3F);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_RCTRL_MODE0, 0x1A);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CCTRL_MODE0, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x03);
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0xFF);
+
+	/* TX settings */
+	ep_pcie_write_reg(dev->phy, QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN,
+				0x45);
+	ep_pcie_write_reg(dev->phy, QSERDES_TX_LANE_MODE, 0x06);
+	ep_pcie_write_reg(dev->phy, QSERDES_TX_RES_CODE_LANE_OFFSET, 0x02);
+	ep_pcie_write_reg(dev->phy, QSERDES_TX_RCV_DETECT_LVL_2, 0x12);
+
+	/* RX settings */
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_ENABLES, 0x1C);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xDB);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE,
+				0x4B);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_UCDR_SO_GAIN, 0x04);
+	ep_pcie_write_reg(dev->phy, QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04);
+
+	/* EP_REF_CLK settings */
+	ep_pcie_write_reg(dev->phy, QSERDES_COM_CLK_EP_DIV, 0x19);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_ENDPOINT_REFCLK_DRIVE, 0x00);
+
+	/* PCIE L1SS settings */
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x40);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB,
+				0x00);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB,
+				0x40);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK_MSB,
+				0x00);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK,	0x40);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_PLL_LOCK_CHK_DLY_TIME, 0x73);
+
+	/* PCS settings */
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_SIGDET_CNTRL, 0x07);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_RX_SIGDET_LVL, 0x99);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_TXDEEMPH_M6DB_V0, 0x15);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_TXDEEMPH_M3P5DB_V0, 0x0E);
+
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_SW_RESET, 0x00);
+	ep_pcie_write_reg(dev->phy, PCIE_PHY_START_CONTROL, 0x03);
+}
+
+bool ep_pcie_phy_is_ready(struct ep_pcie_dev_t *dev)
+{
+	u32 offset;
+
+	if (dev->phy_status_reg)
+		offset = dev->phy_status_reg;
+	else
+		offset = PCIE_PHY_PCS_STATUS;
+
+	if (readl_relaxed(dev->phy + offset) & BIT(6))
+		return false;
+	else
+		return true;
+}
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.h b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h
new file mode 100644
index 0000000..c8f01de
--- /dev/null
+++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h
@@ -0,0 +1,463 @@
+/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __EP_PCIE_PHY_H
+#define __EP_PCIE_PHY_H
+
+#define QSERDES_COM_ATB_SEL1                           0x000
+#define QSERDES_COM_ATB_SEL2                           0x004
+#define QSERDES_COM_FREQ_UPDATE                        0x008
+#define QSERDES_COM_BG_TIMER                           0x00C
+#define QSERDES_COM_SSC_EN_CENTER                      0x010
+#define QSERDES_COM_SSC_ADJ_PER1                       0x014
+#define QSERDES_COM_SSC_ADJ_PER2                       0x018
+#define QSERDES_COM_SSC_PER1                           0x01C
+#define QSERDES_COM_SSC_PER2                           0x020
+#define QSERDES_COM_SSC_STEP_SIZE1                     0x024
+#define QSERDES_COM_SSC_STEP_SIZE2                     0x028
+#define QSERDES_COM_POST_DIV                           0x02C
+#define QSERDES_COM_POST_DIV_MUX                       0x030
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN                0x034
+#define QSERDES_COM_CLK_ENABLE1                        0x038
+#define QSERDES_COM_SYS_CLK_CTRL                       0x03C
+#define QSERDES_COM_SYSCLK_BUF_ENABLE                  0x040
+#define QSERDES_COM_PLL_EN                             0x044
+#define QSERDES_COM_PLL_IVCO                           0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0                    0x04C
+#define QSERDES_COM_LOCK_CMP2_MODE0                    0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0                    0x054
+#define QSERDES_COM_LOCK_CMP1_MODE1                    0x058
+#define QSERDES_COM_LOCK_CMP2_MODE1                    0x05C
+#define QSERDES_COM_LOCK_CMP3_MODE1                    0x060
+#define QSERDES_COM_CMN_RSVD0                          0x064
+#define QSERDES_COM_EP_CLOCK_DETECT_CTRL               0x068
+#define QSERDES_COM_SYSCLK_DET_COMP_STATUS             0x06C
+#define QSERDES_COM_BG_TRIM                            0x070
+#define QSERDES_COM_CLK_EP_DIV                         0x074
+#define QSERDES_COM_CP_CTRL_MODE0                      0x078
+#define QSERDES_COM_CP_CTRL_MODE1                      0x07C
+#define QSERDES_COM_CMN_RSVD1                          0x080
+#define QSERDES_COM_PLL_RCTRL_MODE0                    0x084
+#define QSERDES_COM_PLL_RCTRL_MODE1                    0x088
+#define QSERDES_COM_CMN_RSVD2                          0x08C
+#define QSERDES_COM_PLL_CCTRL_MODE0                    0x090
+#define QSERDES_COM_PLL_CCTRL_MODE1                    0x094
+#define QSERDES_COM_CMN_RSVD3                          0x098
+#define QSERDES_COM_PLL_CNTRL                          0x09C
+#define QSERDES_COM_PHASE_SEL_CTRL                     0x0A0
+#define QSERDES_COM_PHASE_SEL_DC                       0x0A4
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM                0x0A8
+#define QSERDES_COM_SYSCLK_EN_SEL                      0x0AC
+#define QSERDES_COM_CML_SYSCLK_SEL                     0x0B0
+#define QSERDES_COM_RESETSM_CNTRL                      0x0B4
+#define QSERDES_COM_RESETSM_CNTRL2                     0x0B8
+#define QSERDES_COM_RESTRIM_CTRL                       0x0BC
+#define QSERDES_COM_RESTRIM_CTRL2                      0x0C0
+#define QSERDES_COM_RESCODE_DIV_NUM                    0x0C4
+#define QSERDES_COM_LOCK_CMP_EN                        0x0C8
+#define QSERDES_COM_LOCK_CMP_CFG                       0x0CC
+#define QSERDES_COM_DEC_START_MODE0                    0x0D0
+#define QSERDES_COM_DEC_START_MODE1                    0x0D4
+#define QSERDES_COM_VCOCAL_DEADMAN_CTRL                0x0D8
+#define QSERDES_COM_DIV_FRAC_START1_MODE0              0x0DC
+#define QSERDES_COM_DIV_FRAC_START2_MODE0              0x0E0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0              0x0E4
+#define QSERDES_COM_DIV_FRAC_START1_MODE1              0x0E8
+#define QSERDES_COM_DIV_FRAC_START2_MODE1              0x0EC
+#define QSERDES_COM_DIV_FRAC_START3_MODE1              0x0F0
+#define QSERDES_COM_VCO_TUNE_MINVAL1                   0x0F4
+#define QSERDES_COM_VCO_TUNE_MINVAL2                   0x0F8
+#define QSERDES_COM_CMN_RSVD4                          0x0FC
+#define QSERDES_COM_INTEGLOOP_INITVAL                  0x100
+#define QSERDES_COM_INTEGLOOP_EN                       0x104
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0              0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0              0x10C
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1              0x110
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1              0x114
+#define QSERDES_COM_VCO_TUNE_MAXVAL1                   0x118
+#define QSERDES_COM_VCO_TUNE_MAXVAL2                   0x11C
+#define QSERDES_COM_RES_TRIM_CONTROL2                  0x120
+#define QSERDES_COM_VCO_TUNE_CTRL                      0x124
+#define QSERDES_COM_VCO_TUNE_MAP                       0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0                    0x12C
+#define QSERDES_COM_VCO_TUNE2_MODE0                    0x130
+#define QSERDES_COM_VCO_TUNE1_MODE1                    0x134
+#define QSERDES_COM_VCO_TUNE2_MODE1                    0x138
+#define QSERDES_COM_VCO_TUNE_INITVAL1                  0x13C
+#define QSERDES_COM_VCO_TUNE_INITVAL2                  0x140
+#define QSERDES_COM_VCO_TUNE_TIMER1                    0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2                    0x148
+#define QSERDES_COM_SAR                                0x14C
+#define QSERDES_COM_SAR_CLK                            0x150
+#define QSERDES_COM_SAR_CODE_OUT_STATUS                0x154
+#define QSERDES_COM_SAR_CODE_READY_STATUS              0x158
+#define QSERDES_COM_CMN_STATUS                         0x15C
+#define QSERDES_COM_RESET_SM_STATUS                    0x160
+#define QSERDES_COM_RESTRIM_CODE_STATUS                0x164
+#define QSERDES_COM_PLLCAL_CODE1_STATUS                0x168
+#define QSERDES_COM_PLLCAL_CODE2_STATUS                0x16C
+#define QSERDES_COM_BG_CTRL                            0x170
+#define QSERDES_COM_CLK_SELECT                         0x174
+#define QSERDES_COM_HSCLK_SEL                          0x178
+#define QSERDES_COM_PLL_ANALOG                         0x180
+#define QSERDES_COM_CORECLK_DIV                        0x184
+#define QSERDES_COM_SW_RESET                           0x188
+#define QSERDES_COM_CORE_CLK_EN                        0x18C
+#define QSERDES_COM_C_READY_STATUS                     0x190
+#define QSERDES_COM_CMN_CONFIG                         0x194
+#define QSERDES_COM_CMN_RATE_OVERRIDE                  0x198
+#define QSERDES_COM_SVS_MODE_CLK_SEL                   0x19C
+#define QSERDES_COM_DEBUG_BUS0                         0x1A0
+#define QSERDES_COM_DEBUG_BUS1                         0x1A4
+#define QSERDES_COM_DEBUG_BUS2                         0x1A8
+#define QSERDES_COM_DEBUG_BUS3                         0x1AC
+#define QSERDES_COM_DEBUG_BUS_SEL                      0x1B0
+#define QSERDES_COM_CMN_MISC1                          0x1B4
+#define QSERDES_COM_CMN_MISC2                          0x1B8
+#define QSERDES_COM_CORECLK_DIV_MODE1                  0x1BC
+#define QSERDES_COM_CMN_RSVD5                          0x1C0
+#define QSERDES_TX_BIST_MODE_LANENO                    0x200
+#define QSERDES_TX_BIST_INVERT                         0x204
+#define QSERDES_TX_CLKBUF_ENABLE                       0x208
+#define QSERDES_TX_CMN_CONTROL_ONE                     0x20C
+#define QSERDES_TX_CMN_CONTROL_TWO                     0x210
+#define QSERDES_TX_CMN_CONTROL_THREE                   0x214
+#define QSERDES_TX_TX_EMP_POST1_LVL                    0x218
+#define QSERDES_TX_TX_POST2_EMPH                       0x21C
+#define QSERDES_TX_TX_BOOST_LVL_UP_DN                  0x220
+#define QSERDES_TX_HP_PD_ENABLES                       0x224
+#define QSERDES_TX_TX_IDLE_LVL_LARGE_AMP               0x228
+#define QSERDES_TX_TX_DRV_LVL                          0x22C
+#define QSERDES_TX_TX_DRV_LVL_OFFSET                   0x230
+#define QSERDES_TX_RESET_TSYNC_EN                      0x234
+#define QSERDES_TX_PRE_STALL_LDO_BOOST_EN              0x238
+#define QSERDES_TX_TX_BAND                             0x23C
+#define QSERDES_TX_SLEW_CNTL                           0x240
+#define QSERDES_TX_INTERFACE_SELECT                    0x244
+#define QSERDES_TX_LPB_EN                              0x248
+#define QSERDES_TX_RES_CODE_LANE_TX                    0x24C
+#define QSERDES_TX_RES_CODE_LANE_RX                    0x250
+#define QSERDES_TX_RES_CODE_LANE_OFFSET                0x254
+#define QSERDES_TX_PERL_LENGTH1                        0x258
+#define QSERDES_TX_PERL_LENGTH2                        0x25C
+#define QSERDES_TX_SERDES_BYP_EN_OUT                   0x260
+#define QSERDES_TX_DEBUG_BUS_SEL                       0x264
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN    0x268
+#define QSERDES_TX_TX_POL_INV                          0x26C
+#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN          0x270
+#define QSERDES_TX_BIST_PATTERN1                       0x274
+#define QSERDES_TX_BIST_PATTERN2                       0x278
+#define QSERDES_TX_BIST_PATTERN3                       0x27C
+#define QSERDES_TX_BIST_PATTERN4                       0x280
+#define QSERDES_TX_BIST_PATTERN5                       0x284
+#define QSERDES_TX_BIST_PATTERN6                       0x288
+#define QSERDES_TX_BIST_PATTERN7                       0x28C
+#define QSERDES_TX_BIST_PATTERN8                       0x290
+#define QSERDES_TX_LANE_MODE                           0x294
+#define QSERDES_TX_IDAC_CAL_LANE_MODE                  0x298
+#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION    0x29C
+#define QSERDES_TX_ATB_SEL1                            0x2A0
+#define QSERDES_TX_ATB_SEL2                            0x2A4
+#define QSERDES_TX_RCV_DETECT_LVL                      0x2A8
+#define QSERDES_TX_RCV_DETECT_LVL_2                    0x2AC
+#define QSERDES_TX_PRBS_SEED1                          0x2B0
+#define QSERDES_TX_PRBS_SEED2                          0x2B4
+#define QSERDES_TX_PRBS_SEED3                          0x2B8
+#define QSERDES_TX_PRBS_SEED4                          0x2BC
+#define QSERDES_TX_RESET_GEN                           0x2C0
+#define QSERDES_TX_RESET_GEN_MUXES                     0x2C4
+#define QSERDES_TX_TRAN_DRVR_EMP_EN                    0x2C8
+#define QSERDES_TX_TX_INTERFACE_MODE                   0x2CC
+#define QSERDES_TX_PWM_CTRL                            0x2D0
+#define QSERDES_TX_PWM_ENCODED_OR_DATA                 0x2D4
+#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND2            0x2D8
+#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND2            0x2DC
+#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND2            0x2E0
+#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND2            0x2E4
+#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND0_1          0x2E8
+#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND0_1          0x2EC
+#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND0_1          0x2F0
+#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND0_1          0x2F4
+#define QSERDES_TX_VMODE_CTRL1                         0x2F8
+#define QSERDES_TX_VMODE_CTRL2                         0x2FC
+#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL              0x300
+#define QSERDES_TX_BIST_STATUS                         0x304
+#define QSERDES_TX_BIST_ERROR_COUNT1                   0x308
+#define QSERDES_TX_BIST_ERROR_COUNT2                   0x30C
+#define QSERDES_TX_TX_ALOG_INTF_OBSV                   0x310
+#define QSERDES_RX_UCDR_FO_GAIN_HALF                   0x400
+#define QSERDES_RX_UCDR_FO_GAIN_QUARTER                0x404
+#define QSERDES_RX_UCDR_FO_GAIN_EIGHTH                 0x408
+#define QSERDES_RX_UCDR_FO_GAIN                        0x40C
+#define QSERDES_RX_UCDR_SO_GAIN_HALF                   0x410
+#define QSERDES_RX_UCDR_SO_GAIN_QUARTER                0x414
+#define QSERDES_RX_UCDR_SO_GAIN_EIGHTH                 0x418
+#define QSERDES_RX_UCDR_SO_GAIN                        0x41C
+#define QSERDES_RX_UCDR_SVS_FO_GAIN_HALF               0x420
+#define QSERDES_RX_UCDR_SVS_FO_GAIN_QUARTER            0x424
+#define QSERDES_RX_UCDR_SVS_FO_GAIN_EIGHTH             0x428
+#define QSERDES_RX_UCDR_SVS_FO_GAIN                    0x42C
+#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF               0x430
+#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER            0x434
+#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH             0x438
+#define QSERDES_RX_UCDR_SVS_SO_GAIN                    0x43C
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN               0x440
+#define QSERDES_RX_UCDR_FD_GAIN                        0x444
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE       0x448
+#define QSERDES_RX_UCDR_FO_TO_SO_DELAY                 0x44C
+#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW             0x450
+#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH            0x454
+#define QSERDES_RX_UCDR_MODULATE                       0x458
+#define QSERDES_RX_UCDR_PI_CONTROLS                    0x45C
+#define QSERDES_RX_RBIST_CONTROL                       0x460
+#define QSERDES_RX_AUX_CONTROL                         0x464
+#define QSERDES_RX_AUX_DATA_TCOARSE                    0x468
+#define QSERDES_RX_AUX_DATA_TFINE_LSB                  0x46C
+#define QSERDES_RX_AUX_DATA_TFINE_MSB                  0x470
+#define QSERDES_RX_RCLK_AUXDATA_SEL                    0x474
+#define QSERDES_RX_AC_JTAG_ENABLE                      0x478
+#define QSERDES_RX_AC_JTAG_INITP                       0x47C
+#define QSERDES_RX_AC_JTAG_INITN                       0x480
+#define QSERDES_RX_AC_JTAG_LVL                         0x484
+#define QSERDES_RX_AC_JTAG_MODE                        0x488
+#define QSERDES_RX_AC_JTAG_RESET                       0x48C
+#define QSERDES_RX_RX_TERM_BW                          0x490
+#define QSERDES_RX_RX_RCVR_IQ_EN                       0x494
+#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS                0x498
+#define QSERDES_RX_RX_IDAC_IBAR_DC_OFFSETS             0x49C
+#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS                0x4A0
+#define QSERDES_RX_RX_IDAC_QBAR_DC_OFFSETS             0x4A4
+#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS                0x4A8
+#define QSERDES_RX_RX_IDAC_ABAR_DC_OFFSETS             0x4AC
+#define QSERDES_RX_RX_IDAC_EN                          0x4B0
+#define QSERDES_RX_RX_IDAC_ENABLES                     0x4B4
+#define QSERDES_RX_RX_IDAC_SIGN                        0x4B8
+#define QSERDES_RX_RX_HIGHZ_HIGHRATE                   0x4BC
+#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET  0x4C0
+#define QSERDES_RX_RX_EQ_GAIN1_LSB                     0x4C4
+#define QSERDES_RX_RX_EQ_GAIN1_MSB                     0x4C8
+#define QSERDES_RX_RX_EQ_GAIN2_LSB                     0x4CC
+#define QSERDES_RX_RX_EQ_GAIN2_MSB                     0x4D0
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1               0x4D4
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2               0x4D8
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3               0x4DC
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4               0x4E0
+#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION           0x4E4
+#define QSERDES_RX_RX_IDAC_TSETTLE_LOW                 0x4E8
+#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH                0x4EC
+#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW                 0x4F0
+#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH                0x4F4
+#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW                0x4F8
+#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH               0x4FC
+#define QSERDES_RX_RX_EQ_OFFSET_LSB                    0x500
+#define QSERDES_RX_RX_EQ_OFFSET_MSB                    0x504
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1         0x508
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2            0x50C
+#define QSERDES_RX_SIGDET_ENABLES                      0x510
+#define QSERDES_RX_SIGDET_CNTRL                        0x514
+#define QSERDES_RX_SIGDET_LVL                          0x518
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL               0x51C
+#define QSERDES_RX_RX_BAND                             0x520
+#define QSERDES_RX_CDR_FREEZE_UP_DN                    0x524
+#define QSERDES_RX_CDR_RESET_OVERRIDE                  0x528
+#define QSERDES_RX_RX_INTERFACE_MODE                   0x52C
+#define QSERDES_RX_JITTER_GEN_MODE                     0x530
+#define QSERDES_RX_BUJ_AMP                             0x534
+#define QSERDES_RX_SJ_AMP1                             0x538
+#define QSERDES_RX_SJ_AMP2                             0x53C
+#define QSERDES_RX_SJ_PER1                             0x540
+#define QSERDES_RX_SJ_PER2                             0x544
+#define QSERDES_RX_BUJ_STEP_FREQ1                      0x548
+#define QSERDES_RX_BUJ_STEP_FREQ2                      0x54C
+#define QSERDES_RX_PPM_OFFSET1                         0x550
+#define QSERDES_RX_PPM_OFFSET2                         0x554
+#define QSERDES_RX_SIGN_PPM_PERIOD1                    0x558
+#define QSERDES_RX_SIGN_PPM_PERIOD2                    0x55C
+#define QSERDES_RX_SSC_CTRL                            0x560
+#define QSERDES_RX_SSC_COUNT1                          0x564
+#define QSERDES_RX_SSC_COUNT2                          0x568
+#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL              0x56C
+#define QSERDES_RX_RX_PWM_ENABLE_AND_DATA              0x570
+#define QSERDES_RX_RX_PWM_GEAR1_TIMEOUT_COUNT          0x574
+#define QSERDES_RX_RX_PWM_GEAR2_TIMEOUT_COUNT          0x578
+#define QSERDES_RX_RX_PWM_GEAR3_TIMEOUT_COUNT          0x57C
+#define QSERDES_RX_RX_PWM_GEAR4_TIMEOUT_COUNT          0x580
+#define QSERDES_RX_PI_CTRL1                            0x584
+#define QSERDES_RX_PI_CTRL2                            0x588
+#define QSERDES_RX_PI_QUAD                             0x58C
+#define QSERDES_RX_IDATA1                              0x590
+#define QSERDES_RX_IDATA2                              0x594
+#define QSERDES_RX_AUX_DATA1                           0x598
+#define QSERDES_RX_AUX_DATA2                           0x59C
+#define QSERDES_RX_AC_JTAG_OUTP                        0x5A0
+#define QSERDES_RX_AC_JTAG_OUTN                        0x5A4
+#define QSERDES_RX_RX_SIGDET                           0x5A8
+#define QSERDES_RX_RX_VDCOFF                           0x5AC
+#define QSERDES_RX_IDAC_CAL_ON                         0x5B0
+#define QSERDES_RX_IDAC_STATUS_I                       0x5B4
+#define QSERDES_RX_IDAC_STATUS_IBAR                    0x5B8
+#define QSERDES_RX_IDAC_STATUS_Q                       0x5BC
+#define QSERDES_RX_IDAC_STATUS_QBAR                    0x5C0
+#define QSERDES_RX_IDAC_STATUS_A                       0x5C4
+#define QSERDES_RX_IDAC_STATUS_ABAR                    0x5C8
+#define QSERDES_RX_CALST_STATUS_I                      0x5CC
+#define QSERDES_RX_CALST_STATUS_Q                      0x5D0
+#define QSERDES_RX_CALST_STATUS_A                      0x5D4
+#define QSERDES_RX_RX_ALOG_INTF_OBSV                   0x5D8
+#define QSERDES_RX_READ_EQCODE                         0x5DC
+#define QSERDES_RX_READ_OFFSETCODE                     0x5E0
+#define QSERDES_RX_IA_ERROR_COUNTER_LOW                0x5E4
+#define QSERDES_RX_IA_ERROR_COUNTER_HIGH               0x5E8
+#define PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX            0x600
+#define PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX            0x604
+#define PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX            0x608
+#define PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX            0x60C
+#define PCIE_PHY_MISC_PLACEHOLDER_STATUS               0x610
+#define PCIE_PHY_MISC_DEBUG_BUS_0_STATUS               0x614
+#define PCIE_PHY_MISC_DEBUG_BUS_1_STATUS               0x618
+#define PCIE_PHY_MISC_DEBUG_BUS_2_STATUS               0x61C
+#define PCIE_PHY_MISC_DEBUG_BUS_3_STATUS               0x620
+#define PCIE_PHY_MISC_OSC_DTCT_STATUS                  0x624
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG1                 0x628
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG2                 0x62C
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG3                 0x630
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG4                 0x634
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG5                 0x638
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG6                 0x63C
+#define PCIE_PHY_MISC_OSC_DTCT_CONFIG7                 0x640
+#define PCIE_PHY_SW_RESET                              0x800
+#define PCIE_PHY_POWER_DOWN_CONTROL                    0x804
+#define PCIE_PHY_START_CONTROL                         0x808
+#define PCIE_PHY_TXMGN_V0                              0x80C
+#define PCIE_PHY_TXMGN_V1                              0x810
+#define PCIE_PHY_TXMGN_V2                              0x814
+#define PCIE_PHY_TXMGN_V3                              0x818
+#define PCIE_PHY_TXMGN_V4                              0x81C
+#define PCIE_PHY_TXMGN_LS                              0x820
+#define PCIE_PHY_TXDEEMPH_M6DB_V0                      0x824
+#define PCIE_PHY_TXDEEMPH_M3P5DB_V0                    0x828
+#define PCIE_PHY_TXDEEMPH_M6DB_V1                      0x82C
+#define PCIE_PHY_TXDEEMPH_M3P5DB_V1                    0x830
+#define PCIE_PHY_TXDEEMPH_M6DB_V2                      0x834
+#define PCIE_PHY_TXDEEMPH_M3P5DB_V2                    0x838
+#define PCIE_PHY_TXDEEMPH_M6DB_V3                      0x83C
+#define PCIE_PHY_TXDEEMPH_M3P5DB_V3                    0x840
+#define PCIE_PHY_TXDEEMPH_M6DB_V4                      0x844
+#define PCIE_PHY_TXDEEMPH_M3P5DB_V4                    0x848
+#define PCIE_PHY_TXDEEMPH_M6DB_LS                      0x84C
+#define PCIE_PHY_TXDEEMPH_M3P5DB_LS                    0x850
+#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE                 0x854
+#define PCIE_PHY_RX_IDLE_DTCT_CNTRL                    0x858
+#define PCIE_PHY_RATE_SLEW_CNTRL                       0x85C
+#define PCIE_PHY_POWER_STATE_CONFIG1                   0x860
+#define PCIE_PHY_POWER_STATE_CONFIG2                   0x864
+#define PCIE_PHY_POWER_STATE_CONFIG3                   0x868
+#define PCIE_PHY_POWER_STATE_CONFIG4                   0x86C
+#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_L                  0x870
+#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_H                  0x874
+#define PCIE_PHY_RCVR_DTCT_DLY_U3_L                    0x878
+#define PCIE_PHY_RCVR_DTCT_DLY_U3_H                    0x87C
+#define PCIE_PHY_LOCK_DETECT_CONFIG1                   0x880
+#define PCIE_PHY_LOCK_DETECT_CONFIG2                   0x884
+#define PCIE_PHY_LOCK_DETECT_CONFIG3                   0x888
+#define PCIE_PHY_TSYNC_RSYNC_TIME                      0x88C
+#define PCIE_PHY_SIGDET_LOW_2_IDLE_TIME                0x890
+#define PCIE_PHY_BEACON_2_IDLE_TIME_L                  0x894
+#define PCIE_PHY_BEACON_2_IDLE_TIME_H                  0x898
+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK           0x89C
+#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK           0x8A0
+#define PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK             0x8A4
+#define PCIE_PHY_PLL_LOCK_CHK_DLY_TIME                 0x8A8
+#define PCIE_PHY_LFPS_DET_HIGH_COUNT_VAL               0x8AC
+#define PCIE_PHY_LFPS_TX_ECSTART_EQTLOCK               0x8B0
+#define PCIE_PHY_LFPS_TX_END_CNT_P2U3_START            0x8B4
+#define PCIE_PHY_RXEQTRAINING_WAIT_TIME                0x8B8
+#define PCIE_PHY_RXEQTRAINING_RUN_TIME                 0x8BC
+#define PCIE_PHY_TXONESZEROS_RUN_LENGTH                0x8C0
+#define PCIE_PHY_FLL_CNTRL1                            0x8C4
+#define PCIE_PHY_FLL_CNTRL2                            0x8C8
+#define PCIE_PHY_FLL_CNT_VAL_L                         0x8CC
+#define PCIE_PHY_FLL_CNT_VAL_H_TOL                     0x8D0
+#define PCIE_PHY_FLL_MAN_CODE                          0x8D4
+#define PCIE_PHY_AUTONOMOUS_MODE_CTRL                  0x8D8
+#define PCIE_PHY_LFPS_RXTERM_IRQ_CLEAR                 0x8DC
+#define PCIE_PHY_ARCVR_DTCT_EN_PERIOD                  0x8E0
+#define PCIE_PHY_ARCVR_DTCT_CM_DLY                     0x8E4
+#define PCIE_PHY_ALFPS_DEGLITCH_VAL                    0x8E8
+#define PCIE_PHY_INSIG_SW_CTRL1                        0x8EC
+#define PCIE_PHY_INSIG_SW_CTRL2                        0x8F0
+#define PCIE_PHY_INSIG_SW_CTRL3                        0x8F4
+#define PCIE_PHY_INSIG_MX_CTRL1                        0x8F8
+#define PCIE_PHY_INSIG_MX_CTRL2                        0x8FC
+#define PCIE_PHY_INSIG_MX_CTRL3                        0x900
+#define PCIE_PHY_OUTSIG_SW_CTRL1                       0x904
+#define PCIE_PHY_OUTSIG_MX_CTRL1                       0x908
+#define PCIE_PHY_CLK_DEBUG_BYPASS_CTRL                 0x90C
+#define PCIE_PHY_TEST_CONTROL                          0x910
+#define PCIE_PHY_TEST_CONTROL2                         0x914
+#define PCIE_PHY_TEST_CONTROL3                         0x918
+#define PCIE_PHY_TEST_CONTROL4                         0x91C
+#define PCIE_PHY_TEST_CONTROL5                         0x920
+#define PCIE_PHY_TEST_CONTROL6                         0x924
+#define PCIE_PHY_TEST_CONTROL7                         0x928
+#define PCIE_PHY_COM_RESET_CONTROL                     0x92C
+#define PCIE_PHY_BIST_CTRL                             0x930
+#define PCIE_PHY_PRBS_POLY0                            0x934
+#define PCIE_PHY_PRBS_POLY1                            0x938
+#define PCIE_PHY_PRBS_SEED0                            0x93C
+#define PCIE_PHY_PRBS_SEED1                            0x940
+#define PCIE_PHY_FIXED_PAT_CTRL                        0x944
+#define PCIE_PHY_FIXED_PAT0                            0x948
+#define PCIE_PHY_FIXED_PAT1                            0x94C
+#define PCIE_PHY_FIXED_PAT2                            0x950
+#define PCIE_PHY_FIXED_PAT3                            0x954
+#define PCIE_PHY_COM_CLK_SWITCH_CTRL                   0x958
+#define PCIE_PHY_ELECIDLE_DLY_SEL                      0x95C
+#define PCIE_PHY_SPARE1                                0x960
+#define PCIE_PHY_BIST_CHK_ERR_CNT_L_STATUS             0x964
+#define PCIE_PHY_BIST_CHK_ERR_CNT_H_STATUS             0x968
+#define PCIE_PHY_BIST_CHK_STATUS                       0x96C
+#define PCIE_PHY_LFPS_RXTERM_IRQ_SOURCE_STATUS         0x970
+#define PCIE_PHY_PCS_STATUS                            0x974
+#define PCIE_PHY_PCS_STATUS2                           0x978
+#define PCIE_PHY_PCS_STATUS3                           0x97C
+#define PCIE_PHY_COM_RESET_STATUS                      0x980
+#define PCIE_PHY_OSC_DTCT_STATUS                       0x984
+#define PCIE_PHY_REVISION_ID0                          0x988
+#define PCIE_PHY_REVISION_ID1                          0x98C
+#define PCIE_PHY_REVISION_ID2                          0x990
+#define PCIE_PHY_REVISION_ID3                          0x994
+#define PCIE_PHY_DEBUG_BUS_0_STATUS                    0x998
+#define PCIE_PHY_DEBUG_BUS_1_STATUS                    0x99C
+#define PCIE_PHY_DEBUG_BUS_2_STATUS                    0x9A0
+#define PCIE_PHY_DEBUG_BUS_3_STATUS                    0x9A4
+#define PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK_MSB         0x9A8
+#define PCIE_PHY_OSC_DTCT_ACTIONS                      0x9AC
+#define PCIE_PHY_SIGDET_CNTRL                          0x9B0
+#define PCIE_PHY_IDAC_CAL_CNTRL                        0x9B4
+#define PCIE_PHY_CMN_ACK_OUT_SEL                       0x9B8
+#define PCIE_PHY_PLL_LOCK_CHK_DLY_TIME_SYSCLK          0x9BC
+#define PCIE_PHY_AUTONOMOUS_MODE_STATUS                0x9C0
+#define PCIE_PHY_ENDPOINT_REFCLK_CNTRL                 0x9C4
+#define PCIE_PHY_EPCLK_PRE_PLL_LOCK_DLY_SYSCLK         0x9C8
+#define PCIE_PHY_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK         0x9CC
+#define PCIE_PHY_EPCLK_DLY_COUNT_VAL_L                 0x9D0
+#define PCIE_PHY_EPCLK_DLY_COUNT_VAL_H                 0x9D4
+#define PCIE_PHY_RX_SIGDET_LVL                         0x9D8
+#define PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB       0x9DC
+#define PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB       0x9E0
+#define PCIE_PHY_AUTONOMOUS_MODE_CTRL2                 0x9E4
+#define PCIE_PHY_RXTERMINATION_DLY_SEL                 0x9E8
+#define PCIE_PHY_LFPS_PER_TIMER_VAL                    0x9EC
+#define PCIE_PHY_SIGDET_STARTUP_TIMER_VAL              0x9F0
+#define PCIE_PHY_LOCK_DETECT_CONFIG4                   0x9F4
+#endif
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 96b9bd6..7df312e 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -3135,6 +3135,17 @@
 }
 EXPORT_SYMBOL(ipa_ntn_uc_dereg_rdyCB);
 
+int ipa_get_smmu_params(struct ipa_smmu_in_params *in,
+	struct ipa_smmu_out_params *out)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_get_smmu_params, in, out);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_get_smmu_params);
+
 /**
  * ipa_conn_wdi3_pipes() - connect wdi3 pipes
  */
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index b526711..0779f34 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -417,6 +417,9 @@
 
 	int (*ipa_tz_unlock_reg)(struct ipa_tz_unlock_reg_info *reg_info,
 		u16 num_regs);
+
+	int (*ipa_get_smmu_params)(struct ipa_smmu_in_params *in,
+		struct ipa_smmu_out_params *out);
 };
 
 #ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 59d93f3..b615ec8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -6405,5 +6405,39 @@
 	return iommu_map(domain, iova, paddr, size, prot);
 }
 
+/**
+ * ipa3_get_smmu_params()- Return the ipa3 smmu related params.
+ */
+int ipa3_get_smmu_params(struct ipa_smmu_in_params *in,
+	struct ipa_smmu_out_params *out)
+{
+	bool is_smmu_enable = 0;
+
+	if (out == NULL || in == NULL) {
+		IPAERR("bad parms for Client SMMU out params\n");
+		return -EINVAL;
+	}
+
+	if (!ipa3_ctx) {
+		IPAERR("IPA not yet initialized\n");
+		return -EINVAL;
+	}
+
+	switch (in->smmu_client) {
+	case IPA_SMMU_WLAN_CLIENT:
+		is_smmu_enable = !(ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC] |
+			ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]);
+		break;
+	default:
+		is_smmu_enable = 0;
+		IPAERR("Trying to get illegal clients SMMU status");
+		return -EINVAL;
+	}
+
+	out->smmu_enable = is_smmu_enable;
+
+	return 0;
+}
+
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("IPA HW device driver");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 3eff209..3754aa8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -2021,6 +2021,9 @@
 
 u8 ipa3_get_qmb_master_sel(enum ipa_client_type client);
 
+int ipa3_get_smmu_params(struct ipa_smmu_in_params *in,
+	struct ipa_smmu_out_params *out);
+
 /* internal functions */
 
 int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 065a099..fb29d00 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -4503,6 +4503,7 @@
 	api_ctrl->ipa_enable_wdi3_pipes = ipa3_enable_wdi3_pipes;
 	api_ctrl->ipa_disable_wdi3_pipes = ipa3_disable_wdi3_pipes;
 	api_ctrl->ipa_tz_unlock_reg = ipa3_tz_unlock_reg;
+	api_ctrl->ipa_get_smmu_params = ipa3_get_smmu_params;
 
 	return 0;
 }
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 21f8d2eda..f64e9de 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -1086,6 +1086,10 @@
 	ctx->keep_radio_on_during_sleep = of_property_read_bool(of_node,
 		"qcom,keep-radio-on-during-sleep");
 	ctx->bus_scale = msm_bus_cl_get_pdata(pdev);
+	if (!ctx->bus_scale) {
+		dev_err(ctx->dev, "Unable to read bus-scaling from DT\n");
+		return -EINVAL;
+	}
 
 	ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en");
 	if (ctx->smmu_s1_en) {
@@ -1114,7 +1118,7 @@
 	rc = msm_11ad_init_vregs(ctx);
 	if (rc) {
 		dev_err(ctx->dev, "msm_11ad_init_vregs failed: %d\n", rc);
-		return rc;
+		goto out_bus_scale;
 	}
 	rc = msm_11ad_enable_vregs(ctx);
 	if (rc) {
@@ -1173,6 +1177,18 @@
 	}
 	ctx->pcidev = pcidev;
 
+	rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
+				 pcidev, NULL, 0);
+	if (rc) {
+		dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed:%d\n",
+			rc);
+		goto out_rc;
+	}
+
+	pci_set_power_state(pcidev, PCI_D0);
+
+	pci_restore_state(ctx->pcidev);
+
 	/* Read current state */
 	rc = pci_read_config_dword(pcidev,
 				   PCIE20_CAP_LINKCTRLSTATUS, &val);
@@ -1180,7 +1196,7 @@
 		dev_err(ctx->dev,
 			"reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
 			rc);
-		goto out_rc;
+		goto out_suspend;
 	}
 
 	ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1;
@@ -1193,7 +1209,7 @@
 		if (rc) {
 			dev_err(ctx->dev,
 				"failed to disable L1, rc %d\n", rc);
-			goto out_rc;
+			goto out_suspend;
 		}
 	}
 
@@ -1213,7 +1229,7 @@
 	rc = msm_11ad_ssr_init(ctx);
 	if (rc) {
 		dev_err(ctx->dev, "msm_11ad_ssr_init failed: %d\n", rc);
-		goto out_rc;
+		goto out_suspend;
 	}
 
 	msm_11ad_init_cpu_boost(ctx);
@@ -1235,6 +1251,9 @@
 	msm_11ad_suspend_power_off(ctx);
 
 	return 0;
+out_suspend:
+	msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
+			    pcidev, NULL, 0);
 out_rc:
 	if (ctx->gpio_en >= 0)
 		gpio_direction_output(ctx->gpio_en, 0);
@@ -1248,6 +1267,8 @@
 	msm_11ad_release_clocks(ctx);
 	msm_11ad_disable_vregs(ctx);
 	msm_11ad_release_vregs(ctx);
+out_bus_scale:
+	msm_bus_cl_clear_pdata(ctx->bus_scale);
 
 	return rc;
 }
@@ -1262,7 +1283,6 @@
 		 ctx->pcidev);
 	kfree(ctx->pristine_state);
 
-	msm_bus_cl_clear_pdata(ctx->bus_scale);
 	pci_dev_put(ctx->pcidev);
 	if (ctx->gpio_en >= 0) {
 		gpio_direction_output(ctx->gpio_en, 0);
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index c090b2a..bfc401a 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -33,6 +33,7 @@
 #include <soc/qcom/scm.h>
 #include <soc/qcom/restart.h>
 #include <soc/qcom/watchdog.h>
+#include <soc/qcom/minidump.h>
 
 #define EMERGENCY_DLOAD_MAGIC1    0x322A4F99
 #define EMERGENCY_DLOAD_MAGIC2    0xC67E4350
@@ -42,10 +43,11 @@
 #define SCM_IO_DISABLE_PMIC_ARBITER	1
 #define SCM_IO_DEASSERT_PS_HOLD		2
 #define SCM_WDOG_DEBUG_BOOT_PART	0x9
-#define SCM_DLOAD_MODE			0X10
+#define SCM_DLOAD_FULLDUMP		0X10
 #define SCM_EDLOAD_MODE			0X01
 #define SCM_DLOAD_CMD			0x10
-
+#define SCM_DLOAD_MINIDUMP		0X20
+#define SCM_DLOAD_BOTHDUMPS	(SCM_DLOAD_MINIDUMP | SCM_DLOAD_FULLDUMP)
 
 static int restart_mode;
 static void __iomem *restart_reason, *dload_type_addr;
@@ -65,6 +67,7 @@
 #endif
 
 static int in_panic;
+static int dload_type = SCM_DLOAD_FULLDUMP;
 static void *dload_mode_addr;
 static bool dload_mode_enabled;
 static void *emergency_dload_mode_addr;
@@ -137,7 +140,7 @@
 		mb();
 	}
 
-	ret = scm_set_dload_mode(on ? SCM_DLOAD_MODE : 0, 0);
+	ret = scm_set_dload_mode(on ? dload_type : 0, 0);
 	if (ret)
 		pr_err("Failed to set secure DLOAD mode: %d\n", ret);
 
@@ -452,6 +455,9 @@
 {
 	uint32_t read_val, show_val;
 
+	if (!dload_type_addr)
+		return -ENODEV;
+
 	read_val = __raw_readl(dload_type_addr);
 	if (read_val == EMMC_DLOAD_TYPE)
 		show_val = 1;
@@ -467,6 +473,9 @@
 	uint32_t enabled;
 	int ret;
 
+	if (!dload_type_addr)
+		return -ENODEV;
+
 	ret = kstrtouint(buf, 0, &enabled);
 	if (ret < 0)
 		return ret;
@@ -481,10 +490,57 @@
 
 	return count;
 }
+
+#ifdef CONFIG_QCOM_MINIDUMP
+static DEFINE_MUTEX(tcsr_lock);
+
+static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr,
+				char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n",
+		(dload_type == SCM_DLOAD_BOTHDUMPS) ? "both" :
+		((dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full"));
+}
+
+static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr,
+				const char *buf, size_t count)
+{
+	if (sysfs_streq(buf, "full")) {
+		dload_type = SCM_DLOAD_FULLDUMP;
+	} else if (sysfs_streq(buf, "mini")) {
+		if (!msm_minidump_enabled()) {
+			pr_err("Minidump is not enabled\n");
+			return -ENODEV;
+		}
+		dload_type = SCM_DLOAD_MINIDUMP;
+	} else if (sysfs_streq(buf, "both")) {
+		if (!msm_minidump_enabled()) {
+			pr_err("Minidump not enabled, setting fulldump only\n");
+			dload_type = SCM_DLOAD_FULLDUMP;
+			return count;
+		}
+		dload_type = SCM_DLOAD_BOTHDUMPS;
+	} else{
+		pr_err("Invalid Dump setup request..\n");
+		pr_err("Supported dumps:'full', 'mini', or 'both'\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&tcsr_lock);
+	/*Overwrite TCSR reg*/
+	set_dload_mode(dload_type);
+	mutex_unlock(&tcsr_lock);
+	return count;
+}
+RESET_ATTR(dload_mode, 0644, show_dload_mode, store_dload_mode);
+#endif
 RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
 
 static struct attribute *reset_attrs[] = {
 	&reset_attr_emmc_dload.attr,
+#ifdef CONFIG_QCOM_MINIDUMP
+	&reset_attr_dload_mode.attr,
+#endif
 	NULL
 };
 
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 0012a92..7894850 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -2845,7 +2845,9 @@
 		 * more, but it may impact compliance.
 		 */
 		sink_attached = chg->typec_status[3] & UFP_DFP_MODE_STATUS_BIT;
-		if (!chg->typec_legacy_valid && !sink_attached && hvdcp)
+		if ((chg->connector_type != POWER_SUPPLY_CONNECTOR_MICRO_USB)
+				&& !chg->typec_legacy_valid
+				&& !sink_attached && hvdcp)
 			schedule_work(&chg->legacy_detection_work);
 	}
 
diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c
index 494ecd1..db4ecec 100644
--- a/drivers/scsi/ufs/ufs-qcom-debugfs.c
+++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c
@@ -121,7 +121,8 @@
 	struct ufs_hba *hba = host->hba;
 
 
-	ret = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE,
+	ret = simple_write_to_buffer(configuration,
+		TESTBUS_CFG_BUFF_LINE_SIZE - 1,
 		&buff_pos, ubuf, cnt);
 	if (ret < 0) {
 		dev_err(host->hba->dev, "%s: failed to read user data\n",
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 18aaacc..9750969 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -251,6 +251,23 @@
 	  of deadlocks or cpu hangs these dump regions are captured to
 	  give a snapshot of the system at the time of the crash.
 
+config QCOM_MINIDUMP
+	bool "QCOM Minidump Support"
+	depends on MSM_SMEM && QCOM_DLOAD_MODE
+	help
+	  This enables minidump feature. It allows various clients to
+	  register to dump their state at system bad state (panic/WDT,etc.,).
+	  Minidump would dump all registered entries, only when DLOAD mode
+	  is enabled.
+
+config MINIDUMP_MAX_ENTRIES
+	int "Minidump Maximum num of entries"
+	default 200
+	depends on QCOM_MINIDUMP
+	help
+	  This defines maximum number of entries to be allocated for application
+	  subsytem in Minidump table.
+
 config QCOM_BUS_SCALING
 	bool "Bus scaling driver"
 	help
@@ -693,4 +710,4 @@
 	help
 	  The driver will help route diag traffic from modem side over the QDSS
 	  sub-system to USB on APSS side. The driver acts as a bridge between the
-	  MHI and USB interface. If unsure, say N.
\ No newline at end of file
+	  MHI and USB interface. If unsure, say N.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index bb08357..768d4d9 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -28,6 +28,7 @@
 obj-$(CONFIG_QCOM_EUD) += eud.o
 obj-$(CONFIG_QCOM_WATCHDOG_V2) += watchdog_v2.o
 obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o
+obj-$(CONFIG_QCOM_MINIDUMP) += msm_minidump.o minidump_log.o
 obj-$(CONFIG_QCOM_RUN_QUEUE_STATS) += rq_stats.o
 obj-$(CONFIG_QCOM_SECURE_BUFFER) += secure_buffer.o
 obj-$(CONFIG_MSM_SMEM) += msm_smem.o smem_debug.o
@@ -79,4 +80,4 @@
 obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o
 obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o
 obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o
-obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o
\ No newline at end of file
+obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index 252bd21..72abf50 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -197,6 +197,7 @@
 			len);
 	return len;
 }
+EXPORT_SYMBOL(cmd_db_get_aux_data);
 
 int cmd_db_get_aux_data_len(const char *resource_id)
 {
@@ -208,6 +209,7 @@
 
 	return ret < 0 ? 0 : ent.len;
 }
+EXPORT_SYMBOL(cmd_db_get_aux_data_len);
 
 u16 cmd_db_get_version(const char *resource_id)
 {
diff --git a/drivers/soc/qcom/cpuss_dump.c b/drivers/soc/qcom/cpuss_dump.c
index 886a32f..eba1128 100644
--- a/drivers/soc/qcom/cpuss_dump.c
+++ b/drivers/soc/qcom/cpuss_dump.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -74,6 +74,8 @@
 
 		dump_data->addr = dump_addr;
 		dump_data->len = size;
+		scnprintf(dump_data->name, sizeof(dump_data->name),
+			"KCPUSS%X", id);
 		dump_entry.id = id;
 		dump_entry.addr = virt_to_phys(dump_data);
 		ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c
index 5873f5c..b76fe86 100644
--- a/drivers/soc/qcom/memory_dump_v2.c
+++ b/drivers/soc/qcom/memory_dump_v2.c
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <soc/qcom/memory_dump.h>
+#include <soc/qcom/minidump.h>
 #include <soc/qcom/scm.h>
 #include <linux/of_device.h>
 #include <linux/dma-mapping.h>
@@ -100,6 +101,33 @@
 	return table;
 }
 
+static int msm_dump_data_add_minidump(struct msm_dump_entry *entry)
+{
+	struct msm_dump_data *data;
+	struct md_region md_entry;
+
+	data = (struct msm_dump_data *)(phys_to_virt(entry->addr));
+
+	if (!data->addr || !data->len)
+		return -EINVAL;
+
+	if (!strcmp(data->name, "")) {
+		pr_debug("Entry name is NULL, Use ID %d for minidump\n",
+			 entry->id);
+		snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X",
+			 entry->id);
+	} else {
+		strlcpy(md_entry.name, data->name, sizeof(md_entry.name));
+	}
+
+	md_entry.phys_addr = data->addr;
+	md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr);
+	md_entry.size = data->len;
+	md_entry.id = entry->id;
+
+	return msm_minidump_add_region(&md_entry);
+}
+
 int msm_dump_data_register(enum msm_dump_table_ids id,
 			   struct msm_dump_entry *entry)
 {
@@ -120,6 +148,10 @@
 	table->num_entries++;
 
 	dmac_flush_range(table, (void *)table + sizeof(struct msm_dump_table));
+
+	if (msm_dump_data_add_minidump(entry))
+		pr_err("Failed to add entry in Minidump table\n");
+
 	return 0;
 }
 EXPORT_SYMBOL(msm_dump_data_register);
@@ -286,6 +318,9 @@
 
 		dump_data->addr = dump_addr;
 		dump_data->len = size;
+		strlcpy(dump_data->name, child_node->name,
+			strlen(child_node->name) + 1);
+
 		dump_entry.id = id;
 		dump_entry.addr = virt_to_phys(dump_data);
 		ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry);
diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c
new file mode 100644
index 0000000..c65dfd9
--- /dev/null
+++ b/drivers/soc/qcom/minidump_log.c
@@ -0,0 +1,104 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kallsyms.h>
+#include <linux/slab.h>
+#include <linux/thread_info.h>
+#include <soc/qcom/minidump.h>
+#include <asm/sections.h>
+
+static void __init register_log_buf(void)
+{
+	char **log_bufp;
+	uint32_t *log_buf_lenp;
+	struct md_region md_entry;
+
+	log_bufp = (char **)kallsyms_lookup_name("log_buf");
+	log_buf_lenp = (uint32_t *)kallsyms_lookup_name("log_buf_len");
+	if (!log_bufp || !log_buf_lenp) {
+		pr_err("Unable to find log_buf by kallsyms!\n");
+		return;
+	}
+	/*Register logbuf to minidump, first idx would be from bss section */
+	strlcpy(md_entry.name, "KLOGBUF", sizeof(md_entry.name));
+	md_entry.virt_addr = (uintptr_t) (*log_bufp);
+	md_entry.phys_addr = virt_to_phys(*log_bufp);
+	md_entry.size = *log_buf_lenp;
+	if (msm_minidump_add_region(&md_entry))
+		pr_err("Failed to add logbuf in Minidump\n");
+}
+
+static void __init register_kernel_sections(void)
+{
+	struct md_region ksec_entry;
+	char *data_name = "KDATABSS";
+	const size_t static_size = __per_cpu_end - __per_cpu_start;
+	void __percpu *base = (void __percpu *)__per_cpu_start;
+	unsigned int cpu;
+
+	strlcpy(ksec_entry.name, data_name, sizeof(ksec_entry.name));
+	ksec_entry.virt_addr = (uintptr_t)_sdata;
+	ksec_entry.phys_addr = virt_to_phys(_sdata);
+	ksec_entry.size = roundup((__bss_stop - _sdata), 4);
+	if (msm_minidump_add_region(&ksec_entry))
+		pr_err("Failed to add data section in Minidump\n");
+
+	/* Add percpu static sections */
+	for_each_possible_cpu(cpu) {
+		void *start = per_cpu_ptr(base, cpu);
+
+		memset(&ksec_entry, 0, sizeof(ksec_entry));
+		scnprintf(ksec_entry.name, sizeof(ksec_entry.name),
+			"KSPERCPU%d", cpu);
+		ksec_entry.virt_addr = (uintptr_t)start;
+		ksec_entry.phys_addr = per_cpu_ptr_to_phys(start);
+		ksec_entry.size = static_size;
+		if (msm_minidump_add_region(&ksec_entry))
+			pr_err("Failed to add percpu sections in Minidump\n");
+	}
+}
+
+void dump_stack_minidump(u64 sp)
+{
+	struct md_region ksp_entry, ktsk_entry;
+	u32 cpu = smp_processor_id();
+
+	if (sp < KIMAGE_VADDR || sp > -256UL)
+		sp = current_stack_pointer;
+
+	sp &= ~(THREAD_SIZE - 1);
+	scnprintf(ksp_entry.name, sizeof(ksp_entry.name), "KSTACK%d", cpu);
+	ksp_entry.virt_addr = sp;
+	ksp_entry.phys_addr = virt_to_phys((uintptr_t *)sp);
+	ksp_entry.size = THREAD_SIZE;
+	if (msm_minidump_add_region(&ksp_entry))
+		pr_err("Failed to add stack of cpu %d in Minidump\n", cpu);
+
+	scnprintf(ktsk_entry.name, sizeof(ktsk_entry.name), "KTASK%d", cpu);
+	ktsk_entry.virt_addr = (u64)current;
+	ktsk_entry.phys_addr = virt_to_phys((uintptr_t *)current);
+	ktsk_entry.size = sizeof(struct task_struct);
+	if (msm_minidump_add_region(&ktsk_entry))
+		pr_err("Failed to add current task %d in Minidump\n", cpu);
+}
+
+static int __init msm_minidump_log_init(void)
+{
+	register_kernel_sections();
+	register_log_buf();
+	return 0;
+}
+late_initcall(msm_minidump_log_init);
diff --git a/drivers/soc/qcom/minidump_private.h b/drivers/soc/qcom/minidump_private.h
new file mode 100644
index 0000000..81ebb1c
--- /dev/null
+++ b/drivers/soc/qcom/minidump_private.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __MINIDUMP_PRIVATE_H
+#define __MINIDUMP_PRIVATE_H
+
+#define MD_REVISION		1
+#define SBL_MINIDUMP_SMEM_ID	602
+#define MAX_NUM_OF_SS		10
+#define MD_SS_HLOS_ID		0
+#define SMEM_ENTRY_SIZE		40
+
+/* Bootloader has 16 byte support, 4 bytes reserved for itself */
+#define MAX_REGION_NAME_LENGTH	16
+
+#define MD_REGION_VALID		('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
+#define MD_REGION_INVALID	('I' << 24 | 'N' << 16 | 'V' << 8 | 'A' << 0)
+#define MD_REGION_INIT		('I' << 24 | 'N' << 16 | 'I' << 8 | 'T' << 0)
+#define MD_REGION_NOINIT	0
+
+#define MD_SS_ENCR_REQ		(0 << 24 | 'Y' << 16 | 'E' << 8 | 'S' << 0)
+#define MD_SS_ENCR_NOTREQ	(0 << 24 | 0 << 16 | 'N' << 8 | 'R' << 0)
+#define MD_SS_ENCR_NONE		('N' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
+#define MD_SS_ENCR_DONE		('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
+#define MD_SS_ENCR_START	('S' << 24 | 'T' << 16 | 'R' << 8 | 'T' << 0)
+#define MD_SS_ENABLED		('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
+#define MD_SS_DISABLED		('D' << 24 | 'S' << 16 | 'B' << 8 | 'L' << 0)
+
+/**
+ * md_ss_region - Minidump region
+ * @name		: Name of the region to be dumped
+ * @seq_num:		: Use to differentiate regions with same name.
+ * @md_valid		: This entry to be dumped (if set to 1)
+ * @region_base_address	: Physical address of region to be dumped
+ * @region_size		: Size of the region
+ */
+struct md_ss_region {
+	char	name[MAX_REGION_NAME_LENGTH];
+	u32	seq_num;
+	u32	md_valid;
+	u64	region_base_address;
+	u64	region_size;
+};
+
+/**
+ * md_ss_toc: Sub system SMEM Table of content
+ * @md_ss_toc_init : SS toc init status
+ * @md_ss_enable_status : if set to 1, Bootloader would dump this SS regions
+ * @encryption_status: Encryption status for this subsystem
+ * @encryption_required : Decides to encrypt the SS regions or not
+ * @ss_region_count : Number of regions added in this SS toc
+ * @md_ss_smem_regions_baseptr : regions base pointer of the Subsystem
+ */
+struct md_ss_toc {
+	u32			md_ss_toc_init;
+	u32			md_ss_enable_status;
+	u32			encryption_status;
+	u32			encryption_required;
+	u32			ss_region_count;
+	struct md_ss_region	*md_ss_smem_regions_baseptr;
+};
+
+/**
+ * md_global_toc: Global Table of Content
+ * @md_toc_init : Global Minidump init status
+ * @md_revision : Minidump revision
+ * @md_enable_status : Minidump enable status
+ * @md_ss_toc : Array of subsystems toc
+ */
+struct md_global_toc {
+	u32			md_toc_init;
+	u32			md_revision;
+	u32			md_enable_status;
+	struct md_ss_toc	md_ss_toc[MAX_NUM_OF_SS];
+};
+
+#endif
diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c
new file mode 100644
index 0000000..3fe62f1
--- /dev/null
+++ b/drivers/soc/qcom/msm_minidump.c
@@ -0,0 +1,380 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "Minidump: " fmt
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/minidump.h>
+#include "minidump_private.h"
+
+#define MAX_NUM_ENTRIES         (CONFIG_MINIDUMP_MAX_ENTRIES + 1)
+#define MAX_STRTBL_SIZE		(MAX_NUM_ENTRIES * MAX_REGION_NAME_LENGTH)
+
+/**
+ * md_table : Local Minidump toc holder
+ * @num_regions : Number of regions requested
+ * @md_ss_toc  : HLOS toc pointer
+ * @md_gbl_toc : Global toc pointer
+ * @md_regions : HLOS regions base pointer
+ * @entry : array of HLOS regions requested
+ */
+struct md_table {
+	u32			revision;
+	u32                     num_regions;
+	struct md_ss_toc	*md_ss_toc;
+	struct md_global_toc	*md_gbl_toc;
+	struct md_ss_region	*md_regions;
+	struct md_region        entry[MAX_NUM_ENTRIES];
+};
+
+/**
+ * md_elfhdr: Minidump table elf header
+ * @ehdr: elf main header
+ * @shdr: Section header
+ * @phdr: Program header
+ * @elf_offset: section offset in elf
+ * @strtable_idx: string table current index position
+ */
+struct md_elfhdr {
+	struct elfhdr		*ehdr;
+	struct elf_shdr		*shdr;
+	struct elf_phdr		*phdr;
+	u64			elf_offset;
+	u64			strtable_idx;
+};
+
+/* Protect elfheader and smem table from deferred calls contention */
+static DEFINE_SPINLOCK(mdt_lock);
+static struct md_table		minidump_table;
+static struct md_elfhdr		minidump_elfheader;
+
+/* Number of pending entries to be added in ToC regions */
+static unsigned int pendings;
+
+static inline char *elf_lookup_string(struct elfhdr *hdr, int offset)
+{
+	char *strtab = elf_str_table(hdr);
+
+	if ((strtab == NULL) || (minidump_elfheader.strtable_idx < offset))
+		return NULL;
+	return strtab + offset;
+}
+
+static inline unsigned int set_section_name(const char *name)
+{
+	char *strtab = elf_str_table(minidump_elfheader.ehdr);
+	int idx = minidump_elfheader.strtable_idx;
+	int ret = 0;
+
+	if ((strtab == NULL) || (name == NULL))
+		return 0;
+
+	ret = idx;
+	idx += strlcpy((strtab + idx), name, MAX_REGION_NAME_LENGTH);
+	minidump_elfheader.strtable_idx = idx + 1;
+
+	return ret;
+}
+
+static inline bool md_check_name(const char *name)
+{
+	struct md_region *mde = minidump_table.entry;
+	int i, regno = minidump_table.num_regions;
+
+	for (i = 0; i < regno; i++, mde++)
+		if (!strcmp(mde->name, name))
+			return true;
+	return false;
+}
+
+/* Return next seq no, if name already exists in the table */
+static inline int md_get_seq_num(const char *name)
+{
+	struct md_ss_region *mde = minidump_table.md_regions;
+	int i, regno = minidump_table.md_ss_toc->ss_region_count;
+	int seqno = 0;
+
+	for (i = 0; i < (regno - 1); i++, mde++) {
+		if (!strcmp(mde->name, name)) {
+			if (mde->seq_num >= seqno)
+				seqno = mde->seq_num + 1;
+		}
+	}
+	return seqno;
+}
+
+/* Update Mini dump table in SMEM */
+static void md_update_ss_toc(const struct md_region *entry)
+{
+	struct md_ss_region *mdr;
+	struct elfhdr *hdr = minidump_elfheader.ehdr;
+	struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++);
+	struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++);
+	int reg_cnt = minidump_table.md_ss_toc->ss_region_count++;
+
+	mdr = &minidump_table.md_regions[reg_cnt];
+
+	strlcpy(mdr->name, entry->name, sizeof(mdr->name));
+	mdr->region_base_address = entry->phys_addr;
+	mdr->region_size = entry->size;
+	mdr->seq_num = md_get_seq_num(entry->name);
+
+	/* Update elf header */
+	shdr->sh_type = SHT_PROGBITS;
+	shdr->sh_name = set_section_name(mdr->name);
+	shdr->sh_addr = (elf_addr_t)entry->virt_addr;
+	shdr->sh_size = mdr->region_size;
+	shdr->sh_flags = SHF_WRITE;
+	shdr->sh_offset = minidump_elfheader.elf_offset;
+	shdr->sh_entsize = 0;
+
+	phdr->p_type = PT_LOAD;
+	phdr->p_offset = minidump_elfheader.elf_offset;
+	phdr->p_vaddr = entry->virt_addr;
+	phdr->p_paddr = entry->phys_addr;
+	phdr->p_filesz = phdr->p_memsz =  mdr->region_size;
+	phdr->p_flags = PF_R | PF_W;
+
+	minidump_elfheader.elf_offset += shdr->sh_size;
+	mdr->md_valid = MD_REGION_VALID;
+}
+
+bool msm_minidump_enabled(void)
+{
+	bool ret = false;
+
+	spin_lock(&mdt_lock);
+	if (minidump_table.md_ss_toc &&
+		(minidump_table.md_ss_toc->md_ss_enable_status ==
+		 MD_SS_ENABLED))
+		ret = true;
+	spin_unlock(&mdt_lock);
+	return ret;
+}
+EXPORT_SYMBOL(msm_minidump_enabled);
+
+int msm_minidump_add_region(const struct md_region *entry)
+{
+	u32 entries;
+	struct md_region *mdr;
+	int ret = 0;
+
+	if (!entry)
+		return -EINVAL;
+
+	if ((strlen(entry->name) > MAX_NAME_LENGTH) ||
+		md_check_name(entry->name) || !entry->virt_addr) {
+		pr_err("Invalid entry details\n");
+		return -EINVAL;
+	}
+
+	if (!IS_ALIGNED(entry->size, 4)) {
+		pr_err("size should be 4 byte aligned\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&mdt_lock);
+	entries = minidump_table.num_regions;
+	if (entries >= MAX_NUM_ENTRIES) {
+		pr_err("Maximum entries reached.\n");
+		spin_unlock(&mdt_lock);
+		return -ENOMEM;
+	}
+
+	mdr = &minidump_table.entry[entries];
+	strlcpy(mdr->name, entry->name, sizeof(mdr->name));
+	mdr->virt_addr = entry->virt_addr;
+	mdr->phys_addr = entry->phys_addr;
+	mdr->size = entry->size;
+	mdr->id = entry->id;
+
+	minidump_table.num_regions = entries + 1;
+
+	if (minidump_table.md_ss_toc &&
+		(minidump_table.md_ss_toc->md_ss_enable_status ==
+		MD_SS_ENABLED))
+		md_update_ss_toc(entry);
+	else
+		pendings++;
+
+	spin_unlock(&mdt_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_minidump_add_region);
+
+static int msm_minidump_add_header(void)
+{
+	struct md_ss_region *mdreg = &minidump_table.md_regions[0];
+	struct elfhdr *ehdr;
+	struct elf_shdr *shdr;
+	struct elf_phdr *phdr;
+	unsigned int strtbl_off, elfh_size, phdr_off;
+	char *banner;
+
+	/* Header buffer contains:
+	 * elf header, MAX_NUM_ENTRIES+4 of section and program elf headers,
+	 * string table section and linux banner.
+	 */
+	elfh_size = sizeof(*ehdr) + MAX_STRTBL_SIZE + (strlen(linux_banner) +
+		1) + ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 4));
+	elfh_size = ALIGN(elfh_size, 4);
+
+	minidump_elfheader.ehdr = kzalloc(elfh_size, GFP_KERNEL);
+	if (!minidump_elfheader.ehdr)
+		return -ENOMEM;
+
+	strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name));
+	mdreg->region_base_address = virt_to_phys(minidump_elfheader.ehdr);
+	mdreg->region_size = elfh_size;
+
+	ehdr = minidump_elfheader.ehdr;
+	/* Assign section/program headers offset */
+	minidump_elfheader.shdr = shdr = (struct elf_shdr *)(ehdr + 1);
+	minidump_elfheader.phdr = phdr =
+				 (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
+	phdr_off = sizeof(*ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
+
+	memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+	ehdr->e_ident[EI_CLASS] = ELF_CLASS;
+	ehdr->e_ident[EI_DATA] = ELF_DATA;
+	ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+	ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+	ehdr->e_type = ET_CORE;
+	ehdr->e_machine  = ELF_ARCH;
+	ehdr->e_version = EV_CURRENT;
+	ehdr->e_ehsize = sizeof(*ehdr);
+	ehdr->e_phoff = phdr_off;
+	ehdr->e_phentsize = sizeof(*phdr);
+	ehdr->e_shoff = sizeof(*ehdr);
+	ehdr->e_shentsize = sizeof(*shdr);
+	ehdr->e_shstrndx = 1;
+
+	minidump_elfheader.elf_offset = elfh_size;
+
+	/*
+	 * First section header should be NULL,
+	 * 2nd section is string table.
+	 */
+	minidump_elfheader.strtable_idx = 1;
+	strtbl_off = sizeof(*ehdr) +
+			((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
+	shdr++;
+	shdr->sh_type = SHT_STRTAB;
+	shdr->sh_offset = (elf_addr_t)strtbl_off;
+	shdr->sh_size = MAX_STRTBL_SIZE;
+	shdr->sh_entsize = 0;
+	shdr->sh_flags = 0;
+	shdr->sh_name = set_section_name("STR_TBL");
+	shdr++;
+
+	/* 3rd section is for minidump_table VA, used by parsers */
+	shdr->sh_type = SHT_PROGBITS;
+	shdr->sh_entsize = 0;
+	shdr->sh_flags = 0;
+	shdr->sh_addr = (elf_addr_t)&minidump_table;
+	shdr->sh_name = set_section_name("minidump_table");
+	shdr++;
+
+	/* 4th section is linux banner */
+	banner = (char *)ehdr + strtbl_off + MAX_STRTBL_SIZE;
+	strlcpy(banner, linux_banner, strlen(linux_banner) + 1);
+
+	shdr->sh_type = SHT_PROGBITS;
+	shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
+	shdr->sh_size = strlen(linux_banner) + 1;
+	shdr->sh_addr = (elf_addr_t)linux_banner;
+	shdr->sh_entsize = 0;
+	shdr->sh_flags = SHF_WRITE;
+	shdr->sh_name = set_section_name("linux_banner");
+
+	phdr->p_type = PT_LOAD;
+	phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
+	phdr->p_vaddr = (elf_addr_t)linux_banner;
+	phdr->p_paddr = virt_to_phys(linux_banner);
+	phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1;
+	phdr->p_flags = PF_R | PF_W;
+
+	/* Update headers count*/
+	ehdr->e_phnum = 1;
+	ehdr->e_shnum = 4;
+
+	mdreg->md_valid = MD_REGION_VALID;
+	return 0;
+}
+
+static int __init msm_minidump_init(void)
+{
+	unsigned int i, size;
+	struct md_region *mdr;
+	struct md_global_toc *md_global_toc;
+	struct md_ss_toc *md_ss_toc;
+
+	/* Get Minidump table */
+	md_global_toc = smem_get_entry(SBL_MINIDUMP_SMEM_ID, &size, 0,
+					SMEM_ANY_HOST_FLAG);
+	if (IS_ERR_OR_NULL(md_global_toc)) {
+		pr_err("SMEM is not initialized.\n");
+		return -ENODEV;
+	}
+
+	/*Check global minidump support initialization */
+	if (!md_global_toc->md_toc_init) {
+		pr_err("System Minidump TOC not initialized\n");
+		return -ENODEV;
+	}
+
+	minidump_table.md_gbl_toc = md_global_toc;
+	minidump_table.revision = md_global_toc->md_revision;
+	md_ss_toc = &md_global_toc->md_ss_toc[MD_SS_HLOS_ID];
+
+	md_ss_toc->encryption_status = MD_SS_ENCR_NONE;
+	md_ss_toc->encryption_required = MD_SS_ENCR_REQ;
+
+	minidump_table.md_ss_toc = md_ss_toc;
+	minidump_table.md_regions = kzalloc((MAX_NUM_ENTRIES *
+				sizeof(struct md_ss_region)), GFP_KERNEL);
+	if (!minidump_table.md_regions)
+		return -ENOMEM;
+
+	md_ss_toc->md_ss_smem_regions_baseptr =
+				(void *)virt_to_phys(minidump_table.md_regions);
+
+	/* First entry would be ELF header */
+	md_ss_toc->ss_region_count = 1;
+	msm_minidump_add_header();
+
+	/* Add pending entries to HLOS TOC */
+	spin_lock(&mdt_lock);
+	md_ss_toc->md_ss_toc_init = 1;
+	md_ss_toc->md_ss_enable_status = MD_SS_ENABLED;
+	for (i = 0; i < pendings; i++) {
+		mdr = &minidump_table.entry[i];
+		md_update_ss_toc(mdr);
+	}
+
+	pendings = 0;
+	spin_unlock(&mdt_lock);
+
+	pr_info("Enabled with max number of regions %d\n",
+		CONFIG_MINIDUMP_MAX_ENTRIES);
+
+	return 0;
+}
+subsys_initcall(msm_minidump_init)
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 0477064..2ca0615 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -38,6 +38,7 @@
 #define PROXY_TIMEOUT_MS	10000
 #define MAX_SSR_REASON_LEN	256U
 #define STOP_ACK_TIMEOUT_MS	1000
+#define QDSP6SS_NMI_STATUS	0x44
 
 #define subsys_to_drv(d) container_of(d, struct modem_data, subsys_desc)
 
@@ -74,12 +75,17 @@
 static irqreturn_t modem_err_fatal_intr_handler(int irq, void *dev_id)
 {
 	struct modem_data *drv = subsys_to_drv(dev_id);
+	u32 nmi_status = readl_relaxed(drv->q6->reg_base + QDSP6SS_NMI_STATUS);
 
 	/* Ignore if we're the one that set the force stop GPIO */
 	if (drv->crash_shutdown)
 		return IRQ_HANDLED;
 
-	pr_err("Fatal error on the modem.\n");
+	if (nmi_status & 0x04)
+		pr_err("%s: Fatal error on the modem due to TZ NMI\n",
+			__func__);
+	else
+		pr_err("%s: Fatal error on the modem\n", __func__);
 	subsys_set_crash_status(drv->subsys, CRASH_STATUS_ERR_FATAL);
 	restart_modem(drv);
 	return IRQ_HANDLED;
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index e4c1bb8..7758c64 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -454,23 +454,6 @@
 
 }
 
-static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
-{
-	return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
-}
-
-static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
-{
-	return &elf_sheader(hdr)[idx];
-}
-
-static inline char *elf_str_table(struct elfhdr *hdr)
-{
-	if (hdr->e_shstrndx == SHN_UNDEF)
-		return NULL;
-	return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
-}
-
 static inline unsigned int set_section_name(const char *name,
 					    struct elfhdr *ehdr)
 {
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 119a788..876e176 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -537,14 +537,15 @@
 		 */
 		pr_err("GLINK_REMOTE_DISCONNECTED, ch [%s].\n", ch->name);
 
-		ch->glink_state = event;
-
 		/*
 		 * Abort any blocking read() operation.
 		 * The glink notification might be after REMOTE_DISCONNECT.
 		 */
 		spcom_notify_rx_abort(NULL, ch, NULL);
 
+		/* set the state to not-connected after notify-rx-abort */
+		ch->glink_state = event;
+
 		/*
 		 * after glink_close(),
 		 * expecting notify GLINK_LOCAL_DISCONNECTED
@@ -597,9 +598,7 @@
 
 	pr_debug("ch [%s] pending rx aborted.\n", ch->name);
 
-	/* ignore rx-abort after local channel disconect,
-	 * so check that the channel is connected.
-	 */
+	/* ignore rx-abort after local channel disconected */
 	if (spcom_is_channel_connected(ch) && (!ch->rx_abort)) {
 		ch->rx_abort = true;
 		complete_all(&ch->rx_done);
@@ -880,14 +879,16 @@
 	for (retry = 0; retry < TX_MAX_RETRY ; retry++) {
 		ret = glink_tx(ch->glink_handle, pkt_priv, buf, size, tx_flags);
 		if (ret == -EAGAIN) {
-			pr_err("glink_tx() fail, try again.\n");
+			pr_err("glink_tx() fail, try again, ch [%s].\n",
+				ch->name);
 			/*
 			 * Delay to allow remote side to queue rx buffer.
 			 * This may happen after the first channel connection.
 			 */
 			msleep(TX_RETRY_DELAY_MSEC);
 		} else if (ret < 0) {
-			pr_err("glink_tx() error %d.\n", ret);
+			pr_err("glink_tx() error [%d], ch [%s].\n",
+				ret, ch->name);
 			goto exit_err;
 		} else {
 			break; /* no retry needed */
@@ -1080,10 +1081,19 @@
 	for (i = 0 ; i < ARRAY_SIZE(spcom_dev->channels); i++) {
 		struct spcom_channel *ch = &spcom_dev->channels[i];
 
-		if (ch->is_server) {
-			pr_debug("rx-abort server on ch [%s].\n", ch->name);
-			spcom_notify_rx_abort(NULL, ch, NULL);
-		}
+		/* relevant only for servers */
+		if (!ch->is_server)
+			continue;
+
+		/* The server might not be connected to a client.
+		 * Don't check if connected, only if open.
+		 */
+		if (!spcom_is_channel_open(ch) || (ch->rx_abort))
+			continue;
+
+		pr_debug("rx-abort server ch [%s].\n", ch->name);
+		ch->rx_abort = true;
+		complete_all(&ch->rx_done);
 	}
 }
 
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index d65756c..5b600f6 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -42,6 +42,7 @@
 
 #define ERR_READY	0
 #define PBL_DONE	1
+#define QDSP6SS_NMI_STATUS	0x44
 
 #define desc_to_data(d) container_of(d, struct pil_tz_data, desc)
 #define subsys_to_data(d) container_of(d, struct pil_tz_data, subsys_desc)
@@ -109,6 +110,7 @@
 	void __iomem *irq_mask;
 	void __iomem *err_status;
 	void __iomem *err_status_spare;
+	void __iomem *reg_base;
 	u32 bits_arr[2];
 };
 
@@ -925,8 +927,19 @@
 static irqreturn_t subsys_err_fatal_intr_handler (int irq, void *dev_id)
 {
 	struct pil_tz_data *d = subsys_to_data(dev_id);
+	u32 nmi_status = 0;
 
-	pr_err("Fatal error on %s!\n", d->subsys_desc.name);
+	if (d->reg_base)
+		nmi_status = readl_relaxed(d->reg_base +
+						QDSP6SS_NMI_STATUS);
+
+	if (nmi_status & 0x04)
+		pr_err("%s: Fatal error on the %s due to TZ NMI\n",
+			__func__, d->subsys_desc.name);
+	else
+		pr_err("%s Fatal error on the %s\n",
+			__func__, d->subsys_desc.name);
+
 	if (subsys_get_crash_status(d->subsys)) {
 		pr_err("%s: Ignoring error fatal, restart in progress\n",
 							d->subsys_desc.name);
@@ -1062,6 +1075,13 @@
 	d->keep_proxy_regs_on = of_property_read_bool(pdev->dev.of_node,
 						"qcom,keep-proxy-regs-on");
 
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base_reg");
+	d->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(d->reg_base)) {
+		dev_err(&pdev->dev, "Failed to ioremap base register\n");
+		d->reg_base = NULL;
+	}
+
 	rc = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
 				      &d->desc.name);
 	if (rc)
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 9aea6db..f5e76e0 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -29,6 +29,7 @@
 #include <linux/wait.h>
 #include <soc/qcom/scm.h>
 #include <soc/qcom/memory_dump.h>
+#include <soc/qcom/minidump.h>
 #include <soc/qcom/watchdog.h>
 #include <linux/dma-mapping.h>
 
@@ -549,6 +550,8 @@
 		cpu_data[cpu].addr = virt_to_phys(cpu_buf +
 						cpu * MAX_CPU_CTX_SIZE);
 		cpu_data[cpu].len = MAX_CPU_CTX_SIZE;
+		snprintf(cpu_data[cpu].name, sizeof(cpu_data[cpu].name),
+			"KCPU_CTX%d", cpu);
 		dump_entry.id = MSM_DUMP_DATA_CPU_CTX + cpu;
 		dump_entry.addr = virt_to_phys(&cpu_data[cpu]);
 		ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
@@ -596,6 +599,8 @@
 
 		cpu_data->addr = dump_addr;
 		cpu_data->len = MAX_CPU_SCANDUMP_SIZE;
+		snprintf(cpu_data->name, sizeof(cpu_data->name),
+			"KSCANDUMP%d", cpu);
 		dump_entry.id = MSM_DUMP_DATA_SCANDUMP_PER_CPU + cpu;
 		dump_entry.addr = virt_to_phys(cpu_data);
 		ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
@@ -799,6 +804,7 @@
 {
 	int ret;
 	struct msm_watchdog_data *wdog_dd;
+	struct md_region md_entry;
 
 	if (!pdev->dev.of_node || !enable)
 		return -ENODEV;
@@ -820,6 +826,15 @@
 		goto err;
 	}
 	init_watchdog_data(wdog_dd);
+
+	/* Add wdog info to minidump table */
+	strlcpy(md_entry.name, "KWDOGDATA", sizeof(md_entry.name));
+	md_entry.virt_addr = (uintptr_t)wdog_dd;
+	md_entry.phys_addr = virt_to_phys(wdog_dd);
+	md_entry.size = sizeof(*wdog_dd);
+	if (msm_minidump_add_region(&md_entry))
+		pr_info("Failed to add Watchdog data in Minidump\n");
+
 	return 0;
 err:
 	kzfree(wdog_dd);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 035f03b..e4b39a7 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2290,6 +2290,14 @@
 	return hcd->driver->get_core_id(hcd);
 }
 
+int usb_hcd_stop_endpoint(struct usb_device *udev,
+		struct usb_host_endpoint *ep)
+{
+	struct usb_hcd	*hcd = bus_to_hcd(udev->bus);
+
+	return hcd->driver->stop_endpoint(hcd, udev, ep);
+}
+
 #ifdef	CONFIG_PM
 
 int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index d745733..bb2a4fe 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -734,6 +734,12 @@
 }
 EXPORT_SYMBOL(usb_get_controller_id);
 
+int usb_stop_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
+{
+	return usb_hcd_stop_endpoint(dev, ep);
+}
+EXPORT_SYMBOL(usb_stop_endpoint);
+
 /*-------------------------------------------------------------------*/
 /*
  * __usb_get_extra_descriptor() finds a descriptor of specific type in the
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f511055..68a40f9 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -480,6 +480,8 @@
 #define DWC3_DEPCMD_SETTRANSFRESOURCE	(0x02 << 0)
 #define DWC3_DEPCMD_SETEPCONFIG		(0x01 << 0)
 
+#define DWC3_DEPCMD_CMD(x)		((x) & 0xf)
+
 /* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
 #define DWC3_DALEPENA_EP(n)		(1 << n)
 
@@ -613,6 +615,7 @@
 #define DWC3_EP_BUSY		(1 << 4)
 #define DWC3_EP_PENDING_REQUEST	(1 << 5)
 #define DWC3_EP_MISSED_ISOC	(1 << 6)
+#define DWC3_EP_TRANSFER_STARTED (1 << 8)
 
 	/* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN		(1 << 31)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c12fbf3..5571374 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -355,7 +355,7 @@
 		}
 	}
 
-	if (cmd == DWC3_DEPCMD_STARTTRANSFER) {
+	if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
 		int		needs_wakeup;
 
 		needs_wakeup = (dwc->link_state == DWC3_LINK_STATE_U1 ||
@@ -423,6 +423,20 @@
 
 	trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
 
+	if (ret == 0) {
+		switch (DWC3_DEPCMD_CMD(cmd)) {
+		case DWC3_DEPCMD_STARTTRANSFER:
+			dep->flags |= DWC3_EP_TRANSFER_STARTED;
+			break;
+		case DWC3_DEPCMD_ENDTRANSFER:
+			dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+			break;
+		default:
+			/* nothing */
+			break;
+		}
+	}
+
 	if (unlikely(susphy)) {
 		reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
 		reg |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -1200,6 +1214,14 @@
 	return 0;
 }
 
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
+{
+	u32			reg;
+
+	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+	return DWC3_DSTS_SOFFN(reg);
+}
+
 static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
 		struct dwc3_ep *dep, u32 cur_uf)
 {
@@ -1214,8 +1236,11 @@
 		return;
 	}
 
-	/* 4 micro frames in the future */
-	uf = cur_uf + dep->interval * 4;
+	/*
+	 * Schedule the first trb for one interval in the future or at
+	 * least 4 microframes.
+	 */
+	uf = cur_uf + max_t(u32, 4, dep->interval);
 
 	ret = __dwc3_gadget_kick_transfer(dep, uf);
 	if (ret < 0)
@@ -1285,12 +1310,28 @@
 	 * errors which will force us issue EndTransfer command.
 	 */
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
-		if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
-				list_empty(&dep->started_list)) {
-			dwc3_stop_active_transfer(dwc, dep->number, true);
-			dep->flags = DWC3_EP_ENABLED;
+		if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
+			if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
+				dwc3_stop_active_transfer(dwc, dep->number, true);
+				dep->flags = DWC3_EP_ENABLED;
+			} else {
+				u32 cur_uf;
+
+				cur_uf = __dwc3_gadget_get_frame(dwc);
+				__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+				dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+			}
+			return 0;
 		}
-		return 0;
+
+		if ((dep->flags & DWC3_EP_BUSY) &&
+		    !(dep->flags & DWC3_EP_MISSED_ISOC)) {
+			WARN_ON_ONCE(!dep->resource_index);
+			ret = __dwc3_gadget_kick_transfer(dep,
+							  dep->resource_index);
+		}
+
+		goto out;
 	}
 
 	if (!dwc3_calc_trbs_left(dep))
@@ -1301,6 +1342,7 @@
 		dwc3_trace(trace_dwc3_gadget,
 				"%s: failed to kick transfers",
 				dep->name);
+out:
 	if (ret == -EBUSY)
 		ret = 0;
 
@@ -1635,10 +1677,8 @@
 static int dwc3_gadget_get_frame(struct usb_gadget *g)
 {
 	struct dwc3		*dwc = gadget_to_dwc(g);
-	u32			reg;
 
-	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-	return DWC3_DSTS_SOFFN(reg);
+	return __dwc3_gadget_get_frame(dwc);
 }
 
 static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 969cfe7..5605c1e 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -23,7 +23,7 @@
 #include "u_uac2.h"
 
 /* Keep everyone on toes */
-#define USB_XFERS	2
+#define USB_XFERS	8
 
 /*
  * The driver implements a simple UAC_2 topology.
@@ -54,6 +54,10 @@
 #define UNFLW_CTRL	8
 #define OVFLW_CTRL	10
 
+static bool enable_capture;
+module_param(enable_capture, bool, 0644);
+MODULE_PARM_DESC(enable_capture, "Enable USB Peripheral speaker function");
+
 static const char *uac2_name = "snd_uac2";
 
 struct uac2_req {
@@ -126,6 +130,8 @@
 	struct usb_ep *in_ep, *out_ep;
 	struct usb_function func;
 
+	bool enable_capture;
+
 	/* The ALSA Sound Card it represents on the USB-Client side */
 	struct snd_uac2_chip uac2;
 };
@@ -468,7 +474,9 @@
 	 * Create a substream only for non-zero channel streams
 	 */
 	err = snd_pcm_new(uac2->card, "UAC2 PCM", 0,
-			       p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm);
+			       p_chmask ? 1 : 0,
+			       (c_chmask && audio_dev->enable_capture) ? 1 : 0,
+			       &pcm);
 	if (err < 0)
 		goto snd_fail;
 
@@ -779,6 +787,13 @@
 	.bInterval = 4,
 };
 
+static struct usb_ss_ep_comp_descriptor ss_epout_comp_desc = {
+	 .bLength =		 sizeof(ss_epout_comp_desc),
+	 .bDescriptorType =	 USB_DT_SS_ENDPOINT_COMP,
+
+	 .wBytesPerInterval =	cpu_to_le16(1024),
+};
+
 /* CS AS ISO OUT Endpoint */
 static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {
 	.bLength = sizeof as_iso_out_desc,
@@ -856,6 +871,13 @@
 	.bInterval = 4,
 };
 
+static struct usb_ss_ep_comp_descriptor ss_epin_comp_desc = {
+	 .bLength =		 sizeof(ss_epin_comp_desc),
+	 .bDescriptorType =	 USB_DT_SS_ENDPOINT_COMP,
+
+	 .wBytesPerInterval =	cpu_to_le16(1024),
+};
+
 /* CS AS ISO IN Endpoint */
 static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {
 	.bLength = sizeof as_iso_in_desc,
@@ -898,6 +920,25 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *fs_playback_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&as_in_fmt1_desc,
+	(struct usb_descriptor_header *)&fs_epin_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
 static struct usb_descriptor_header *hs_audio_desc[] = {
 	(struct usb_descriptor_header *)&iad_desc,
 	(struct usb_descriptor_header *)&std_ac_if_desc,
@@ -928,6 +969,77 @@
 	NULL,
 };
 
+static struct usb_descriptor_header *hs_playback_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&as_in_fmt1_desc,
+	(struct usb_descriptor_header *)&hs_epin_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *ss_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&out_clk_src_desc,
+	(struct usb_descriptor_header *)&usb_out_it_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+	(struct usb_descriptor_header *)&io_out_ot_desc,
+
+	(struct usb_descriptor_header *)&std_as_out_if0_desc,
+	(struct usb_descriptor_header *)&std_as_out_if1_desc,
+
+	(struct usb_descriptor_header *)&as_out_hdr_desc,
+	(struct usb_descriptor_header *)&as_out_fmt1_desc,
+	(struct usb_descriptor_header *)&hs_epout_desc,
+	(struct usb_descriptor_header *)&ss_epout_comp_desc,
+	(struct usb_descriptor_header *)&as_iso_out_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&as_in_fmt1_desc,
+	(struct usb_descriptor_header *)&hs_epin_desc,
+	(struct usb_descriptor_header *)&ss_epin_comp_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *ss_playback_audio_desc[] = {
+	(struct usb_descriptor_header *)&iad_desc,
+	(struct usb_descriptor_header *)&std_ac_if_desc,
+
+	(struct usb_descriptor_header *)&ac_hdr_desc,
+	(struct usb_descriptor_header *)&in_clk_src_desc,
+	(struct usb_descriptor_header *)&io_in_it_desc,
+	(struct usb_descriptor_header *)&usb_in_ot_desc,
+
+	(struct usb_descriptor_header *)&std_as_in_if0_desc,
+	(struct usb_descriptor_header *)&std_as_in_if1_desc,
+
+	(struct usb_descriptor_header *)&as_in_hdr_desc,
+	(struct usb_descriptor_header *)&as_in_fmt1_desc,
+	(struct usb_descriptor_header *)&hs_epin_desc,
+	(struct usb_descriptor_header *)&ss_epin_comp_desc,
+	(struct usb_descriptor_header *)&as_iso_in_desc,
+	NULL,
+};
+
 struct cntrl_cur_lay3 {
 	__u32	dCUR;
 };
@@ -1035,24 +1147,30 @@
 	snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
 	snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
 
+	pr_debug("%s bind with capture enabled(%d)\n", __func__,
+							enable_capture);
+	agdev->enable_capture = enable_capture;
 	ret = usb_interface_id(cfg, fn);
 	if (ret < 0) {
 		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 		return ret;
 	}
 	std_ac_if_desc.bInterfaceNumber = ret;
+	iad_desc.bFirstInterface = ret;
 	agdev->ac_intf = ret;
 	agdev->ac_alt = 0;
 
-	ret = usb_interface_id(cfg, fn);
-	if (ret < 0) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
+	if (agdev->enable_capture) {
+		ret = usb_interface_id(cfg, fn);
+		if (ret < 0) {
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+			return ret;
+		}
+		std_as_out_if0_desc.bInterfaceNumber = ret;
+		std_as_out_if1_desc.bInterfaceNumber = ret;
+		agdev->as_out_intf = ret;
+		agdev->as_out_alt = 0;
 	}
-	std_as_out_if0_desc.bInterfaceNumber = ret;
-	std_as_out_if1_desc.bInterfaceNumber = ret;
-	agdev->as_out_intf = ret;
-	agdev->as_out_alt = 0;
 
 	ret = usb_interface_id(cfg, fn);
 	if (ret < 0) {
@@ -1064,10 +1182,12 @@
 	agdev->as_in_intf = ret;
 	agdev->as_in_alt = 0;
 
-	agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
-	if (!agdev->out_ep) {
-		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
-		return ret;
+	if (agdev->enable_capture) {
+		agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
+		if (!agdev->out_ep) {
+			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+			return ret;
+		}
 	}
 
 	agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
@@ -1088,17 +1208,25 @@
 	hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
 	hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
 
-	ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL,
-				     NULL);
+	if (agdev->enable_capture) {
+		ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc,
+					ss_audio_desc, NULL);
+	} else {
+		ret = usb_assign_descriptors(fn, fs_playback_audio_desc,
+						hs_playback_audio_desc,
+						ss_playback_audio_desc, NULL);
+	}
 	if (ret)
 		return ret;
 
-	prm = &agdev->uac2.c_prm;
-	prm->max_psize = hs_epout_desc.wMaxPacketSize;
-	prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
-	if (!prm->rbuf) {
-		prm->max_psize = 0;
-		goto err_free_descs;
+	if (agdev->enable_capture) {
+		prm = &agdev->uac2.c_prm;
+		prm->max_psize = hs_epout_desc.wMaxPacketSize;
+		prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
+		if (!prm->rbuf) {
+			prm->max_psize = 0;
+			goto err_free_descs;
+		}
 	}
 
 	prm = &agdev->uac2.p_prm;
@@ -1150,7 +1278,7 @@
 		return 0;
 	}
 
-	if (intf == agdev->as_out_intf) {
+	if (intf == agdev->as_out_intf && agdev->enable_capture) {
 		ep = agdev->out_ep;
 		prm = &uac2->c_prm;
 		config_ep_by_speed(gadget, fn, ep);
@@ -1200,27 +1328,31 @@
 		return 0;
 	}
 
-	prm->ep_enabled = true;
-	usb_ep_enable(ep);
+	if (intf == agdev->as_in_intf ||
+		(intf == agdev->as_out_intf && agdev->enable_capture)) {
+		prm->ep_enabled = true;
+		usb_ep_enable(ep);
 
-	for (i = 0; i < USB_XFERS; i++) {
-		if (!prm->ureq[i].req) {
-			req = usb_ep_alloc_request(ep, GFP_ATOMIC);
-			if (req == NULL)
-				return -ENOMEM;
+		for (i = 0; i < USB_XFERS; i++) {
+			if (!prm->ureq[i].req) {
+				req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+				if (req == NULL)
+					return -ENOMEM;
 
-			prm->ureq[i].req = req;
-			prm->ureq[i].pp = prm;
+				prm->ureq[i].req = req;
+				prm->ureq[i].pp = prm;
 
-			req->zero = 0;
-			req->context = &prm->ureq[i];
-			req->length = req_len;
-			req->complete = agdev_iso_complete;
-			req->buf = prm->rbuf + i * prm->max_psize;
+				req->zero = 0;
+				req->context = &prm->ureq[i];
+				req->length = req_len;
+				req->complete = agdev_iso_complete;
+				req->buf = prm->rbuf + i * prm->max_psize;
+			}
+
+			if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
+				dev_err(dev, "%s:%d Error!\n", __func__,
+						__LINE__);
 		}
-
-		if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
-			dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
 	}
 
 	return 0;
@@ -1234,7 +1366,7 @@
 
 	if (intf == agdev->ac_intf)
 		return agdev->ac_alt;
-	else if (intf == agdev->as_out_intf)
+	else if (intf == agdev->as_out_intf && agdev->enable_capture)
 		return agdev->as_out_alt;
 	else if (intf == agdev->as_in_intf)
 		return agdev->as_in_alt;
@@ -1255,8 +1387,10 @@
 	free_ep(&uac2->p_prm, agdev->in_ep);
 	agdev->as_in_alt = 0;
 
-	free_ep(&uac2->c_prm, agdev->out_ep);
-	agdev->as_out_alt = 0;
+	if (agdev->enable_capture) {
+		free_ep(&uac2->c_prm, agdev->out_ep);
+		agdev->as_out_alt = 0;
+	}
 }
 
 static int
@@ -1558,8 +1692,10 @@
 	prm = &agdev->uac2.p_prm;
 	kfree(prm->rbuf);
 
-	prm = &agdev->uac2.c_prm;
-	kfree(prm->rbuf);
+	if (agdev->enable_capture) {
+		prm = &agdev->uac2.c_prm;
+		kfree(prm->rbuf);
+	}
 	usb_free_all_descriptors(f);
 }
 
@@ -1590,6 +1726,19 @@
 }
 
 DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
+
+static int afunc_init(void)
+{
+	return usb_function_register(&uac2usb_func);
+}
+module_init(afunc_init);
+
+static void __exit afunc_exit(void)
+{
+	usb_function_unregister(&uac2usb_func);
+}
+module_exit(afunc_exit);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Yadwinder Singh");
 MODULE_AUTHOR("Jaswinder Singh");
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index c7689d0..c99d547 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -84,7 +84,7 @@
 	.bNumEndpoints		= 1,
 	.bInterfaceClass	= USB_CLASS_VIDEO,
 	.bInterfaceSubClass	= UVC_SC_VIDEOCONTROL,
-	.bInterfaceProtocol	= 0x00,
+	.bInterfaceProtocol	= 0x01,
 	.iInterface		= 0,
 };
 
@@ -788,16 +788,18 @@
 	cd->bmControls[2]		= 0;
 
 	pd = &opts->uvc_processing;
-	pd->bLength			= UVC_DT_PROCESSING_UNIT_SIZE(2);
+	pd->bLength			= UVC_DT_PROCESSING_UNIT_SIZE(3);
 	pd->bDescriptorType		= USB_DT_CS_INTERFACE;
 	pd->bDescriptorSubType		= UVC_VC_PROCESSING_UNIT;
 	pd->bUnitID			= 2;
 	pd->bSourceID			= 1;
 	pd->wMaxMultiplier		= cpu_to_le16(16*1024);
-	pd->bControlSize		= 2;
-	pd->bmControls[0]		= 1;
-	pd->bmControls[1]		= 0;
+	pd->bControlSize		= 3;
+	pd->bmControls[0]		= 64;
+	pd->bmControls[1]		= 16;
+	pd->bmControls[2]		= 1;
 	pd->iProcessing			= 0;
+	pd->bmVideoStandards		= 0;
 
 	od = &opts->uvc_output_terminal;
 	od->bLength			= UVC_DT_OUTPUT_TERMINAL_SIZE;
@@ -923,5 +925,18 @@
 }
 
 DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
+
+static int uvc_init(void)
+{
+	return usb_function_register(&uvcusb_func);
+}
+module_init(uvc_init);
+
+static void __exit uvc_exit(void)
+{
+	usb_function_unregister(&uvcusb_func);
+}
+module_exit(uvc_exit);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Laurent Pinchart");
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 78dd372..f7d2d44 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -22,7 +22,7 @@
 #define UAC2_DEF_PSRATE 48000
 #define UAC2_DEF_PSSIZE 2
 #define UAC2_DEF_CCHMASK 0x3
-#define UAC2_DEF_CSRATE 64000
+#define UAC2_DEF_CSRATE 44100
 #define UAC2_DEF_CSSIZE 2
 
 struct f_uac2_opts {
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 31125a4..8820e11 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -144,7 +144,7 @@
 	h->desc.bLength			= UVC_DT_HEADER_SIZE(1);
 	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
 	h->desc.bDescriptorSubType	= UVC_VC_HEADER;
-	h->desc.bcdUVC			= cpu_to_le16(0x0100);
+	h->desc.bcdUVC			= cpu_to_le16(0x0150);
 	h->desc.dwClockFrequency	= cpu_to_le32(48000000);
 
 	config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
@@ -626,14 +626,21 @@
 	struct config_group	group;
 } uvcg_mjpeg_grp;
 
+/* streaming/h264 */
+static struct uvcg_h264_grp {
+	struct config_group	group;
+} uvcg_h264_grp;
+
 static struct config_item *fmt_parent[] = {
 	&uvcg_uncompressed_grp.group.cg_item,
 	&uvcg_mjpeg_grp.group.cg_item,
+	&uvcg_h264_grp.group.cg_item,
 };
 
 enum uvcg_format_type {
 	UVCG_UNCOMPRESSED = 0,
 	UVCG_MJPEG,
+	UVCG_H264,
 };
 
 struct uvcg_format {
@@ -918,20 +925,11 @@
 
 /* streaming/<mode>/<format>/<NAME> */
 struct uvcg_frame {
-	struct {
-		u8	b_length;
-		u8	b_descriptor_type;
-		u8	b_descriptor_subtype;
-		u8	b_frame_index;
-		u8	bm_capabilities;
-		u16	w_width;
-		u16	w_height;
-		u32	dw_min_bit_rate;
-		u32	dw_max_bit_rate;
-		u32	dw_max_video_frame_buffer_size;
-		u32	dw_default_frame_interval;
-		u8	b_frame_interval_type;
-	} __attribute__((packed)) frame;
+	union {
+		struct uvc_frame_uncompressed uf;
+		struct uvc_frame_mjpeg mf;
+		struct uvc_frame_h264 hf;
+	} frame;
 	u32 *dw_frame_interval;
 	enum uvcg_format_type	fmt_type;
 	struct config_item	item;
@@ -942,8 +940,9 @@
 	return container_of(item, struct uvcg_frame, item);
 }
 
-#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
-static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\
+#define UVCG_FRAME_ATTR(cname, fname, to_cpu_endian, to_little_endian, bits) \
+static ssize_t uvcg_frame_##fname##_##cname##_show(struct config_item *item, \
+							char *page)	\
 {									\
 	struct uvcg_frame *f = to_uvcg_frame(item);			\
 	struct f_uvc_opts *opts;					\
@@ -957,14 +956,15 @@
 	opts = to_f_uvc_opts(opts_item);				\
 									\
 	mutex_lock(&opts->lock);					\
-	result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname));	\
+	result = snprintf(page, PAGE_SIZE, "%d\n",			\
+			to_cpu_endian(f->frame.fname.cname));		\
 	mutex_unlock(&opts->lock);					\
 									\
 	mutex_unlock(su_mutex);						\
 	return result;							\
 }									\
 									\
-static ssize_t  uvcg_frame_##cname##_store(struct config_item *item,	\
+static ssize_t  uvcg_frame_##fname##_##cname##_store(struct config_item *item, \
 					   const char *page, size_t len)\
 {									\
 	struct uvcg_frame *f = to_uvcg_frame(item);			\
@@ -991,7 +991,7 @@
 		goto end;						\
 	}								\
 									\
-	f->frame.cname = to_little_endian(num);				\
+	f->frame.fname.cname = to_little_endian(num);			\
 	ret = len;							\
 end:									\
 	mutex_unlock(&opts->lock);					\
@@ -999,21 +999,46 @@
 	return ret;							\
 }									\
 									\
-UVC_ATTR(uvcg_frame_, cname, aname);
+UVC_ATTR(uvcg_frame_, fname##_##cname, cname);
 
 #define noop_conversion(x) (x)
 
-UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
+/* Declare configurable frame attributes for uncompressed format */
+UVCG_FRAME_ATTR(bmCapabilities, uf, noop_conversion,
 		noop_conversion, 8);
-UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
-UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
-UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
-UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
-UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
+UVCG_FRAME_ATTR(wWidth, uf, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(wHeight, uf, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dwMinBitRate, uf, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwMaxBitRate, uf, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwMaxVideoFrameBufferSize, uf,
 		le32_to_cpu, cpu_to_le32, 32);
-UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
+UVCG_FRAME_ATTR(dwDefaultFrameInterval, uf,
 		le32_to_cpu, cpu_to_le32, 32);
 
+/* Declare configurable frame attributes for mjpeg format */
+UVCG_FRAME_ATTR(bmCapabilities, mf, noop_conversion,
+		noop_conversion, 8);
+UVCG_FRAME_ATTR(wWidth, mf, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(wHeight, mf, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dwMinBitRate, mf, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwMaxBitRate, mf, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwMaxVideoFrameBufferSize, mf,
+		le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwDefaultFrameInterval, mf,
+		le32_to_cpu, cpu_to_le32, 32);
+
+/* Declare configurable frame attributes for h264 format */
+UVCG_FRAME_ATTR(bmCapabilities, hf, noop_conversion,
+		noop_conversion, 8);
+UVCG_FRAME_ATTR(wWidth, hf, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(wHeight, hf, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dwMinBitRate, hf, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwMaxBitRate, hf, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dwDefaultFrameInterval, hf,
+		le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(bLevelIDC, hf, noop_conversion,
+		noop_conversion, 8);
+
 #undef noop_conversion
 
 #undef UVCG_FRAME_ATTR
@@ -1025,7 +1050,7 @@
 	struct f_uvc_opts *opts;
 	struct config_item *opts_item;
 	struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
-	int result, i;
+	int result, i, n;
 	char *pg = page;
 
 	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
@@ -1034,7 +1059,15 @@
 	opts = to_f_uvc_opts(opts_item);
 
 	mutex_lock(&opts->lock);
-	for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
+	n = 0;
+	if (frm->fmt_type == UVCG_UNCOMPRESSED)
+		n = frm->frame.uf.bFrameIntervalType;
+	else if (frm->fmt_type == UVCG_MJPEG)
+		n = frm->frame.mf.bFrameIntervalType;
+	else if (frm->fmt_type == UVCG_H264)
+		n = frm->frame.hf.bNumFrameIntervals;
+
+	for (result = 0, i = 0; i < n; ++i) {
 		result += sprintf(pg, "%d\n",
 				  le32_to_cpu(frm->dw_frame_interval[i]));
 		pg = page + result;
@@ -1137,7 +1170,13 @@
 
 	kfree(ch->dw_frame_interval);
 	ch->dw_frame_interval = frm_intrv;
-	ch->frame.b_frame_interval_type = n;
+	if (ch->fmt_type == UVCG_UNCOMPRESSED)
+		ch->frame.uf.bFrameIntervalType = n;
+	else if (ch->fmt_type == UVCG_MJPEG)
+		ch->frame.mf.bFrameIntervalType = n;
+	else if (ch->fmt_type == UVCG_H264)
+		ch->frame.hf.bNumFrameIntervals = n;
+
 	ret = len;
 
 end:
@@ -1148,20 +1187,54 @@
 
 UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval);
 
-static struct configfs_attribute *uvcg_frame_attrs[] = {
-	&uvcg_frame_attr_bm_capabilities,
-	&uvcg_frame_attr_w_width,
-	&uvcg_frame_attr_w_height,
-	&uvcg_frame_attr_dw_min_bit_rate,
-	&uvcg_frame_attr_dw_max_bit_rate,
-	&uvcg_frame_attr_dw_max_video_frame_buffer_size,
-	&uvcg_frame_attr_dw_default_frame_interval,
+static struct configfs_attribute *uvcg_uncompressed_frame_attrs[] = {
+	&uvcg_frame_attr_uf_bmCapabilities,
+	&uvcg_frame_attr_uf_wWidth,
+	&uvcg_frame_attr_uf_wHeight,
+	&uvcg_frame_attr_uf_dwMinBitRate,
+	&uvcg_frame_attr_uf_dwMaxBitRate,
+	&uvcg_frame_attr_uf_dwMaxVideoFrameBufferSize,
+	&uvcg_frame_attr_uf_dwDefaultFrameInterval,
 	&uvcg_frame_attr_dw_frame_interval,
 	NULL,
 };
 
-static struct config_item_type uvcg_frame_type = {
-	.ct_attrs	= uvcg_frame_attrs,
+static struct configfs_attribute *uvcg_mjpeg_frame_attrs[] = {
+	&uvcg_frame_attr_mf_bmCapabilities,
+	&uvcg_frame_attr_mf_wWidth,
+	&uvcg_frame_attr_mf_wHeight,
+	&uvcg_frame_attr_mf_dwMinBitRate,
+	&uvcg_frame_attr_mf_dwMaxBitRate,
+	&uvcg_frame_attr_mf_dwMaxVideoFrameBufferSize,
+	&uvcg_frame_attr_mf_dwDefaultFrameInterval,
+	&uvcg_frame_attr_dw_frame_interval,
+	NULL,
+};
+
+static struct configfs_attribute *uvcg_h264_frame_attrs[] = {
+	&uvcg_frame_attr_hf_bmCapabilities,
+	&uvcg_frame_attr_hf_wWidth,
+	&uvcg_frame_attr_hf_wHeight,
+	&uvcg_frame_attr_hf_bLevelIDC,
+	&uvcg_frame_attr_hf_dwMinBitRate,
+	&uvcg_frame_attr_hf_dwMaxBitRate,
+	&uvcg_frame_attr_hf_dwDefaultFrameInterval,
+	&uvcg_frame_attr_dw_frame_interval,
+	NULL,
+};
+
+static struct config_item_type uvcg_uncompressed_frame_type = {
+	.ct_attrs	= uvcg_uncompressed_frame_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item_type uvcg_mjpeg_frame_type = {
+	.ct_attrs	= uvcg_mjpeg_frame_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item_type uvcg_h264_frame_type = {
+	.ct_attrs	= uvcg_h264_frame_attrs,
 	.ct_owner	= THIS_MODULE,
 };
 
@@ -1172,19 +1245,17 @@
 	struct uvcg_format *fmt;
 	struct f_uvc_opts *opts;
 	struct config_item *opts_item;
+	struct config_item_type *uvcg_frame_config_item;
+	struct uvc_frame_uncompressed *uf;
 
 	h = kzalloc(sizeof(*h), GFP_KERNEL);
 	if (!h)
 		return ERR_PTR(-ENOMEM);
 
-	h->frame.b_descriptor_type		= USB_DT_CS_INTERFACE;
-	h->frame.b_frame_index			= 1;
-	h->frame.w_width			= cpu_to_le16(640);
-	h->frame.w_height			= cpu_to_le16(360);
-	h->frame.dw_min_bit_rate		= cpu_to_le32(18432000);
-	h->frame.dw_max_bit_rate		= cpu_to_le32(55296000);
-	h->frame.dw_max_video_frame_buffer_size	= cpu_to_le32(460800);
-	h->frame.dw_default_frame_interval	= cpu_to_le32(666666);
+	uf = &h->frame.uf;
+
+	uf->bDescriptorType		= USB_DT_CS_INTERFACE;
+	uf->bFrameIndex			= 1;
 
 	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
 	opts = to_f_uvc_opts(opts_item);
@@ -1192,11 +1263,52 @@
 	mutex_lock(&opts->lock);
 	fmt = to_uvcg_format(&group->cg_item);
 	if (fmt->type == UVCG_UNCOMPRESSED) {
-		h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED;
+		uf->bDescriptorSubType	= UVC_VS_FRAME_UNCOMPRESSED;
+		uf->wWidth			= cpu_to_le16(640);
+		uf->wHeight			= cpu_to_le16(360);
+		uf->dwMinBitRate		= cpu_to_le32(18432000);
+		uf->dwMaxBitRate		= cpu_to_le32(55296000);
+		uf->dwMaxVideoFrameBufferSize	= cpu_to_le32(460800);
+		uf->dwDefaultFrameInterval	= cpu_to_le32(666666);
+
 		h->fmt_type = UVCG_UNCOMPRESSED;
+		uvcg_frame_config_item = &uvcg_uncompressed_frame_type;
 	} else if (fmt->type == UVCG_MJPEG) {
-		h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
+		struct uvc_frame_mjpeg *mf = &h->frame.mf;
+
+		mf->bDescriptorType		= USB_DT_CS_INTERFACE;
+		mf->bFrameIndex			= 1;
+		mf->bDescriptorSubType	= UVC_VS_FRAME_MJPEG;
+		mf->wWidth			= cpu_to_le16(640);
+		mf->wHeight			= cpu_to_le16(360);
+		mf->dwMinBitRate		= cpu_to_le32(18432000);
+		mf->dwMaxBitRate		= cpu_to_le32(55296000);
+		mf->dwMaxVideoFrameBufferSize	= cpu_to_le32(460800);
+		mf->dwDefaultFrameInterval	= cpu_to_le32(666666);
+
 		h->fmt_type = UVCG_MJPEG;
+		uvcg_frame_config_item = &uvcg_mjpeg_frame_type;
+	} else if (fmt->type == UVCG_H264) {
+		struct uvc_frame_h264 *hf = &h->frame.hf;
+
+		hf->bDescriptorSubType	= UVC_VS_FRAME_H264;
+		hf->wWidth			= cpu_to_le16(1920);
+		hf->wHeight			= cpu_to_le16(960);
+		hf->dwMinBitRate		= cpu_to_le32(29491200);
+		hf->dwMaxBitRate		= cpu_to_le32(100000000);
+		hf->dwDefaultFrameInterval	= cpu_to_le32(333667);
+		hf->wSARwidth		= 1;
+		hf->wSARheight		= 1;
+		hf->wProfile		= 0x6400;
+		hf->bLevelIDC		= 0x33;
+		hf->bmSupportedUsages	= 0x70003;
+		hf->wConstrainedToolset	= cpu_to_le16(0);
+		hf->bmCapabilities	= 0x47;
+		hf->bmSVCCapabilities	= 0x4;
+		hf->bmMVCCapabilities	= 0;
+
+		h->fmt_type = UVCG_H264;
+		uvcg_frame_config_item = &uvcg_h264_frame_type;
 	} else {
 		mutex_unlock(&opts->lock);
 		kfree(h);
@@ -1205,7 +1317,7 @@
 	++fmt->num_frames;
 	mutex_unlock(&opts->lock);
 
-	config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+	config_item_init_type_name(&h->item, name, uvcg_frame_config_item);
 
 	return &h->item;
 }
@@ -1678,6 +1790,219 @@
 	.ct_owner	= THIS_MODULE,
 };
 
+/* streaming/h264/<NAME> */
+struct uvcg_h264 {
+	struct uvcg_format		fmt;
+	struct uvc_format_h264		desc;
+};
+
+static struct uvcg_h264 *to_uvcg_h264(struct config_item *item)
+{
+	return container_of(
+		container_of(to_config_group(item), struct uvcg_format, group),
+		struct uvcg_h264, fmt);
+}
+
+static struct configfs_group_operations uvcg_h264_group_ops = {
+	.make_item		= uvcg_frame_make,
+	.drop_item		= uvcg_frame_drop,
+};
+
+#define UVCG_H264_ATTR_RO(cname, aname, conv)				\
+static ssize_t uvcg_h264_##cname##_show(struct config_item *item, char *page)\
+{									\
+	struct uvcg_h264 *u = to_uvcg_h264(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = snprintf(page, PAGE_SIZE, "%d\n",			\
+					conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_h264_, cname, aname)
+
+#define UVCG_H264_ATTR(cname, aname, conv)				\
+static ssize_t uvcg_h264_##cname##_show(struct config_item *item, char *page)\
+{									\
+	struct uvcg_h264 *u = to_uvcg_h264(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = snprintf(page, PAGE_SIZE, "%d\n",			\
+					conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_h264_##cname##_store(struct config_item *item,			\
+			   const char *page, size_t len)		\
+{									\
+	struct uvcg_h264 *u = to_uvcg_h264(item);			\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int ret;							\
+	u8 num;								\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (u->fmt.linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou8(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > 255) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	u->desc.aname = num;						\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(uvcg_h264_, cname, aname)
+
+#define identity_conv(x) (x)
+
+UVCG_H264_ATTR(b_default_frame_index, bDefaultFrameIndex,
+		       identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_H264_ATTR
+#undef UVCG_H264_ATTR_RO
+
+static inline ssize_t
+uvcg_h264_bma_controls_show(struct config_item *item, char *page)
+{
+	struct uvcg_h264 *u = to_uvcg_h264(item);
+
+	return uvcg_format_bma_controls_show(&u->fmt, page);
+}
+
+static inline ssize_t
+uvcg_h264_bma_controls_store(struct config_item *item,
+				     const char *page, size_t len)
+{
+	struct uvcg_h264 *u = to_uvcg_h264(item);
+
+	return uvcg_format_bma_controls_store(&u->fmt, page, len);
+}
+
+UVC_ATTR(uvcg_h264_, bma_controls, bmaControls);
+
+static struct configfs_attribute *uvcg_h264_attrs[] = {
+	&uvcg_h264_attr_b_default_frame_index,
+	&uvcg_h264_attr_bma_controls,
+	NULL,
+};
+
+static struct config_item_type uvcg_h264_type = {
+	.ct_group_ops	= &uvcg_h264_group_ops,
+	.ct_attrs	= uvcg_h264_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_h264_make(struct config_group *group,
+						   const char *name)
+{
+	struct uvcg_h264 *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_FORMAT_H264_SIZE;
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_H264;
+	h->desc.bDefaultFrameIndex	= 1;
+	h->desc.bMaxCodecConfigDelay	= 0x4;
+	h->desc.bmSupportedSliceModes			= 0;
+	h->desc.bmSupportedSyncFrameTypes		= 0x76;
+	h->desc.bResolutionScaling			= 0;
+	h->desc.Reserved1				= 0;
+	h->desc.bmSupportedRateControlModes		= 0x3F;
+	h->desc.wMaxMBperSecOneResNoScalability	= cpu_to_le16(972);
+	h->desc.wMaxMBperSecTwoResNoScalability	= 0;
+	h->desc.wMaxMBperSecThreeResNoScalability	= 0;
+	h->desc.wMaxMBperSecFourResNoScalability	= 0;
+	h->desc.wMaxMBperSecOneResTemporalScalability	= cpu_to_le16(972);
+	h->desc.wMaxMBperSecTwoResTemporalScalability	= 0;
+	h->desc.wMaxMBperSecThreeResTemporalScalability	= 0;
+	h->desc.wMaxMBperSecFourResTemporalScalability		= 0;
+	h->desc.wMaxMBperSecOneResTemporalQualityScalability	=
+							cpu_to_le16(972);
+	h->desc.wMaxMBperSecTwoResTemporalQualityScalability	= 0;
+	h->desc.wMaxMBperSecThreeResTemporalQualityScalability	= 0;
+	h->desc.wMaxMBperSecFourResTemporalQualityScalability	= 0;
+	h->desc.wMaxMBperSecOneResTemporalSpatialScalability	= 0;
+	h->desc.wMaxMBperSecTwoResTemporalSpatialScalability	= 0;
+	h->desc.wMaxMBperSecThreeResTemporalSpatialScalability	= 0;
+	h->desc.wMaxMBperSecFourResTemporalSpatialScalability	= 0;
+	h->desc.wMaxMBperSecOneResFullScalability		= 0;
+	h->desc.wMaxMBperSecTwoResFullScalability		= 0;
+	h->desc.wMaxMBperSecThreeResFullScalability		= 0;
+	h->desc.wMaxMBperSecFourResFullScalability		= 0;
+
+	h->fmt.type = UVCG_H264;
+	config_group_init_type_name(&h->fmt.group, name,
+				    &uvcg_h264_type);
+
+	return &h->fmt.group;
+}
+
+static void uvcg_h264_drop(struct config_group *group,
+			    struct config_item *item)
+{
+	struct uvcg_h264 *h = to_uvcg_h264(item);
+
+	kfree(h);
+}
+
+static struct configfs_group_operations uvcg_h264_grp_ops = {
+	.make_group		= uvcg_h264_make,
+	.drop_item		= uvcg_h264_drop,
+};
+
+static struct config_item_type uvcg_h264_grp_type = {
+	.ct_group_ops	= &uvcg_h264_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
 /* streaming/color_matching/default */
 static struct uvcg_default_color_matching {
 	struct config_group	group;
@@ -1873,6 +2198,11 @@
 				container_of(fmt, struct uvcg_mjpeg, fmt);
 
 			*size += sizeof(m->desc);
+		} else if (fmt->type == UVCG_H264) {
+			struct uvcg_h264 *h =
+				container_of(fmt, struct uvcg_h264, fmt);
+
+			*size += sizeof(h->desc);
 		} else {
 			return -EINVAL;
 		}
@@ -1880,10 +2210,23 @@
 	break;
 	case UVCG_FRAME: {
 		struct uvcg_frame *frm = priv1;
-		int sz = sizeof(frm->dw_frame_interval);
 
-		*size += sizeof(frm->frame);
-		*size += frm->frame.b_frame_interval_type * sz;
+		if (frm->fmt_type == UVCG_UNCOMPRESSED) {
+			struct uvc_frame_uncompressed uf =
+				frm->frame.uf;
+			*size +=
+			UVC_DT_FRAME_UNCOMPRESSED_SIZE(uf.bFrameIntervalType);
+		} else if (frm->fmt_type == UVCG_MJPEG) {
+			struct uvc_frame_mjpeg mf =
+				frm->frame.mf;
+			*size +=
+			UVC_DT_FRAME_UNCOMPRESSED_SIZE(mf.bFrameIntervalType);
+		} else if (frm->fmt_type == UVCG_H264) {
+			struct uvc_frame_h264 hf =
+				frm->frame.hf;
+			*size +=
+			UVC_DT_FRAME_UNCOMPRESSED_SIZE(hf.bNumFrameIntervals);
+		}
 	}
 	break;
 	}
@@ -1949,6 +2292,15 @@
 			*dest += sizeof(m->desc);
 			mjp->bNumFrameDescriptors = fmt->num_frames;
 			mjp->bFormatIndex = n + 1;
+		} else if (fmt->type == UVCG_H264) {
+			struct uvc_format_h264 *hf = *dest;
+			struct uvcg_h264 *h =
+				container_of(fmt, struct uvcg_h264, fmt);
+
+			memcpy(*dest, &h->desc, sizeof(h->desc));
+			*dest += sizeof(h->desc);
+			hf->bNumFrameDescriptors = fmt->num_frames;
+			hf->bFormatIndex = n + 1;
 		} else {
 			return -EINVAL;
 		}
@@ -1956,21 +2308,46 @@
 	break;
 	case UVCG_FRAME: {
 		struct uvcg_frame *frm = priv1;
-		struct uvc_descriptor_header *h = *dest;
 
-		sz = sizeof(frm->frame);
-		memcpy(*dest, &frm->frame, sz);
-		*dest += sz;
-		sz = frm->frame.b_frame_interval_type *
-			sizeof(*frm->dw_frame_interval);
+		if (frm->fmt_type == UVCG_UNCOMPRESSED) {
+			struct uvc_frame_uncompressed *uf =
+				&frm->frame.uf;
+			uf->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
+				uf->bFrameIntervalType);
+			uf->bFrameIndex = n+1;
+			sz = UVC_DT_FRAME_UNCOMPRESSED_SIZE(0);
+			memcpy(*dest, uf, sz);
+			*dest += sz;
+			sz = uf->bFrameIntervalType *
+				sizeof(*frm->dw_frame_interval);
+		} else if (frm->fmt_type == UVCG_MJPEG) {
+			struct uvc_frame_mjpeg *mf =
+				&frm->frame.mf;
+			mf->bLength = UVC_DT_FRAME_MJPEG_SIZE(
+				mf->bFrameIntervalType);
+			mf->bFrameIndex = n+1;
+			sz = UVC_DT_FRAME_MJPEG_SIZE(0);
+			memcpy(*dest, mf, sz);
+			*dest += sz;
+			sz = mf->bFrameIntervalType *
+				sizeof(*frm->dw_frame_interval);
+		} else if (frm->fmt_type == UVCG_H264) {
+			struct uvc_frame_h264 *hf =
+				&frm->frame.hf;
+			hf->bLength = UVC_DT_FRAME_H264_SIZE(
+				hf->bNumFrameIntervals);
+			hf->bFrameIndex = n+1;
+			sz = UVC_DT_FRAME_H264_SIZE(0);
+			memcpy(*dest, hf, sz);
+			*dest += sz;
+			sz = hf->bNumFrameIntervals *
+				sizeof(*frm->dw_frame_interval);
+		} else {
+			return -EINVAL;
+		}
+
 		memcpy(*dest, frm->dw_frame_interval, sz);
 		*dest += sz;
-		if (frm->fmt_type == UVCG_UNCOMPRESSED)
-			h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
-				frm->frame.b_frame_interval_type);
-		else if (frm->fmt_type == UVCG_MJPEG)
-			h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
-				frm->frame.b_frame_interval_type);
 	}
 	break;
 	}
@@ -2183,7 +2560,7 @@
 	return ret;							\
 }									\
 									\
-UVC_ATTR(f_uvc_opts_, cname, aname)
+UVC_ATTR(f_uvc_opts_, cname, cname)
 
 #define identity_conv(x) (x)
 
@@ -2278,6 +2655,9 @@
 	config_group_init_type_name(&uvcg_mjpeg_grp.group,
 				    "mjpeg",
 				    &uvcg_mjpeg_grp_type);
+	config_group_init_type_name(&uvcg_h264_grp.group,
+				    "h264",
+				    &uvcg_h264_grp_type);
 	config_group_init_type_name(&uvcg_default_color_matching.group,
 				    "default",
 				    &uvcg_default_color_matching_type);
@@ -2310,6 +2690,8 @@
 			&uvcg_streaming_grp.group);
 	configfs_add_default_group(&uvcg_mjpeg_grp.group,
 			&uvcg_streaming_grp.group);
+	configfs_add_default_group(&uvcg_h264_grp.group,
+			&uvcg_streaming_grp.group);
 	configfs_add_default_group(&uvcg_color_matching_grp.group,
 			&uvcg_streaming_grp.group);
 	configfs_add_default_group(&uvcg_streaming_class_grp.group,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1332057..ab3633c 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5045,6 +5045,61 @@
 	return xhci->core_id;
 }
 
+static int  xhci_stop_endpoint(struct usb_hcd *hcd,
+	struct usb_device *udev, struct usb_host_endpoint *ep)
+{
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	unsigned int ep_index;
+	struct xhci_virt_device *virt_dev;
+	struct xhci_command *cmd;
+	unsigned long flags;
+	int ret = 0;
+
+	cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
+	if (!cmd)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&xhci->lock, flags);
+	virt_dev = xhci->devs[udev->slot_id];
+	if (!virt_dev) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ep_index = xhci_get_endpoint_index(&ep->desc);
+	if (virt_dev->eps[ep_index].ring &&
+			virt_dev->eps[ep_index].ring->dequeue) {
+		ret = xhci_queue_stop_endpoint(xhci, cmd, udev->slot_id,
+				ep_index, 0);
+		if (ret)
+			goto err;
+
+		xhci_ring_cmd_db(xhci);
+		spin_unlock_irqrestore(&xhci->lock, flags);
+
+		/* Wait for stop endpoint command to finish */
+		wait_for_completion(cmd->completion);
+
+		if (cmd->status == COMP_CMD_ABORT ||
+				cmd->status == COMP_CMD_STOP) {
+			xhci_warn(xhci,
+				"stop endpoint command timeout for ep%d%s\n",
+				usb_endpoint_num(&ep->desc),
+				usb_endpoint_dir_in(&ep->desc) ? "in" : "out");
+			ret = -ETIME;
+		}
+		goto free_cmd;
+	}
+
+err:
+	spin_unlock_irqrestore(&xhci->lock, flags);
+free_cmd:
+	xhci_free_command(xhci, cmd);
+	return ret;
+}
+
+
+
 static const struct hc_driver xhci_hc_driver = {
 	.description =		"xhci-hcd",
 	.product_desc =		"xHCI Host Controller",
@@ -5109,6 +5164,7 @@
 	.get_sec_event_ring_phys_addr =	xhci_get_sec_event_ring_phys_addr,
 	.get_xfer_ring_phys_addr =	xhci_get_xfer_ring_phys_addr,
 	.get_core_id =			xhci_get_core_id,
+	.stop_endpoint =		xhci_stop_endpoint,
 };
 
 void xhci_init_driver(struct hc_driver *drv,
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index 1d9a8c4..57b0902 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -92,7 +92,8 @@
 err_brelse:
 	brelse(bhs[0]);
 err:
-	fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)", (llu)blocknr);
+	fat_msg_ratelimit(sb, KERN_ERR,
+			"FAT read failed (blocknr %llu)", (llu)blocknr);
 	return -EIO;
 }
 
@@ -105,8 +106,8 @@
 	fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
 	fatent->bhs[0] = sb_bread(sb, blocknr);
 	if (!fatent->bhs[0]) {
-		fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)",
-		       (llu)blocknr);
+		fat_msg_ratelimit(sb, KERN_ERR,
+			"FAT read failed (blocknr %llu)", (llu)blocknr);
 		return -EIO;
 	}
 	fatent->nr_bhs = 1;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index a2c05f2..0b6ba8c 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -843,8 +843,9 @@
 	fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset);
 	bh = sb_bread(sb, blocknr);
 	if (!bh) {
-		fat_msg(sb, KERN_ERR, "unable to read inode block "
-		       "for updating (i_pos %lld)", i_pos);
+		fat_msg_ratelimit(sb, KERN_ERR,
+			"unable to read inode block for updating (i_pos %lld)",
+			i_pos);
 		return -EIO;
 	}
 	spin_lock(&sbi->inode_hash_lock);
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 20fa8d8..611e3ae 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -42,6 +42,39 @@
 
 #endif
 
+/* Generic helpers for ELF use */
+/* Return first section header */
+static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
+{
+	return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
+}
+
+/* Return idx section header */
+static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
+{
+	return &elf_sheader(hdr)[idx];
+}
+
+/* Return first program header */
+static inline struct elf_phdr *elf_pheader(struct elfhdr *hdr)
+{
+	return (struct elf_phdr *)((size_t)hdr + (size_t)hdr->e_phoff);
+}
+
+/* Return idx program header */
+static inline struct elf_phdr *elf_program(struct elfhdr *hdr, int idx)
+{
+	return &elf_pheader(hdr)[idx];
+}
+
+/* Retunr section's string table header */
+static inline char *elf_str_table(struct elfhdr *hdr)
+{
+	if (hdr->e_shstrndx == SHN_UNDEF)
+		return NULL;
+	return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
+}
+
 /* Optional callbacks to write extra ELF notes. */
 struct file;
 struct coredump_params;
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index dd6849d..405aed5 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -1175,6 +1175,28 @@
 	u64 size;
 };
 
+/**
+ * struct  ipa_smmu_in_params - information provided from client
+ * @ipa_smmu_client_type: clinet requesting for the smmu info.
+ */
+
+enum ipa_smmu_client_type {
+	IPA_SMMU_WLAN_CLIENT,
+	IPA_SMMU_CLIENT_MAX
+};
+
+struct ipa_smmu_in_params {
+	enum ipa_smmu_client_type smmu_client;
+};
+
+/**
+ * struct  ipa_smmu_out_params - information provided to IPA client
+ * @ipa_smmu_s1_enable: IPA S1 SMMU enable/disable status
+ */
+struct ipa_smmu_out_params {
+	bool smmu_enable;
+};
+
 #if defined CONFIG_IPA || defined CONFIG_IPA3
 
 /*
@@ -1564,6 +1586,9 @@
  */
 int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
 
+int ipa_get_smmu_params(struct ipa_smmu_in_params *in,
+	struct ipa_smmu_out_params *out);
+
 #else /* (CONFIG_IPA || CONFIG_IPA3) */
 
 /*
@@ -2351,6 +2376,12 @@
 	return -EPERM;
 }
 
+
+static inline int ipa_get_smmu_params(struct ipa_smmu_in_params *in,
+	struct ipa_smmu_out_params *out)
+{
+	return -EPERM;
+}
 #endif /* (CONFIG_IPA || CONFIG_IPA3) */
 
 #endif /* _IPA_H_ */
diff --git a/include/linux/msm_ep_pcie.h b/include/linux/msm_ep_pcie.h
new file mode 100644
index 0000000..a1d2a17
--- /dev/null
+++ b/include/linux/msm_ep_pcie.h
@@ -0,0 +1,290 @@
+/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_EP_PCIE_H
+#define __MSM_EP_PCIE_H
+
+#include <linux/types.h>
+
+enum ep_pcie_link_status {
+	EP_PCIE_LINK_DISABLED,
+	EP_PCIE_LINK_UP,
+	EP_PCIE_LINK_ENABLED,
+};
+
+enum ep_pcie_event {
+	EP_PCIE_EVENT_INVALID = 0,
+	EP_PCIE_EVENT_PM_D0 = 0x1,
+	EP_PCIE_EVENT_PM_D3_HOT = 0x2,
+	EP_PCIE_EVENT_PM_D3_COLD = 0x4,
+	EP_PCIE_EVENT_PM_RST_DEAST = 0x8,
+	EP_PCIE_EVENT_LINKDOWN = 0x10,
+	EP_PCIE_EVENT_LINKUP = 0x20,
+	EP_PCIE_EVENT_MHI_A7 = 0x40,
+	EP_PCIE_EVENT_MMIO_WRITE = 0x80,
+};
+
+enum ep_pcie_irq_event {
+	EP_PCIE_INT_EVT_LINK_DOWN = 1,
+	EP_PCIE_INT_EVT_BME,
+	EP_PCIE_INT_EVT_PM_TURNOFF,
+	EP_PCIE_INT_EVT_DEBUG,
+	EP_PCIE_INT_EVT_LTR,
+	EP_PCIE_INT_EVT_MHI_Q6,
+	EP_PCIE_INT_EVT_MHI_A7,
+	EP_PCIE_INT_EVT_DSTATE_CHANGE,
+	EP_PCIE_INT_EVT_L1SUB_TIMEOUT,
+	EP_PCIE_INT_EVT_MMIO_WRITE,
+	EP_PCIE_INT_EVT_CFG_WRITE,
+	EP_PCIE_INT_EVT_BRIDGE_FLUSH_N,
+	EP_PCIE_INT_EVT_LINK_UP,
+	EP_PCIE_INT_EVT_MAX = 13,
+};
+
+enum ep_pcie_trigger {
+	EP_PCIE_TRIGGER_CALLBACK,
+	EP_PCIE_TRIGGER_COMPLETION,
+};
+
+enum ep_pcie_options {
+	EP_PCIE_OPT_NULL = 0,
+	EP_PCIE_OPT_AST_WAKE = 0x1,
+	EP_PCIE_OPT_POWER_ON = 0x2,
+	EP_PCIE_OPT_ENUM = 0x4,
+	EP_PCIE_OPT_ENUM_ASYNC = 0x8,
+	EP_PCIE_OPT_ALL = 0xFFFFFFFF,
+};
+
+struct ep_pcie_notify {
+	enum ep_pcie_event event;
+	void *user;
+	void *data;
+	u32 options;
+};
+
+struct ep_pcie_register_event {
+	u32 events;
+	void *user;
+	enum ep_pcie_trigger mode;
+	void (*callback)(struct ep_pcie_notify *notify);
+	struct ep_pcie_notify notify;
+	struct completion *completion;
+	u32 options;
+};
+
+struct ep_pcie_iatu {
+	u32 start;
+	u32 end;
+	u32 tgt_lower;
+	u32 tgt_upper;
+};
+
+struct ep_pcie_msi_config {
+	u32 lower;
+	u32 upper;
+	u32 data;
+	u32 msg_num;
+};
+
+struct ep_pcie_db_config {
+	u8 base;
+	u8 end;
+	u32 tgt_addr;
+};
+
+struct ep_pcie_hw {
+	struct list_head node;
+	u32 device_id;
+	void **private_data;
+	int (*register_event)(struct ep_pcie_register_event *reg);
+	int (*deregister_event)(void);
+	enum ep_pcie_link_status (*get_linkstatus)(void);
+	int (*config_outbound_iatu)(struct ep_pcie_iatu entries[],
+				u32 num_entries);
+	int (*get_msi_config)(struct ep_pcie_msi_config *cfg);
+	int (*trigger_msi)(u32 idx);
+	int (*wakeup_host)(void);
+	int (*enable_endpoint)(enum ep_pcie_options opt);
+	int (*disable_endpoint)(void);
+	int (*config_db_routing)(struct ep_pcie_db_config chdb_cfg,
+				struct ep_pcie_db_config erdb_cfg);
+	int (*mask_irq_event)(enum ep_pcie_irq_event event,
+				bool enable);
+};
+
+/*
+ * ep_pcie_register_drv - register HW driver.
+ * @phandle:	PCIe endpoint HW driver handle
+ *
+ * This function registers PCIe HW driver to PCIe endpoint service
+ * layer.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_register_drv(struct ep_pcie_hw *phandle);
+
+/*
+ * ep_pcie_deregister_drv - deregister HW driver.
+ * @phandle:	PCIe endpoint HW driver handle
+ *
+ * This function deregisters PCIe HW driver to PCIe endpoint service
+ * layer.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_deregister_drv(struct ep_pcie_hw *phandle);
+
+/*
+ * ep_pcie_get_phandle - get PCIe endpoint HW driver handle.
+ * @id:	PCIe endpoint device ID
+ *
+ * This function deregisters PCIe HW driver from PCIe endpoint service
+ * layer.
+ *
+ * Return: PCIe endpoint HW driver handle
+ */
+struct ep_pcie_hw *ep_pcie_get_phandle(u32 id);
+
+/*
+ * ep_pcie_register_event - register event with PCIe driver.
+ * @phandle:	PCIe endpoint HW driver handle
+ * @reg:	event structure
+ *
+ * This function gives PCIe client driver an option to register
+ * event with PCIe driver.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_register_event(struct ep_pcie_hw *phandle,
+	struct ep_pcie_register_event *reg);
+
+/*
+ * ep_pcie_deregister_event - deregister event with PCIe driver.
+ * @phandle:	PCIe endpoint HW driver handle
+ *
+ * This function gives PCIe client driver an option to deregister
+ * existing event with PCIe driver.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_deregister_event(struct ep_pcie_hw *phandle);
+
+/*
+ * ep_pcie_get_linkstatus - indicate the status of PCIe link.
+ * @phandle:	PCIe endpoint HW driver handle
+ *
+ * This function tells PCIe client about the status of PCIe link.
+ *
+ * Return: status of PCIe link
+ */
+enum ep_pcie_link_status ep_pcie_get_linkstatus(struct ep_pcie_hw *phandle);
+
+/*
+ * ep_pcie_config_outbound_iatu - configure outbound iATU.
+ * @entries:	iatu entries
+ * @num_entries:	number of iatu entries
+ *
+ * This function configures the outbound iATU for PCIe
+ * client's access to the regions in the host memory which
+ * are specified by the SW on host side.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_config_outbound_iatu(struct ep_pcie_hw *phandle,
+				struct ep_pcie_iatu entries[],
+				u32 num_entries);
+
+/*
+ * ep_pcie_get_msi_config - get MSI config info.
+ * @phandle:	PCIe endpoint HW driver handle
+ * @cfg:	pointer to MSI config
+ *
+ * This function returns MSI config info.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_get_msi_config(struct ep_pcie_hw *phandle,
+				struct ep_pcie_msi_config *cfg);
+
+/*
+ * ep_pcie_trigger_msi - trigger an MSI.
+ * @phandle:	PCIe endpoint HW driver handle
+ * @idx:	MSI index number
+ *
+ * This function allows PCIe client to trigger an MSI
+ * on host side.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_trigger_msi(struct ep_pcie_hw *phandle, u32 idx);
+
+/*
+ * ep_pcie_wakeup_host - wake up the host.
+ * @phandle:	PCIe endpoint HW driver handle
+ *
+ * This function asserts WAKE GPIO to wake up the host.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_wakeup_host(struct ep_pcie_hw *phandle);
+
+/*
+ * ep_pcie_enable_endpoint - enable PCIe endpoint.
+ * @phandle:	PCIe endpoint HW driver handle
+ * @opt:	endpoint enable options
+ *
+ * This function is to enable the PCIe endpoint device.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_enable_endpoint(struct ep_pcie_hw *phandle,
+				enum ep_pcie_options opt);
+
+/*
+ * ep_pcie_disable_endpoint - disable PCIe endpoint.
+ * @phandle:	PCIe endpoint HW driver handle
+ *
+ * This function is to disable the PCIe endpoint device.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_disable_endpoint(struct ep_pcie_hw *phandle);
+
+/*
+ * ep_pcie_config_db_routing - Configure routing of doorbells to another block.
+ * @phandle:	PCIe endpoint HW driver handle
+ * @chdb_cfg:	channel doorbell config
+ * @erdb_cfg:	event ring doorbell config
+ *
+ * This function allows PCIe core to route the doorbells intended
+ * for another entity via a target address.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_config_db_routing(struct ep_pcie_hw *phandle,
+				struct ep_pcie_db_config chdb_cfg,
+				struct ep_pcie_db_config erdb_cfg);
+
+/*
+ * ep_pcie_mask_irq_event - enable and disable IRQ event.
+ * @phandle:	PCIe endpoint HW driver handle
+ * @event:	IRQ event
+ * @enable:     true to enable that IRQ event and false to disable
+ *
+ * This function is to enable and disable IRQ event.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int ep_pcie_mask_irq_event(struct ep_pcie_hw *phandle,
+				enum ep_pcie_irq_event event,
+				bool enable);
+#endif
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
index d5af3c2..220380b 100644
--- a/include/linux/netfilter/nf_conntrack_sip.h
+++ b/include/linux/netfilter/nf_conntrack_sip.h
@@ -166,6 +166,11 @@
 };
 extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
 
+extern void (*nf_nat_sip_seq_adjust_hook)
+			(struct sk_buff *skb,
+			unsigned int protoff,
+			s16 off);
+
 int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr,
 			 unsigned int datalen, unsigned int *matchoff,
 			 unsigned int *matchlen, union nf_inet_addr *addr,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 232c3e0..81e8469 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -757,6 +757,9 @@
 	struct usb_host_endpoint *ep, dma_addr_t *dma);
 extern int usb_get_controller_id(struct usb_device *dev);
 
+extern int usb_stop_endpoint(struct usb_device *dev,
+	struct usb_host_endpoint *ep);
+
 /* Sets up a group of bulk endpoints to support multiple stream IDs. */
 extern int usb_alloc_streams(struct usb_interface *interface,
 		struct usb_host_endpoint **eps, unsigned int num_eps,
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 1699d2b..d070109 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -407,6 +407,8 @@
 			struct usb_device *udev, struct usb_host_endpoint *ep,
 			dma_addr_t *dma);
 	int (*get_core_id)(struct usb_hcd *hcd);
+	int (*stop_endpoint)(struct usb_hcd *hcd, struct usb_device *udev,
+			struct usb_host_endpoint *ep);
 };
 
 static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@@ -454,6 +456,8 @@
 extern phys_addr_t usb_hcd_get_xfer_ring_phys_addr(
 	struct usb_device *udev, struct usb_host_endpoint *ep, dma_addr_t *dma);
 extern int usb_hcd_get_controller_id(struct usb_device *udev);
+extern int usb_hcd_stop_endpoint(struct usb_device *udev,
+	struct usb_host_endpoint *ep);
 
 struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
 		struct device *sysdev, struct device *dev, const char *bus_name,
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index 4260d3c..1ba16f7 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -18,6 +18,7 @@
 #include <linux/compiler.h>
 #include <linux/atomic.h>
 #include <linux/rhashtable.h>
+#include <linux/list.h>
 
 #include <linux/netfilter/nf_conntrack_tcp.h>
 #include <linux/netfilter/nf_conntrack_dccp.h>
@@ -27,6 +28,14 @@
 
 #include <net/netfilter/nf_conntrack_tuple.h>
 
+#define SIP_LIST_ELEMENTS	2
+
+struct sip_length {
+	int msg_length[SIP_LIST_ELEMENTS];
+	int skb_len[SIP_LIST_ELEMENTS];
+	int data_len[SIP_LIST_ELEMENTS];
+};
+
 /* per conntrack: protocol private data */
 union nf_conntrack_proto {
 	/* insert conntrack proto private data here */
@@ -130,6 +139,11 @@
 #ifdef CONFIG_IP_NF_TARGET_NATTYPE_MODULE
 	unsigned long nattype_entry;
 #endif
+	struct list_head sip_segment_list;
+	const char *dptr_prev;
+	struct sip_length segment;
+	bool sip_original_dir;
+	bool sip_reply_dir;
 
 	/* Storage reserved for other modules, must be the last member */
 	union nf_conntrack_proto proto;
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index a0043c7..abc090c 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -20,6 +20,9 @@
 /* This header is used to share core functionality between the
    standalone connection tracking module, and the compatibility layer's use
    of connection tracking. */
+
+extern unsigned int nf_conntrack_hash_rnd;
+
 unsigned int nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
 			     struct sk_buff *skb);
 
@@ -51,7 +54,9 @@
 			const struct nf_conntrack_l3proto *l3proto,
 			const struct nf_conntrack_l4proto *l4proto);
 extern void (*delete_sfe_entry)(struct nf_conn *ct);
-extern bool (*nattype_refresh_timer)(unsigned long nattype);
+extern bool (*nattype_refresh_timer)
+			(unsigned long nattype,
+			unsigned long timeout_value);
 
 /* Find a connection corresponding to a tuple. */
 struct nf_conntrack_tuple_hash *
@@ -88,4 +93,9 @@
 
 extern spinlock_t nf_conntrack_expect_lock;
 
+struct sip_list {
+	struct nf_queue_entry *entry;
+	struct list_head list;
+};
+
 #endif /* _NF_CONNTRACK_CORE_H */
diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h
index e2c72d1..3c2aff3 100644
--- a/include/soc/qcom/cmd-db.h
+++ b/include/soc/qcom/cmd-db.h
@@ -110,17 +110,18 @@
 	return 0;
 }
 
-bool cmd_db_get_priority(u32 addr, u8 drv_id)
+static inline bool cmd_db_get_priority(u32 addr, u8 drv_id)
 {
 	return false;
 }
 
-int cmd_db_get_aux_data(const char *resource_id, u8 *data, int len)
+static inline int cmd_db_get_aux_data(const char *resource_id,
+		u8 *data, int len)
 {
 	return -ENODEV;
 }
 
-int cmd_db_get_aux_data_len(const char *resource_id)
+static inline int cmd_db_get_aux_data_len(const char *resource_id)
 {
 	return -ENODEV;
 }
diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h
new file mode 100644
index 0000000..5c751e8
--- /dev/null
+++ b/include/soc/qcom/minidump.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MINIDUMP_H
+#define __MINIDUMP_H
+
+#define MAX_NAME_LENGTH		12
+/* md_region -  Minidump table entry
+ * @name:	Entry name, Minidump will dump binary with this name.
+ * @id:		Entry ID, used only for SDI dumps.
+ * @virt_addr:  Address of the entry.
+ * @phys_addr:	Physical address of the entry to dump.
+ * @size:	Number of byte to dump from @address location
+ *		it should be 4 byte aligned.
+ */
+struct md_region {
+	char	name[MAX_NAME_LENGTH];
+	u32	id;
+	u64	virt_addr;
+	u64	phys_addr;
+	u64	size;
+};
+
+/* Register an entry in Minidump table
+ * Returns:
+ *	Zero: on successful addition
+ *	Negetive error number on failures
+ */
+#ifdef CONFIG_QCOM_MINIDUMP
+extern int msm_minidump_add_region(const struct md_region *entry);
+extern bool msm_minidump_enabled(void);
+extern void dump_stack_minidump(u64 sp);
+#else
+static inline int msm_minidump_add_region(const struct md_region *entry)
+{
+	/* Return quietly, if minidump is not supported */
+	return 0;
+}
+static inline bool msm_minidump_enabled(void) { return false; }
+static inline void dump_stack_minidump(u64 sp) {}
+#endif
+#endif
diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h
index 97eefc6..c25da0e 100644
--- a/include/trace/events/trace_msm_low_power.h
+++ b/include/trace/events/trace_msm_low_power.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -250,24 +250,6 @@
 		__entry->sample, __entry->tmr)
 );
 
-TRACE_EVENT(pre_pc_cb,
-
-	TP_PROTO(int tzflag),
-
-	TP_ARGS(tzflag),
-
-	TP_STRUCT__entry(
-		__field(int, tzflag)
-	),
-
-	TP_fast_assign(
-		__entry->tzflag = tzflag;
-	),
-
-	TP_printk("tzflag:%d",
-		__entry->tzflag
-	)
-);
 #endif
 #define TRACE_INCLUDE_FILE trace_msm_low_power
 #include <trace/define_trace.h>
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index f05155b..9ee2a8b 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -142,6 +142,7 @@
 #define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL
 #define KGSL_MEMFLAGS_SPARSE_PHYS 0x20000000ULL
 #define KGSL_MEMFLAGS_SPARSE_VIRT 0x40000000ULL
+#define KGSL_MEMFLAGS_IOCOHERENT  0x80000000ULL
 
 /* Memory types for which allocations are made */
 #define KGSL_MEMTYPE_MASK		0x0000FF00
diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
index 69ab695..dc9380b 100644
--- a/include/uapi/linux/usb/video.h
+++ b/include/uapi/linux/usb/video.h
@@ -54,6 +54,8 @@
 #define UVC_VS_FORMAT_FRAME_BASED			0x10
 #define UVC_VS_FRAME_FRAME_BASED			0x11
 #define UVC_VS_FORMAT_STREAM_BASED			0x12
+#define UVC_VS_FORMAT_H264				0x13
+#define UVC_VS_FRAME_H264				0x14
 
 /* A.7. Video Class-Specific Endpoint Descriptor Subtypes */
 #define UVC_EP_UNDEFINED				0x00
@@ -299,11 +301,12 @@
 	__u8  bSourceID;
 	__u16 wMaxMultiplier;
 	__u8  bControlSize;
-	__u8  bmControls[2];
+	__u8  bmControls[3];
 	__u8  iProcessing;
+	__u8  bmVideoStandards;
 } __attribute__((__packed__));
 
-#define UVC_DT_PROCESSING_UNIT_SIZE(n)			(9+(n))
+#define UVC_DT_PROCESSING_UNIT_SIZE(n)			(10+(n))
 
 /* 3.7.2.6. Extension Unit Descriptor */
 struct uvc_extension_unit_descriptor {
@@ -565,5 +568,96 @@
 	__u32 dwFrameInterval[n];			\
 } __attribute__ ((packed))
 
+/* H264 Payload - 3.1.1. H264 Video Format Descriptor */
+struct uvc_format_h264 {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDescriptorSubType;
+	__u8  bFormatIndex;
+	__u8  bNumFrameDescriptors;
+	__u8  bDefaultFrameIndex;
+	__u8  bMaxCodecConfigDelay;
+	__u8  bmSupportedSliceModes;
+	__u8  bmSupportedSyncFrameTypes;
+	__u8  bResolutionScaling;
+	__u8  Reserved1;
+	__u8  bmSupportedRateControlModes;
+	__u16 wMaxMBperSecOneResNoScalability;
+	__u16 wMaxMBperSecTwoResNoScalability;
+	__u16 wMaxMBperSecThreeResNoScalability;
+	__u16 wMaxMBperSecFourResNoScalability;
+	__u16 wMaxMBperSecOneResTemporalScalability;
+	__u16 wMaxMBperSecTwoResTemporalScalability;
+	__u16 wMaxMBperSecThreeResTemporalScalability;
+	__u16 wMaxMBperSecFourResTemporalScalability;
+	__u16 wMaxMBperSecOneResTemporalQualityScalability;
+	__u16 wMaxMBperSecTwoResTemporalQualityScalability;
+	__u16 wMaxMBperSecThreeResTemporalQualityScalability;
+	__u16 wMaxMBperSecFourResTemporalQualityScalability;
+	__u16 wMaxMBperSecOneResTemporalSpatialScalability;
+	__u16 wMaxMBperSecTwoResTemporalSpatialScalability;
+	__u16 wMaxMBperSecThreeResTemporalSpatialScalability;
+	__u16 wMaxMBperSecFourResTemporalSpatialScalability;
+	__u16 wMaxMBperSecOneResFullScalability;
+	__u16 wMaxMBperSecTwoResFullScalability;
+	__u16 wMaxMBperSecThreeResFullScalability;
+	__u16 wMaxMBperSecFourResFullScalability;
+} __attribute__((__packed__));
+
+#define UVC_DT_FORMAT_H264_SIZE		52
+
+/* H264 Payload - 3.1.2. H264 Video Frame Descriptor */
+struct uvc_frame_h264 {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDescriptorSubType;
+	__u8  bFrameIndex;
+	__u16 wWidth;
+	__u16 wHeight;
+	__u16 wSARwidth;
+	__u16 wSARheight;
+	__u16 wProfile;
+	__u8  bLevelIDC;
+	__u16 wConstrainedToolset;
+	__u32 bmSupportedUsages;
+	__u16 bmCapabilities;
+	__u32 bmSVCCapabilities;
+	__u32 bmMVCCapabilities;
+	__u32 dwMinBitRate;
+	__u32 dwMaxBitRate;
+	__u32 dwDefaultFrameInterval;
+	__u8  bNumFrameIntervals;
+	__u32 dwFrameInterval[];
+} __attribute__((__packed__));
+
+#define UVC_DT_FRAME_H264_SIZE(n)			(44+4*(n))
+
+#define UVC_FRAME_H264(n) \
+	uvc_frame_h264_##n
+
+#define DECLARE_UVC_FRAME_H264(n)			\
+struct UVC_FRAME_H264(n) {				\
+	__u8  bLength;					\
+	__u8  bDescriptorType;				\
+	__u8  bDescriptorSubType;			\
+	__u8  bFrameIndex;				\
+	__u16 wWidth;					\
+	__u16 wHeight;					\
+	__u16 wSARwidth;				\
+	__u16 wSARheight;				\
+	__u16 wProfile;					\
+	__u8  bLevelIDC;				\
+	__u16 wConstrainedToolset;			\
+	__u32 bmSupportedUsages;			\
+	__u16 bmCapabilities;				\
+	__u32 bmSVCCapabilities;			\
+	__u32 bmMVCCapabilities;			\
+	__u32 dwMinBitRate;				\
+	__u32 dwMaxBitRate;				\
+	__u32 dwDefaultFrameInterval;			\
+	__u8  bNumFrameIntervals;			\
+	__u32 dwFrameInterval[n];			\
+} __attribute__ ((packed))
+
 #endif /* __LINUX_USB_VIDEO_H */
 
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 85b7e87..229dd25 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -718,6 +718,8 @@
 	v4l2_fourcc('T', 'P', '1', '0') /* Y/CbCr 4:2:0 TP10 */
 #define V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010 \
 	v4l2_fourcc('P', '0', '1', '0') /* Y/CbCr 4:2:0 P10 */
+#define V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS \
+	v4l2_fourcc('Q', 'P', '1', '0') /* Y/CbCr 4:2:0 P10 Venus*/
 
 /* SDR formats - used only for Software Defined Radio devices */
 #define V4L2_SDR_FMT_CU8          v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
diff --git a/include/uapi/media/msm_sde_rotator.h b/include/uapi/media/msm_sde_rotator.h
index 212eb26..dcdbb85 100644
--- a/include/uapi/media/msm_sde_rotator.h
+++ b/include/uapi/media/msm_sde_rotator.h
@@ -61,6 +61,8 @@
 #define SDE_PIX_FMT_RGBA_1010102_UBWC	V4L2_PIX_FMT_SDE_RGBA_1010102_UBWC
 #define SDE_PIX_FMT_RGBX_1010102_UBWC	V4L2_PIX_FMT_SDE_RGBX_1010102_UBWC
 #define SDE_PIX_FMT_Y_CBCR_H2V2_P010	V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010
+#define SDE_PIX_FMT_Y_CBCR_H2V2_P010_VENUS \
+	V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010_VENUS
 #define SDE_PIX_FMT_Y_CBCR_H2V2_TP10	V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_TP10
 #define SDE_PIX_FMT_Y_CBCR_H2V2_TP10_UBWC	V4L2_PIX_FMT_NV12_TP10_UBWC
 #define SDE_PIX_FMT_Y_CBCR_H2V2_P010_UBWC	V4L2_PIX_FMT_NV12_P010_UBWC
diff --git a/kernel/panic.c b/kernel/panic.c
index fcc8786..d797170 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -27,6 +27,7 @@
 #include <linux/bug.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/exception.h>
+#include <soc/qcom/minidump.h>
 
 #define PANIC_TIMER_STEP 100
 #define PANIC_BLINK_SPD 18
@@ -174,6 +175,7 @@
 	va_start(args, fmt);
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
+	dump_stack_minidump(0);
 	pr_emerg("Kernel panic - not syncing: %s\n", buf);
 #ifdef CONFIG_DEBUG_BUGVERBOSE
 	/*
diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c
index 9d9f0bf..d3bcd5c 100644
--- a/kernel/trace/msm_rtb.c
+++ b/kernel/trace/msm_rtb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -28,6 +28,7 @@
 #include <asm-generic/sizes.h>
 #include <linux/msm_rtb.h>
 #include <asm/timex.h>
+#include <soc/qcom/minidump.h>
 
 #define SENTINEL_BYTE_1 0xFF
 #define SENTINEL_BYTE_2 0xAA
@@ -242,6 +243,7 @@
 static int msm_rtb_probe(struct platform_device *pdev)
 {
 	struct msm_rtb_platform_data *d = pdev->dev.platform_data;
+	struct md_region md_entry;
 #if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
 	unsigned int cpu;
 #endif
@@ -293,6 +295,12 @@
 
 	memset(msm_rtb.rtb, 0, msm_rtb.size);
 
+	strlcpy(md_entry.name, "KRTB_BUF", sizeof(md_entry.name));
+	md_entry.virt_addr = (uintptr_t)msm_rtb.rtb;
+	md_entry.phys_addr = msm_rtb.phys;
+	md_entry.size = msm_rtb.size;
+	if (msm_minidump_add_region(&md_entry))
+		pr_info("Failed to add RTB in Minidump\n");
 
 #if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)
 	for_each_possible_cpu(cpu) {
diff --git a/mm/page_owner.c b/mm/page_owner.c
index fe850b9..c4381d93 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -554,11 +554,17 @@
 				continue;
 
 			/*
-			 * We are safe to check buddy flag and order, because
-			 * this is init stage and only single thread runs.
+			 * To avoid having to grab zone->lock, be a little
+			 * careful when reading buddy page order. The only
+			 * danger is that we skip too much and potentially miss
+			 * some early allocated pages, which is better than
+			 * heavy lock contention.
 			 */
 			if (PageBuddy(page)) {
-				pfn += (1UL << page_order(page)) - 1;
+				unsigned long order = page_order_unsafe(page);
+
+				if (order > 0 && order < MAX_ORDER)
+					pfn += (1UL << order) - 1;
 				continue;
 			}
 
@@ -577,6 +583,7 @@
 			set_page_owner(page, 0, 0);
 			count++;
 		}
+		cond_resched();
 	}
 
 	pr_info("Node %d, zone %8s: page owner found early allocated %lu pages\n",
@@ -587,15 +594,12 @@
 {
 	struct zone *zone;
 	struct zone *node_zones = pgdat->node_zones;
-	unsigned long flags;
 
 	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
 		if (!populated_zone(zone))
 			continue;
 
-		spin_lock_irqsave(&zone->lock, flags);
 		init_pages_in_zone(pgdat, zone);
-		spin_unlock_irqrestore(&zone->lock, flags);
 	}
 }
 
diff --git a/net/ipv4/netfilter/ipt_NATTYPE.c b/net/ipv4/netfilter/ipt_NATTYPE.c
index b8d93e9..bed569f8 100644
--- a/net/ipv4/netfilter/ipt_NATTYPE.c
+++ b/net/ipv4/netfilter/ipt_NATTYPE.c
@@ -24,6 +24,7 @@
  * Ubicom32 implementation derived from
  * Cameo's implementation(with many thanks):
  */
+
 #include <linux/types.h>
 #include <linux/ip.h>
 #include <linux/udp.h>
@@ -36,21 +37,17 @@
 #include <linux/tcp.h>
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_core.h>
-#include <net/netfilter/nf_nat_rule.h>
+#include <net/netfilter/nf_nat.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_ipv4/ipt_NATTYPE.h>
 #include <linux/atomic.h>
 
-#if !defined(NATTYPE_DEBUG)
-#define DEBUGP(type, args...)
-#else
 static const char * const types[] = {"TYPE_PORT_ADDRESS_RESTRICTED",
 			"TYPE_ENDPOINT_INDEPENDENT",
 			"TYPE_ADDRESS_RESTRICTED"};
 static const char * const modes[] = {"MODE_DNAT", "MODE_FORWARD_IN",
 			"MODE_FORWARD_OUT"};
 #define DEBUGP(args...) pr_debug(args)
-#endif
 
 /* netfilter NATTYPE TODO:
  * Add magic value checks to data structure.
@@ -58,14 +55,17 @@
 struct ipt_nattype {
 	struct list_head list;
 	struct timer_list timeout;
-	unsigned char is_valid;
+	unsigned long timeout_value;
+	unsigned int nattype_cookie;
 	unsigned short proto;		/* Protocol: TCP or UDP */
-	struct nf_nat_ipv4_range range;	/* LAN side src info*/
+	struct nf_nat_range range;	/* LAN side source information */
 	unsigned short nat_port;	/* Routed NAT port */
 	unsigned int dest_addr;	/* Original egress packets dst addr */
 	unsigned short dest_port;/* Original egress packets destination port */
 };
 
+#define NATTYPE_COOKIE 0x11abcdef
+
 /* TODO: It might be better to use a hash table for performance in
  * heavy traffic.
  */
@@ -78,11 +78,13 @@
 static void nattype_nte_debug_print(const struct ipt_nattype *nte,
 				    const char *s)
 {
-	DEBUGP("%p: %s - proto[%d], src[%pI4:%d], nat[<x>:%d], dest[%pI4:%d]\n",
+	DEBUGP("%p:%s-proto[%d],src[%pI4:%d],nat[%d],dest[%pI4:%d]\n",
 	       nte, s, nte->proto,
-		&nte->range.min_ip, ntohs(nte->range.min.all),
+	       &nte->range.min_addr.ip, ntohs(nte->range.min_proto.all),
 		ntohs(nte->nat_port),
 		&nte->dest_addr, ntohs(nte->dest_port));
+	DEBUGP("Timeout[%lx], Expires[%lx]\n", nte->timeout_value,
+	       nte->timeout.expires);
 }
 
 /* netfilter NATTYPE nattype_free()
@@ -90,28 +92,28 @@
  */
 static void nattype_free(struct ipt_nattype *nte)
 {
-	nattype_nte_debug_print(nte, "free");
 	kfree(nte);
 }
 
 /* netfilter NATTYPE nattype_refresh_timer()
  * Refresh the timer for this object.
  */
-bool nattype_refresh_timer_impl(unsigned long nat_type)
+bool nattype_refresh_timer(unsigned long nat_type, unsigned long timeout_value)
 {
 	struct ipt_nattype *nte = (struct ipt_nattype *)nat_type;
 
 	if (!nte)
 		return false;
 	spin_lock_bh(&nattype_lock);
-	if (!nte->is_valid) {
+	if (nte->nattype_cookie != NATTYPE_COOKIE) {
 		spin_unlock_bh(&nattype_lock);
 		return false;
 	}
 	if (del_timer(&nte->timeout)) {
-		nte->timeout.expires = jiffies + NATTYPE_TIMEOUT * HZ;
+		nte->timeout.expires = timeout_value;
 		add_timer(&nte->timeout);
 		spin_unlock_bh(&nattype_lock);
+		nattype_nte_debug_print(nte, "refresh");
 		return true;
 	}
 	spin_unlock_bh(&nattype_lock);
@@ -213,7 +215,8 @@
 /* netfilter NATTYPE nattype_compare
  * Compare two entries, return true if relevant fields are the same.
  */
-static bool nattype_compare(struct ipt_nattype *n1, struct ipt_nattype *n2)
+static bool nattype_compare(struct ipt_nattype *n1, struct ipt_nattype *n2,
+			    const struct ipt_nattype_info *info)
 {
 	/* netfilter NATTYPE Protocol
 	 * compare.
@@ -228,16 +231,16 @@
 	  * Since we always keep min/max values the same,
 	  * just compare the min values.
 	  */
-	if (n1->range.min_ip != n2->range.min_ip) {
-		DEBUGP("nattype_compare: r.min_ip mismatch: %pI4:%pI4\n",
-		       &n1->range.min_ip, &n2->range.min_ip);
+	if (n1->range.min_addr.ip != n2->range.min_addr.ip) {
+		DEBUGP("nattype_compare: r.min_addr.ip mismatch: %pI4:%pI4\n",
+		       &n1->range.min_addr.ip, &n2->range.min_addr.ip);
 		return false;
 	}
 
-	if (n1->range.min.all != n2->range.min.all) {
+	if (n1->range.min_proto.all != n2->range.min_proto.all) {
 		DEBUGP("nattype_compare: r.min mismatch: %d:%d\n",
-		       ntohs(n1->range.min.all),
-		       ntohs(n2->range.min.all));
+				ntohs(n1->range.min_proto.all),
+				ntohs(n2->range.min_proto.all));
 		return false;
 	}
 
@@ -250,20 +253,16 @@
 		return false;
 	}
 
-	/* netfilter NATTYPE
-	 * Destination compare
+	/* netfilter NATTYPE Destination compare
+	 * Destination Comapre for Address Restricted Cone NAT.
 	 */
-	if (n1->dest_addr != n2->dest_addr) {
+	if ((info->type == TYPE_ADDRESS_RESTRICTED) &&
+	    (n1->dest_addr != n2->dest_addr)) {
 		DEBUGP("nattype_compare: dest_addr mismatch: %pI4:%pI4\n",
 		       &n1->dest_addr, &n2->dest_addr);
 		return false;
 	}
 
-	if (n1->dest_port != n2->dest_port) {
-		DEBUGP("nattype_compare: dest_port mismatch: %d:%d\n",
-		       ntohs(n1->dest_port), ntohs(n2->dest_port));
-		return false;
-	}
 	return true;
 }
 
@@ -283,7 +282,7 @@
 	list_for_each_entry(nte, &nattype_list, list) {
 		struct nf_conn *ct;
 		enum ip_conntrack_info ctinfo;
-		struct nf_nat_ipv4_range newrange;
+		struct nf_nat_range newrange;
 		unsigned int ret;
 
 		if (!nattype_packet_in_match(nte, skb, par->targinfo))
@@ -304,11 +303,21 @@
 			return XT_CONTINUE;
 		}
 
-		/* Expand the ingress conntrack
-		 * to include the reply as source
+		/* netfilter
+		 * Refresh the timer, if we fail, break
+		 * out and forward fail as though we never
+		 * found the entry.
+		 */
+		if (!nattype_refresh_timer((unsigned long)nte,
+					   jiffies + nte->timeout_value))
+			break;
+
+		/* netfilter
+		 * Expand the ingress conntrack to include the reply as source
 		 */
 		DEBUGP("Expand ingress conntrack=%p, type=%d, src[%pI4:%d]\n",
-			ct, ctinfo, &newrange.min_ip, ntohs(newrange.min.all));
+			ct, ctinfo, &newrange.min_addr.ip,
+			ntohs(newrange.min_proto.all));
 		ct->nattype_entry = (unsigned long)nte;
 		ret = nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
 		DEBUGP("Expand returned: %d\n", ret);
@@ -332,12 +341,22 @@
 	enum ip_conntrack_info ctinfo;
 	const struct ipt_nattype_info *info = par->targinfo;
 	u16 nat_port;
+	enum ip_conntrack_dir dir;
 
-	if (par->hooknum != NF_INET_FORWARD)
+
+	if (par->hooknum != NF_INET_POST_ROUTING)
 		return XT_CONTINUE;
 
-	/* Ingress packet,
-	 * refresh the timer if we find an entry.
+	/* netfilter
+	 * Egress packet, create a new rule in our list.  If conntrack does
+	 * not have an entry, skip this packet.
+	 */
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct)
+		return XT_CONTINUE;
+
+	/* netfilter
+	 * Ingress packet, refresh the timer if we find an entry.
 	 */
 	if (info->mode == MODE_FORWARD_IN) {
 		spin_lock_bh(&nattype_lock);
@@ -355,7 +374,8 @@
 			 * out and forward fail as though we never
 			 * found the entry.
 			 */
-			if (!nattype_refresh_timer((unsigned long)nte))
+			if (!nattype_refresh_timer((unsigned long)nte,
+						   ct->timeout.expires))
 				break;
 
 			/* netfilter NATTYPE
@@ -372,15 +392,9 @@
 		return XT_CONTINUE;
 	}
 
-	/* netfilter NATTYPE
-	 * Egress packet, create a new rule in our list.  If conntrack does
-	 * not have an entry, skip this packet.
-	 */
-	ct = nf_ct_get(skb, &ctinfo);
-	if (!ct || (ctinfo == IP_CT_NEW && ctinfo == IP_CT_RELATED))
-		return XT_CONTINUE;
+	dir = CTINFO2DIR(ctinfo);
 
-	nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all;
+	nat_port = ct->tuplehash[!dir].tuple.dst.u.all;
 
 	/* netfilter NATTYPE
 	 * Allocate a new entry
@@ -396,20 +410,22 @@
 	nte->proto = iph->protocol;
 	nte->nat_port = nat_port;
 	nte->dest_addr = iph->daddr;
-	nte->range.min_ip = iph->saddr;
-	nte->range.max_ip = nte->range.min_ip;
+	nte->range.min_addr.ip = iph->saddr;
+	nte->range.max_addr.ip = nte->range.min_addr.ip;
 
 	/* netfilter NATTYPE
 	 * TOOD: Would it be better to get this information from the
 	 * conntrack instead of the headers.
 	 */
 	if (iph->protocol == IPPROTO_TCP) {
-		nte->range.min.tcp.port = ((struct tcphdr *)protoh)->source;
-		nte->range.max.tcp.port = nte->range.min.tcp.port;
+		nte->range.min_proto.tcp.port =
+					((struct tcphdr *)protoh)->source;
+		nte->range.max_proto.tcp.port = nte->range.min_proto.tcp.port;
 		nte->dest_port = ((struct tcphdr *)protoh)->dest;
 	} else if (iph->protocol == IPPROTO_UDP) {
-		nte->range.min.udp.port = ((struct udphdr *)protoh)->source;
-		nte->range.max.udp.port = nte->range.min.udp.port;
+		nte->range.min_proto.udp.port =
+					((struct udphdr *)protoh)->source;
+		nte->range.max_proto.udp.port = nte->range.min_proto.udp.port;
 		nte->dest_port = ((struct udphdr *)protoh)->dest;
 	}
 	nte->range.flags = (NF_NAT_RANGE_MAP_IPS |
@@ -430,7 +446,7 @@
 	 */
 	spin_lock_bh(&nattype_lock);
 	list_for_each_entry(nte2, &nattype_list, list) {
-		if (!nattype_compare(nte, nte2))
+		if (!nattype_compare(nte, nte2, info))
 			continue;
 		spin_unlock_bh(&nattype_lock);
 		/* netfilter NATTYPE
@@ -438,7 +454,9 @@
 		 * entry as this one is timed out and will be removed
 		 * from the list shortly.
 		 */
-		if (!nattype_refresh_timer((unsigned long)nte2))
+		if (!nattype_refresh_timer(
+			(unsigned long)nte2,
+			jiffies + nte2->timeout_value))
 			break;
 
 		/* netfilter NATTYPE
@@ -455,11 +473,12 @@
 	/* netfilter NATTYPE
 	 * Add the new entry to the list.
 	 */
-	nte->timeout.expires = jiffies + (NATTYPE_TIMEOUT  * HZ);
+	nte->timeout_value = ct->timeout.expires;
+	nte->timeout.expires = ct->timeout.expires + jiffies;
 	add_timer(&nte->timeout);
 	list_add(&nte->list, &nattype_list);
 	ct->nattype_entry = (unsigned long)nte;
-	nte->is_valid = 1;
+	nte->nattype_cookie = NATTYPE_COOKIE;
 	spin_unlock_bh(&nattype_lock);
 	nattype_nte_debug_print(nte, "ADD");
 	return XT_CONTINUE;
@@ -549,7 +568,7 @@
 	       types[info->type], modes[info->mode]);
 
 	if (par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
-		(1 << NF_INET_FORWARD))) {
+		(1 << NF_INET_POST_ROUTING))) {
 		DEBUGP("nattype_check: bad hooks %x.\n", par->hook_mask);
 		return -EINVAL;
 	}
@@ -590,7 +609,7 @@
 	.checkentry	= nattype_check,
 	.targetsize	= sizeof(struct ipt_nattype_info),
 	.hooks		= ((1 << NF_INET_PRE_ROUTING) |
-				(1 << NF_INET_FORWARD)),
+				(1 << NF_INET_POST_ROUTING)),
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
index ea91058..1eda519 100644
--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
@@ -68,7 +68,13 @@
 	newrange.max_proto   = range->max_proto;
 
 	/* Hand modified range to generic setup. */
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+	nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
+	return XT_CONTINUE;
+#else
 	return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
+#endif
+
 }
 EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4);
 
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index c348c40..5b9cb3c 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -72,7 +72,10 @@
 struct hlist_nulls_head *nf_conntrack_hash __read_mostly;
 EXPORT_SYMBOL_GPL(nf_conntrack_hash);
 
-bool (*nattype_refresh_timer)(unsigned long nattype) __rcu __read_mostly;
+bool (*nattype_refresh_timer)
+	(unsigned long nattype,
+	unsigned long timeout_value)
+	__rcu __read_mostly;
 EXPORT_SYMBOL(nattype_refresh_timer);
 
 struct conntrack_gc_work {
@@ -188,6 +191,7 @@
 EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);
 
 unsigned int nf_conntrack_max __read_mostly;
+
 seqcount_t nf_conntrack_generation __read_mostly;
 
 unsigned int nf_conntrack_pkt_threshold __read_mostly;
@@ -196,7 +200,8 @@
 DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked);
 EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked);
 
-static unsigned int nf_conntrack_hash_rnd __read_mostly;
+unsigned int nf_conntrack_hash_rnd __read_mostly;
+EXPORT_SYMBOL(nf_conntrack_hash_rnd);
 
 static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple,
 			      const struct net *net)
@@ -399,6 +404,9 @@
 	struct nf_conn *ct = (struct nf_conn *)nfct;
 	struct nf_conntrack_l4proto *l4proto;
 	void (*delete_entry)(struct nf_conn *ct);
+	struct sip_list *sip_node = NULL;
+	struct list_head *sip_node_list;
+	struct list_head *sip_node_save_list;
 
 	pr_debug("destroy_conntrack(%pK)\n", ct);
 	NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
@@ -426,6 +434,14 @@
 	rcu_read_unlock();
 
 	local_bh_disable();
+
+	pr_debug("freeing item in the SIP list\n");
+	list_for_each_safe(sip_node_list, sip_node_save_list,
+			   &ct->sip_segment_list) {
+		sip_node = list_entry(sip_node_list, struct sip_list, list);
+		list_del(&sip_node->list);
+		kfree(sip_node);
+	}
 	/* Expectations will have been removed in clean_from_lists,
 	 * except TFTP can create an expectation on the first packet,
 	 * before connection is in the list, so we need to clean here,
@@ -1097,6 +1113,9 @@
 
 	nf_ct_zone_add(ct, zone);
 
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+	ct->nattype_entry = 0;
+#endif
 	/* Because we use RCU lookups, we set ct_general.use to zero before
 	 * this is inserted in any list.
 	 */
@@ -1200,6 +1219,7 @@
 			     GFP_ATOMIC);
 
 	local_bh_disable();
+	INIT_LIST_HEAD(&ct->sip_segment_list);
 	if (net->ct.expect_count) {
 		spin_lock(&nf_conntrack_expect_lock);
 		exp = nf_ct_find_expectation(net, zone, tuple);
@@ -1468,7 +1488,9 @@
 	struct nf_conn_acct *acct;
 	u64 pkts;
 #if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
-	bool (*nattype_ref_timer)(unsigned long nattype);
+	bool (*nattype_ref_timer)
+		(unsigned long nattype,
+		unsigned long timeout_value);
 #endif
 
 	NF_CT_ASSERT(skb);
@@ -1486,7 +1508,7 @@
 #if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
 	nattype_ref_timer = rcu_dereference(nattype_refresh_timer);
 	if (nattype_ref_timer)
-		nattype_ref_timer(ct->nattype_entry);
+		nattype_ref_timer(ct->nattype_entry, ct->timeout.expires);
 #endif
 
 acct:
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 6bd58eea..1ce25f5 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1540,12 +1540,23 @@
 				    const struct nlattr * const cda[])
 {
 	u_int32_t timeout = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+	bool (*nattype_ref_timer)
+		(unsigned long nattype,
+		unsigned long timeout_value);
+#endif
 
 	ct->timeout = nfct_time_stamp + timeout * HZ;
 
 	if (test_bit(IPS_DYING_BIT, &ct->status))
 		return -ETIME;
 
+/* Refresh the NAT type entry. */
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+	nattype_ref_timer = rcu_dereference(nattype_refresh_timer);
+	if (nattype_ref_timer)
+		nattype_ref_timer(ct->nattype_entry, ct->timeout.expires);
+#endif
 	return 0;
 }
 
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index f132ef9..6d6731f 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1,5 +1,6 @@
 /* SIP extension for IP connection tracking.
  *
+ * Copyright (c) 2015,2017, The Linux Foundation. All rights reserved.
  * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
  * based on RR's ip_conntrack_ftp.c and other modules.
  * (C) 2007 United Security Providers
@@ -20,13 +21,18 @@
 #include <linux/udp.h>
 #include <linux/tcp.h>
 #include <linux/netfilter.h>
-
+#include <net/tcp.h>
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_expect.h>
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <linux/netfilter/nf_conntrack_sip.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_l3proto.h>
+#include <net/netfilter/nf_nat_l4proto.h>
+#include <net/netfilter/nf_queue.h>
+
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
@@ -54,6 +60,12 @@
 static struct ctl_table_header *sip_sysctl_header;
 static unsigned int nf_ct_disable_sip_alg;
 static int sip_direct_media = 1;
+static unsigned int nf_ct_enable_sip_segmentation;
+static int packet_count;
+static
+int proc_sip_segment(struct ctl_table *ctl, int write,
+		     void __user *buffer, size_t *lenp, loff_t *ppos);
+
 static struct ctl_table sip_sysctl_tbl[] = {
 	{
 		.procname     = "nf_conntrack_disable_sip_alg",
@@ -69,9 +81,289 @@
 		.mode         = 0644,
 		.proc_handler = proc_dointvec,
 	},
+	{
+		.procname     = "nf_conntrack_enable_sip_segmentation",
+		.data         = &nf_ct_enable_sip_segmentation,
+		.maxlen       = sizeof(unsigned int),
+		.mode         = 0644,
+		.proc_handler = proc_sip_segment,
+	},
 	{}
 };
 
+static unsigned int (*nf_nat_sip_hook)
+					(struct sk_buff *skb,
+					unsigned int protoff,
+					unsigned int dataoff,
+					const char **dptr,
+					unsigned int *datalen)
+					__read_mostly;
+EXPORT_SYMBOL(nf_nat_sip_hook);
+static void sip_calculate_parameters(s16 *diff, s16 *tdiff,
+				     unsigned int *dataoff, const char **dptr,
+				     unsigned int *datalen,
+				     unsigned int msglen, unsigned int origlen)
+{
+	*diff	 = msglen - origlen;
+	*tdiff	+= *diff;
+	*dataoff += msglen;
+	*dptr	+= msglen;
+	*datalen  = *datalen + *diff - msglen;
+}
+
+static void sip_update_params(enum ip_conntrack_dir dir,
+			      unsigned int *msglen, unsigned int *origlen,
+			      const char **dptr, unsigned int *datalen,
+			      bool skb_is_combined, struct nf_conn *ct)
+{
+	if (skb_is_combined) {
+		/* The msglen of first skb has the total msg length of
+		 * the two fragments. hence after combining,we update
+		 * the msglen to that of the msglen of first skb
+		 */
+		*msglen = (dir == IP_CT_DIR_ORIGINAL) ?
+		  ct->segment.msg_length[0] : ct->segment.msg_length[1];
+		*origlen = *msglen;
+		*dptr = ct->dptr_prev;
+		*datalen = *msglen;
+	}
+}
+
+/* This function is to save all the information of the first segment
+ * that will be needed for combining the two segments
+ */
+static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb,
+				  unsigned int msglen, unsigned int datalen,
+				  const char *dptr,
+				  enum ip_conntrack_info ctinfo)
+{
+	enum ip_conntrack_dir dir = IP_CT_DIR_MAX;
+	bool skip = false;
+
+	/* one set of information is saved per direction ,also only one segment
+	 * per direction is queued based on the assumption that after the first
+	 * complete message leaves the kernel, only then the next fragmented
+	 * segment will reach the kernel
+	 */
+	dir = CTINFO2DIR(ctinfo);
+	if (dir == IP_CT_DIR_ORIGINAL) {
+		/* here we check if there is already an element queued for this
+		 * direction, in that case we do not queue the next element,we
+		 * make skip 1.ideally this scenario should never be hit
+		 */
+		if (ct->sip_original_dir == 1) {
+			skip = true;
+		} else {
+			ct->segment.msg_length[0] = msglen;
+			ct->segment.data_len[0] = datalen;
+			ct->segment.skb_len[0] = skb->len;
+			ct->dptr_prev = dptr;
+			ct->sip_original_dir = 1;
+			skip = false;
+		}
+	} else {
+		if (ct->sip_reply_dir == 1) {
+			skip = true;
+		} else {
+			if (ct->sip_reply_dir == 1) {
+				skip = true;
+			} else {
+				ct->segment.msg_length[1] = msglen;
+				ct->segment.data_len[1] = datalen;
+				ct->segment.skb_len[1] = skb->len;
+				ct->dptr_prev = dptr;
+				ct->sip_reply_dir = 1;
+				skip = false;
+			}
+		}
+	}
+	return skip;
+}
+
+static struct sip_list *sip_coalesce_segments(struct nf_conn *ct,
+					      struct sk_buff **skb_ref,
+					      unsigned int dataoff,
+					      struct sk_buff **combined_skb_ref,
+					      bool *skip_sip_process,
+					      bool do_not_process,
+					      enum ip_conntrack_info ctinfo,
+					      bool *success)
+
+{
+	struct list_head *list_trav_node;
+	struct list_head *list_backup_node;
+	struct nf_conn *ct_list;
+	enum ip_conntrack_info ctinfo_list;
+	enum ip_conntrack_dir dir_list;
+	enum ip_conntrack_dir dir = IP_CT_DIR_MAX;
+	const struct tcphdr *th_old;
+	unsigned int prev_data_len;
+	unsigned int seq_no, seq_old, exp_seq_no;
+	const struct tcphdr *th_new;
+	bool fragstolen = false;
+	int delta_truesize = 0;
+	struct sip_list *sip_entry = NULL;
+
+	th_new = (struct tcphdr *)(skb_network_header(*skb_ref) +
+		ip_hdrlen(*skb_ref));
+	seq_no = ntohl(th_new->seq);
+
+	if (ct) {
+		dir = CTINFO2DIR(ctinfo);
+		/* traverse the list it would have 1 or 2 elements. 1 element
+		 * per direction at max
+		 */
+		list_for_each_safe(list_trav_node, list_backup_node,
+				   &ct->sip_segment_list){
+			sip_entry = list_entry(list_trav_node, struct sip_list,
+					       list);
+			ct_list = nf_ct_get(sip_entry->entry->skb,
+					    &ctinfo_list);
+			dir_list = CTINFO2DIR(ctinfo_list);
+			/* take an element and check if its direction matches
+			 * with the current one
+			 */
+			if (dir_list == dir) {
+				/* once we have the two elements to be combined
+				 * we do another check. match the next expected
+				 * seq no of the packet in the list with the
+				 * seq no of the current packet.this is to be
+				 * protected  against out of order fragments
+				 */
+				th_old = ((struct tcphdr *)(skb_network_header
+					(sip_entry->entry->skb) +
+					ip_hdrlen(sip_entry->entry->skb)));
+
+				prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ?
+				 ct->segment.data_len[0] :
+				 ct->segment.data_len[1];
+				seq_old = (ntohl(th_old->seq));
+				exp_seq_no = seq_old + prev_data_len;
+
+				if (exp_seq_no == seq_no) {
+					/* Found packets to be combined.Pull
+					 * header from second skb when
+					 * preparing combined skb.This shifts
+					 * the second skb start pointer to its
+					 * data that was initially at the start
+					 * of its headers.This so that the
+					 * combined skb has the tcp ip headerof
+					 * the first skb followed by the data
+					 * of first skb followed by the data
+					 * of second skb.
+					 */
+					skb_pull(*skb_ref, dataoff);
+					if (skb_try_coalesce(
+							sip_entry->entry->skb,
+							*skb_ref, &fragstolen,
+							&delta_truesize)) {
+					pr_debug(" Combining segments\n");
+					*combined_skb_ref =
+							  sip_entry->entry->skb;
+					*success = true;
+					list_del(list_trav_node);
+					} else{
+						skb_push(*skb_ref, dataoff);
+					}
+				}
+			} else if (do_not_process) {
+				*skip_sip_process = true;
+			}
+		}
+	}
+	return sip_entry;
+}
+
+static void recalc_header(struct sk_buff *skb, unsigned int skblen,
+			  unsigned int oldlen, unsigned int protoff)
+{
+	unsigned int datalen;
+	struct tcphdr *tcph;
+	const struct nf_nat_l3proto *l3proto;
+
+	/* here we recalculate ip and tcp headers */
+	if (nf_ct_l3num((struct nf_conn *)skb->nfct) == NFPROTO_IPV4) {
+		/* fix IP hdr checksum information */
+		ip_hdr(skb)->tot_len = htons(skblen);
+		ip_send_check(ip_hdr(skb));
+	} else {
+		ipv6_hdr(skb)->payload_len =
+				htons(skblen - sizeof(struct ipv6hdr));
+	}
+	datalen = skb->len - protoff;
+	tcph = (struct tcphdr *)((void *)skb->data + protoff);
+	l3proto = __nf_nat_l3proto_find(nf_ct_l3num
+					((struct nf_conn *)skb->nfct));
+	l3proto->csum_recalc(skb, IPPROTO_TCP, tcph, &tcph->check,
+			     datalen, oldlen);
+}
+
+void (*nf_nat_sip_seq_adjust_hook)
+			(struct sk_buff *skb,
+			unsigned int protoff,
+			s16 off);
+
+static unsigned int (*nf_nat_sip_expect_hook)
+					(struct sk_buff *skb,
+					unsigned int protoff,
+					unsigned int dataoff,
+					const char **dptr,
+					unsigned int *datalen,
+					struct nf_conntrack_expect *exp,
+					unsigned int matchoff,
+					unsigned int matchlen)
+					__read_mostly;
+EXPORT_SYMBOL(nf_nat_sip_expect_hook);
+
+static unsigned int (*nf_nat_sdp_addr_hook)
+					(struct sk_buff *skb,
+					unsigned int protoff,
+					unsigned int dataoff,
+					const char **dptr,
+					unsigned int *datalen,
+					unsigned int sdpoff,
+					enum sdp_header_types type,
+					enum sdp_header_types term,
+					const union nf_inet_addr *addr)
+					__read_mostly;
+EXPORT_SYMBOL(nf_nat_sdp_addr_hook);
+
+static unsigned int (*nf_nat_sdp_port_hook)
+					(struct sk_buff *skb,
+					unsigned int protoff,
+					unsigned int dataoff,
+					const char **dptr,
+					unsigned int *datalen,
+					unsigned int matchoff,
+					unsigned int matchlen,
+					u_int16_t port) __read_mostly;
+EXPORT_SYMBOL(nf_nat_sdp_port_hook);
+
+static unsigned int (*nf_nat_sdp_session_hook)
+					(struct sk_buff *skb,
+					unsigned int protoff,
+					unsigned int dataoff,
+					const char **dptr,
+					unsigned int *datalen,
+					unsigned int sdpoff,
+					const union nf_inet_addr *addr)
+					__read_mostly;
+EXPORT_SYMBOL(nf_nat_sdp_session_hook);
+
+static unsigned int (*nf_nat_sdp_media_hook)
+					(struct sk_buff *skb,
+					unsigned int protoff,
+					unsigned int dataoff,
+					const char **dptr,
+					unsigned int *datalen,
+					struct nf_conntrack_expect *rtp_exp,
+					struct nf_conntrack_expect *rtcp_exp,
+					unsigned int mediaoff,
+					unsigned int medialen,
+					union nf_inet_addr *rtp_addr)
+					__read_mostly;
+EXPORT_SYMBOL(nf_nat_sdp_media_hook);
+
 static int string_len(const struct nf_conn *ct, const char *dptr,
 		      const char *limit, int *shift)
 {
@@ -84,6 +376,43 @@
 	return len;
 }
 
+static int nf_sip_enqueue_packet(struct nf_queue_entry *entry,
+				 unsigned int queuenum)
+{
+	enum ip_conntrack_info ctinfo_list;
+	struct nf_conn *ct_temp;
+	struct sip_list *node = kzalloc(sizeof(*node),
+			GFP_ATOMIC | __GFP_NOWARN);
+	if (!node)
+		return XT_CONTINUE;
+
+	ct_temp = nf_ct_get(entry->skb, &ctinfo_list);
+	node->entry = entry;
+	list_add(&node->list, &ct_temp->sip_segment_list);
+	return 0;
+}
+
+static const struct nf_queue_handler nf_sip_qh = {
+	.outfn	= &nf_sip_enqueue_packet,
+};
+
+static
+int proc_sip_segment(struct ctl_table *ctl, int write,
+		     void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	int ret;
+
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+	if (nf_ct_enable_sip_segmentation) {
+		pr_debug("registering queue handler\n");
+		nf_register_queue_handler(&init_net, &nf_sip_qh);
+	} else {
+		pr_debug("de-registering queue handler\n");
+		nf_unregister_queue_handler(&init_net);
+	}
+	return ret;
+}
+
 static int digits_len(const struct nf_conn *ct, const char *dptr,
 		      const char *limit, int *shift)
 {
@@ -1505,13 +1834,29 @@
 			struct nf_conn *ct, enum ip_conntrack_info ctinfo)
 {
 	struct tcphdr *th, _tcph;
-	unsigned int dataoff, datalen;
+	unsigned int dataoff;
 	unsigned int matchoff, matchlen, clen;
-	unsigned int msglen, origlen;
 	const char *dptr, *end;
 	s16 diff, tdiff = 0;
 	int ret = NF_ACCEPT;
 	bool term;
+	unsigned int datalen = 0, msglen = 0, origlen = 0;
+	unsigned int dataoff_orig = 0;
+	unsigned int splitlen, oldlen, oldlen1;
+	struct sip_list *sip_entry = NULL;
+	bool skip_sip_process = false;
+	bool do_not_process = false;
+	bool skip = false;
+	bool skb_is_combined = false;
+	enum ip_conntrack_dir dir = IP_CT_DIR_MAX;
+	struct sk_buff *combined_skb = NULL;
+	bool content_len_exists = 1;
+
+	packet_count++;
+	pr_debug("packet count %d\n", packet_count);
+
+	if (nf_ct_disable_sip_alg)
+		return NF_ACCEPT;
 
 	if (ctinfo != IP_CT_ESTABLISHED &&
 	    ctinfo != IP_CT_ESTABLISHED_REPLY)
@@ -1535,11 +1880,26 @@
 	if (datalen < strlen("SIP/2.0 200"))
 		return NF_ACCEPT;
 
+	/* here we save the original datalength and data offset of the skb, this
+	 * is needed later to split combined skbs
+	 */
+	oldlen1 = skb->len - protoff;
+	dataoff_orig = dataoff;
+
+	if (!ct)
+		return NF_DROP;
 	while (1) {
 		if (ct_sip_get_header(ct, dptr, 0, datalen,
 				      SIP_HDR_CONTENT_LENGTH,
-				      &matchoff, &matchlen) <= 0)
+				      &matchoff, &matchlen) <= 0){
+			if (nf_ct_enable_sip_segmentation) {
+				do_not_process = true;
+				content_len_exists = 0;
+				goto destination;
+			} else {
 			break;
+			}
+		}
 
 		clen = simple_strtoul(dptr + matchoff, (char **)&end, 10);
 		if (dptr + matchoff == end)
@@ -1555,26 +1915,111 @@
 		}
 		if (!term)
 			break;
+
 		end += strlen("\r\n\r\n") + clen;
+destination:
 
-		msglen = origlen = end - dptr;
-		if (msglen > datalen)
+		if (content_len_exists == 0) {
+			origlen = datalen;
+			msglen = origlen;
+		} else {
+			origlen = end - dptr;
+			msglen = origlen;
+		}
+		pr_debug("mslgen %d datalen %d\n", msglen, datalen);
+		dir = CTINFO2DIR(ctinfo);
+		combined_skb = skb;
+		if (nf_ct_enable_sip_segmentation) {
+			/* Segmented Packet */
+			if (msglen > datalen) {
+				skip = sip_save_segment_info(ct, skb, msglen,
+							     datalen, dptr,
+							     ctinfo);
+				if (!skip)
+					return NF_QUEUE;
+			}
+			/* Traverse list to find prev segment */
+			/*Traverse the list if list non empty */
+			if (((&ct->sip_segment_list)->next) !=
+				(&ct->sip_segment_list)) {
+				/* Combine segments if they are fragments of
+				 *  the same message.
+				 */
+				sip_entry = sip_coalesce_segments(ct, &skb,
+								  dataoff,
+								  &combined_skb,
+							&skip_sip_process,
+								do_not_process,
+								  ctinfo,
+							&skb_is_combined);
+				sip_update_params(dir, &msglen, &origlen, &dptr,
+						  &datalen,
+						  skb_is_combined, ct);
+
+				if (skip_sip_process)
+					goto here;
+				} else if (do_not_process) {
+					goto here;
+				}
+		} else if (msglen > datalen) {
 			return NF_ACCEPT;
-
-		ret = process_sip_msg(skb, ct, protoff, dataoff,
+		}
+		/* process the combined skb having the complete SIP message */
+		ret = process_sip_msg(combined_skb, ct, protoff, dataoff,
 				      &dptr, &msglen);
+
 		/* process_sip_* functions report why this packet is dropped */
 		if (ret != NF_ACCEPT)
 			break;
-		diff     = msglen - origlen;
-		tdiff   += diff;
-
-		dataoff += msglen;
-		dptr    += msglen;
-		datalen  = datalen + diff - msglen;
+		sip_calculate_parameters(&diff, &tdiff, &dataoff, &dptr,
+					 &datalen, msglen, origlen);
+		if (nf_ct_enable_sip_segmentation && skb_is_combined)
+			break;
+	}
+	if (skb_is_combined) {
+		/* once combined skb is processed, split the skbs again The
+		 * length to split at is the same as length of first skb. Any
+		 * changes in the combined skb length because of SIP processing
+		 * will reflect in the second fragment
+		 */
+		splitlen = (dir == IP_CT_DIR_ORIGINAL) ?
+				ct->segment.skb_len[0] : ct->segment.skb_len[1];
+		oldlen = combined_skb->len - protoff;
+		skb_split(combined_skb, skb, splitlen);
+		/* Headers need to be recalculated since during SIP processing
+		 * headers are calculated based on the change in length of the
+		 * combined message
+		 */
+		recalc_header(combined_skb, splitlen, oldlen, protoff);
+		/* Reinject the first skb now that the processing is complete */
+		if (sip_entry) {
+			nf_reinject(sip_entry->entry, NF_ACCEPT);
+			kfree(sip_entry);
+		}
+		skb->len = (oldlen1 + protoff) + tdiff - dataoff_orig;
+		/* After splitting, push the headers back to the first skb which
+		 * were removed before combining the skbs.This moves the skb
+		 * begin pointer back to the beginning of its headers
+		 */
+		skb_push(skb, dataoff_orig);
+		/* Since the length of this second segment willbe affected
+		 * because of SIP processing,we need to recalculate its header
+		 * as well.
+		 */
+		recalc_header(skb, skb->len, oldlen1, protoff);
+		/* Now that the processing is done and the first skb reinjected.
+		 * We allow addition of fragmented skbs to the list for this
+		 * direction
+		 */
+		if (dir == IP_CT_DIR_ORIGINAL)
+			ct->sip_original_dir = 0;
+		else
+			ct->sip_reply_dir = 0;
 	}
 
-	if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
+here:
+
+	if (ret == NF_ACCEPT && ct &&  ct->status & IPS_NAT_MASK) {
 		const struct nf_nat_sip_hooks *hooks;
 
 		hooks = rcu_dereference(nf_nat_sip_hooks);
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index f323faf..ff9887f 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -1477,9 +1477,9 @@
 
 country VN: DFS-FCC
 	(2402 - 2482 @ 40), (20)
-	(5170 - 5250 @ 80), (24), AUTO-BW
-	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
-	(5490 - 5730 @ 160), (24), DFS
+	(5170 - 5250 @ 80), (24)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
 	(5735 - 5835 @ 80), (30)
 	# 60 gHz band channels 1-4
 	(57240 - 65880 @ 2160), (40)
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index 0aeabfe..e2cebf15 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -68,6 +68,8 @@
 	unsigned long xfer_buf_va;
 	size_t xfer_buf_size;
 	phys_addr_t xfer_buf_pa;
+	unsigned int data_ep_pipe;
+	unsigned int sync_ep_pipe;
 	u8 *xfer_buf;
 	u8 intf_num;
 	u8 pcm_card_num;
@@ -415,6 +417,7 @@
 	int protocol, card_num, pcm_dev_num;
 	void *hdr_ptr;
 	u8 *xfer_buf;
+	unsigned int data_ep_pipe = 0, sync_ep_pipe = 0;
 	u32 len, mult, remainder, xfer_buf_len, sg_len, i, total_len = 0;
 	unsigned long va, va_sg, tr_data_va = 0, tr_sync_va = 0;
 	phys_addr_t xhci_pa, xfer_buf_pa, tr_data_pa = 0, tr_sync_pa = 0;
@@ -531,6 +534,7 @@
 			subs->data_endpoint->ep_num);
 		goto err;
 	}
+	data_ep_pipe = subs->data_endpoint->pipe;
 	memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
 	resp->std_as_data_ep_desc_valid = 1;
 
@@ -548,6 +552,7 @@
 			pr_debug("%s: implicit fb on data ep\n", __func__);
 			goto skip_sync_ep;
 		}
+		sync_ep_pipe = subs->sync_endpoint->pipe;
 		memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
 		resp->std_as_sync_ep_desc_valid = 1;
 
@@ -704,6 +709,8 @@
 	uadev[card_num].info[info_idx].xfer_buf_va = va;
 	uadev[card_num].info[info_idx].xfer_buf_pa = xfer_buf_pa;
 	uadev[card_num].info[info_idx].xfer_buf_size = len;
+	uadev[card_num].info[info_idx].data_ep_pipe = data_ep_pipe;
+	uadev[card_num].info[info_idx].sync_ep_pipe = sync_ep_pipe;
 	uadev[card_num].info[info_idx].xfer_buf = xfer_buf;
 	uadev[card_num].info[info_idx].pcm_card_num = card_num;
 	uadev[card_num].info[info_idx].pcm_dev_num = pcm_dev_num;
@@ -732,6 +739,26 @@
 static void uaudio_dev_intf_cleanup(struct usb_device *udev,
 	struct intf_info *info)
 {
+
+	struct usb_host_endpoint *ep;
+
+	if (info->data_ep_pipe) {
+		ep = usb_pipe_endpoint(udev, info->data_ep_pipe);
+		if (!ep)
+			pr_debug("%s: no data ep\n", __func__);
+		else
+			usb_stop_endpoint(udev, ep);
+		info->data_ep_pipe = 0;
+	}
+	if (info->sync_ep_pipe) {
+		ep = usb_pipe_endpoint(udev, info->sync_ep_pipe);
+		if (!ep)
+			pr_debug("%s: no sync ep\n", __func__);
+		else
+			usb_stop_endpoint(udev, ep);
+		info->sync_ep_pipe = 0;
+	}
+
 	uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
 		info->data_xfer_ring_size);
 	info->data_xfer_ring_va = 0;