Merge "ARM: dts: msm: disable wcss and ssc cti on sdm670"
diff --git a/Documentation/devicetree/bindings/arm/msm/smdpkt.txt b/Documentation/devicetree/bindings/arm/msm/smdpkt.txt
new file mode 100644
index 0000000..be9084b
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/smdpkt.txt
@@ -0,0 +1,43 @@
+Qualcomm Technologies, Inc Shared Memory Packet Driver (smdpkt)
+
+[Root level node]
+Required properties:
+-compatible : should be "qcom,smdpkt"
+
+[Second level nodes]
+qcom,smdpkt-port-names
+Required properties:
+-qcom,smdpkt-remote : the remote subsystem name
+-qcom,smdpkt-port-name : the smd channel name
+-qcom,smdpkt-dev-name : the smdpkt device name
+
+Example:
+
+	qcom,smdpkt {
+		compatible = "qcom,smdpkt";
+
+		qcom,smdpkt-data5-cntl {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "DATA5_CNTL";
+			qcom,smdpkt-dev-name = "smdcntl0";
+		};
+
+		qcom,smdpkt-data6-cntl {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "DATA6_CNTL";
+			qcom,smdpkt-dev-name = "smdcntl1";
+		};
+
+		qcom,smdpkt-cxm-qmi-port-8064 {
+			qcom,smdpkt-remote = "wcnss";
+			qcom,smdpkt-port-name = "CXM_QMI_PORT_8064";
+			qcom,smdpkt-dev-name = "smd_cxm_qmi";
+		};
+
+		qcom,smdpkt-loopback {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "LOOPBACK";
+			qcom,smdpkt-dev-name = "smd_pkt_loopback";
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/arm/msm/smdtty.txt b/Documentation/devicetree/bindings/arm/msm/smdtty.txt
new file mode 100644
index 0000000..a445c60
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/smdtty.txt
@@ -0,0 +1,40 @@
+Qualcomm Technologies, Inc Shared Memory TTY Driver (smdtty)
+
+[Root level node]
+Required properties:
+-compatible : should be "qcom,smdtty"
+
+[Second level nodes]
+qcom,smdtty-port-names
+Required properties:
+-qcom,smdtty-remote: the remote subsystem name
+-qcom,smdtty-port-name : the smd channel name
+
+Optional properties:
+-qcom,smdtty-dev-name : the smdtty device name
+
+Required alias:
+- The index into TTY subsystem is specified via an alias with the following format
+         'smd{n}' where n is the tty device index.
+
+Example:
+	aliases {
+		smd1 = &smdtty_apps_fm;
+		smd36 = &smdtty_loopback;
+	};
+
+	qcom,smdtty {
+		compatible = "qcom,smdtty";
+
+		smdtty_apps_fm: qcom,smdtty-apps-fm {
+			qcom,smdtty-remote = "wcnss";
+			qcom,smdtty-port-name = "APPS_FM";
+		};
+
+		smdtty_loopback: smdtty-loopback {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "LOOPBACK";
+			qcom,smdtty-dev-name = "LOOPBACK_TTY";
+		};
+	};
+
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/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
index 795ee95..a5e607d 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -88,11 +88,11 @@
 					50, 60, 70 and 80.
 - interrupts:				Specify the interrupts as per the interrupt
 					encoding.
-					Currently "lab-vreg-ok" is required for
-					LCD mode in pmi8998. For AMOLED mode,
-					"lab-vreg-ok" is required only when SWIRE
-					control is enabled and skipping 2nd SWIRE
-					pulse is required in pmi8952/8996.
+					Currently "lab-vreg-ok" is required and "lab-sc_err"
+					is optional for LCD mode in pmi8998.
+					For AMOLED mode, "lab-vreg-ok" is required
+					only when SWIRE control is enabled and skipping
+					2nd SWIRE pulse is required in pmi8952/8996.
 - interrupt-names:			Interrupt names to match up 1-to-1 with
 					the interrupts specified in 'interrupts'
 					property.
@@ -153,6 +153,10 @@
 					any value in the allowed limit.
 - qcom,notify-lab-vreg-ok-sts:		A boolean property which upon set will
 					poll and notify the lab_vreg_ok status.
+- qcom,qpnp-lab-sc-wait-time-ms:	This property is used to specify the time
+					(in ms) to poll for the short circuit
+					detection. If not specified the default time
+					is 5 sec.
 
 Following properties are available only for PM660A:
 
@@ -209,6 +213,14 @@
 
 IBB subnode optional properties:
 
+- interrupts:				Specify the interrupts as per the interrupt
+					encoding.
+					Currently "ibb-sc-err" could be used for LCD mode
+					in pmi8998 to detect the short circuit fault.
+- interrupt-names:			Interrupt names to match up 1-to-1 with
+					the interrupts specified in 'interrupts'
+					property.
+
 - qcom,qpnp-ibb-discharge-resistor:	The discharge resistor in Kilo Ohms which
 					controls the soft start time. Supported values
 					are 300, 64, 32 and 16.
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
index 38f599b..55fde0d 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
@@ -14,6 +14,11 @@
 	Value type: <string>
 	Definition: should be "qcom,qpnp-oledb-regulator".
 
+- qcom,pmic-revid
+	Usage:      required
+	Value type: <phandle>
+	Definition: Used to identify the PMIC subtype.
+
 - reg
 	Usage:      required
 	Value type: <prop-encoded-array>
@@ -57,13 +62,6 @@
 		     rail.  This property is applicable only if qcom,ext-pin-ctl
 		     property is specified and it is specific to PM660A.
 
-- qcom,force-pd-control
-	Usage:      optional
-	Value type: <bool>
-	Definition:  Used to enable the pull down control forcibly via SPMI by
-		     disabling the pull down configuration done by hardware
-		     automatically through SWIRE pulses.
-
 - qcom,pbs-client
 	Usage:      optional
 	Value type: <phandle>
@@ -224,6 +222,7 @@
 	compatible = "qcom,qpnp-oledb-regulator";
 	#address-cells = <1>;
 	#size-cells = <1>;
+	qcom,pmic-revid = <&pm660l_revid>;
 	reg = <0xe000 0x100>;
 
 	label = "oledb";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index b0be698..4fb23c9 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 {
@@ -457,9 +457,9 @@
 				<CONTROL_TCS   1>;
 	};
 
-	cmd_db: qcom,cmd-db@ca0000c {
+	cmd_db: qcom,cmd-db@c37000c {
 		compatible = "qcom,cmd-db";
-		reg = <0xca0000c 8>;
+		reg = <0xc37000c 8>;
 	};
 
 	system_pm {
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index 28a0c38..d922449 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -232,6 +232,7 @@
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPMH=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_DYNAMIC_MINORS=y
@@ -305,10 +306,10 @@
 CONFIG_SPS_SUPPORT_NDP_BAM=y
 CONFIG_QPNP_REVID=y
 CONFIG_USB_BAM=y
+CONFIG_MSM_CLK_RPMH=y
 CONFIG_MDM_GCC_SDXPOORWILLS=y
 CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
-CONFIG_MAILBOX=y
 CONFIG_MSM_QMP=y
 CONFIG_QCOM_SCM=y
 CONFIG_MSM_BOOT_STATS=y
@@ -317,6 +318,7 @@
 CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
 CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_TRACER_PKT=y
+CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
@@ -324,6 +326,8 @@
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_QCOM_COMMAND_DB=y
+CONFIG_MSM_PM=y
 CONFIG_IIO=y
 CONFIG_PWM=y
 CONFIG_PWM_QPNP=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 6c3ebc7..968284d 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -228,6 +228,7 @@
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_RPMH=y
 CONFIG_REGULATOR_STUB=y
 CONFIG_FB=y
 CONFIG_SOUND=y
@@ -301,10 +302,10 @@
 CONFIG_SPS=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
 CONFIG_QPNP_REVID=y
+CONFIG_MSM_CLK_RPMH=y
 CONFIG_MDM_GCC_SDXPOORWILLS=y
 CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
-CONFIG_MAILBOX=y
 CONFIG_MSM_QMP=y
 CONFIG_QCOM_SCM=y
 CONFIG_MSM_BOOT_STATS=y
@@ -313,6 +314,7 @@
 CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
 CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_TRACER_PKT=y
+CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
@@ -320,6 +322,8 @@
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_QCOM_COMMAND_DB=y
+CONFIG_MSM_PM=y
 CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
 CONFIG_IIO=y
 CONFIG_PWM=y
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 87d5f34..98019cd 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -103,6 +103,17 @@
 
 	aliases {
 		/* smdtty devices */
+		smd1 = &smdtty_apps_fm;
+		smd2 = &smdtty_apps_riva_bt_acl;
+		smd3 = &smdtty_apps_riva_bt_cmd;
+		smd4 = &smdtty_mbalbridge;
+		smd5 = &smdtty_apps_riva_ant_cmd;
+		smd6 = &smdtty_apps_riva_ant_data;
+		smd7 = &smdtty_data1;
+		smd8 = &smdtty_data4;
+		smd11 = &smdtty_data11;
+		smd21 = &smdtty_data21;
+		smd36 = &smdtty_loopback;
 		sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
 		sdhc2 = &sdhc_2; /* SDC2 for SD card */
 	};
@@ -454,6 +465,100 @@
 		};
 	};
 
+	qcom,smdtty {
+		compatible = "qcom,smdtty";
+
+		smdtty_apps_fm: qcom,smdtty-apps-fm {
+			qcom,smdtty-remote = "wcnss";
+			qcom,smdtty-port-name = "APPS_FM";
+		};
+
+		smdtty_apps_riva_bt_acl: smdtty-apps-riva-bt-acl {
+			qcom,smdtty-remote = "wcnss";
+			qcom,smdtty-port-name = "APPS_RIVA_BT_ACL";
+		};
+
+		smdtty_apps_riva_bt_cmd: qcom,smdtty-apps-riva-bt-cmd {
+			qcom,smdtty-remote = "wcnss";
+			qcom,smdtty-port-name = "APPS_RIVA_BT_CMD";
+		};
+
+		smdtty_mbalbridge: qcom,smdtty-mbalbridge {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "MBALBRIDGE";
+		};
+
+		smdtty_apps_riva_ant_cmd: smdtty-apps-riva-ant-cmd {
+			qcom,smdtty-remote = "wcnss";
+			qcom,smdtty-port-name = "APPS_RIVA_ANT_CMD";
+		};
+
+		smdtty_apps_riva_ant_data: smdtty-apps-riva-ant-data {
+			qcom,smdtty-remote = "wcnss";
+			qcom,smdtty-port-name = "APPS_RIVA_ANT_DATA";
+		};
+
+		smdtty_data1: qcom,smdtty-data1 {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "DATA1";
+		};
+
+		smdtty_data4: qcom,smdtty-data4 {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "DATA4";
+		};
+
+		smdtty_data11: qcom,smdtty-data11 {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "DATA11";
+		};
+
+		smdtty_data21: qcom,smdtty-data21 {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "DATA21";
+		};
+
+		smdtty_loopback: smdtty-loopback {
+			qcom,smdtty-remote = "modem";
+			qcom,smdtty-port-name = "LOOPBACK";
+			qcom,smdtty-dev-name = "LOOPBACK_TTY";
+		};
+	};
+
+	qcom,smdpkt {
+		compatible = "qcom,smdpkt";
+
+		qcom,smdpkt-data5-cntl {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "DATA5_CNTL";
+			qcom,smdpkt-dev-name = "smdcntl0";
+		};
+
+		qcom,smdpkt-data22 {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "DATA22";
+			qcom,smdpkt-dev-name = "smd22";
+		};
+
+		qcom,smdpkt-data40-cntl {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "DATA40_CNTL";
+			qcom,smdpkt-dev-name = "smdcntl8";
+		};
+
+		qcom,smdpkt-apr-apps2 {
+			qcom,smdpkt-remote = "adsp";
+			qcom,smdpkt-port-name = "apr_apps2";
+			qcom,smdpkt-dev-name = "apr_apps2";
+		};
+
+		qcom,smdpkt-loopback {
+			qcom,smdpkt-remote = "modem";
+			qcom,smdpkt-port-name = "LOOPBACK";
+			qcom,smdpkt-dev-name = "smd_pkt_loopback";
+		};
+	};
+
 	qcom,wdt@b017000 {
 		compatible = "qcom,msm-watchdog";
 		reg = <0xb017000 0x1000>;
diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index c65430b1..3b0adcd 100644
--- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
@@ -483,6 +483,10 @@
 				regulator-min-microvolt = <4600000>;
 				regulator-max-microvolt = <6000000>;
 
+				interrupts = <0x3 0xdc 0x2
+						IRQ_TYPE_EDGE_RISING>;
+				interrupt-names = "ibb-sc-err";
+
 				qcom,qpnp-ibb-min-voltage = <1400000>;
 				qcom,qpnp-ibb-step-size = <100000>;
 				qcom,qpnp-ibb-slew-rate = <2000000>;
@@ -516,8 +520,11 @@
 				regulator-max-microvolt = <6000000>;
 
 				interrupts = <0x3 0xde 0x0
+						IRQ_TYPE_EDGE_RISING>,
+					     <0x3 0xde 0x1
 						IRQ_TYPE_EDGE_RISING>;
-				interrupt-names = "lab-vreg-ok";
+				interrupt-names = "lab-vreg-ok", "lab-sc-err";
+
 				qcom,qpnp-lab-min-voltage = <4600000>;
 				qcom,qpnp-lab-step-size = <100000>;
 				qcom,qpnp-lab-slew-rate = <5000>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index 1a93fc2..95e5585 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -394,6 +394,60 @@
 
 			};
 
+			qcom,gpu-pwrlevels-3 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				qcom,speed-bin = <163>;
+
+				qcom,initial-pwrlevel = <3>;
+
+				/* SVS_L1 */
+				qcom,gpu-pwrlevel@0 {
+					reg = <0>;
+					qcom,gpu-freq = <430000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* SVS */
+				qcom,gpu-pwrlevel@1 {
+					reg = <1>;
+					qcom,gpu-freq = <355000000>;
+					qcom,bus-freq = <8>;
+					qcom,bus-min = <5>;
+					qcom,bus-max = <9>;
+				};
+
+				/* LOW SVS */
+				qcom,gpu-pwrlevel@2 {
+					reg = <2>;
+					qcom,gpu-freq = <267000000>;
+					qcom,bus-freq = <6>;
+					qcom,bus-min = <4>;
+					qcom,bus-max = <8>;
+				};
+
+				/* MIN SVS */
+				qcom,gpu-pwrlevel@3 {
+					reg = <3>;
+					qcom,gpu-freq = <180000000>;
+					qcom,bus-freq = <4>;
+					qcom,bus-min = <3>;
+					qcom,bus-max = <4>;
+				};
+
+				/* XO */
+				qcom,gpu-pwrlevel@4 {
+					reg = <4>;
+					qcom,gpu-freq = <0>;
+					qcom,bus-freq = <0>;
+					qcom,bus-min = <0>;
+					qcom,bus-max = <0>;
+				};
+			};
+
 		};
 
 	};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
index 0ea4b1f..3ea4fa7 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
@@ -37,6 +37,38 @@
 	/delete-property/ qcom,dsi-display-active;
 };
 
+&dsi_panel_pwr_supply_labibb_amoled {
+	qcom,panel-supply-entry@2 {
+		reg = <2>;
+		qcom,supply-name = "lab";
+		qcom,supply-min-voltage = <4600000>;
+		qcom,supply-max-voltage = <6100000>;
+		qcom,supply-enable-load = <100000>;
+		qcom,supply-disable-load = <100>;
+	};
+
+	qcom,panel-supply-entry@3 {
+		reg = <3>;
+		qcom,supply-name = "ibb";
+		qcom,supply-min-voltage = <4000000>;
+		qcom,supply-max-voltage = <6300000>;
+		qcom,supply-enable-load = <100000>;
+		qcom,supply-disable-load = <100>;
+	};
+
+	qcom,panel-supply-entry@4 {
+		reg = <4>;
+		qcom,supply-name = "oledb";
+		qcom,supply-min-voltage = <5000000>;
+		qcom,supply-max-voltage = <8100000>;
+		qcom,supply-enable-load = <100000>;
+		qcom,supply-disable-load = <100>;
+	};
+};
+
 &dsi_rm67195_amoled_fhd_cmd_display {
 	qcom,dsi-display-active;
+	lab-supply = <&lab_regulator>;
+	ibb-supply = <&ibb_regulator>;
+	oledb-supply = <&pm660a_oledb>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
index 1cf52f5..64133dd 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
@@ -31,6 +31,38 @@
 	/delete-property/ qcom,dsi-display-active;
 };
 
+&dsi_panel_pwr_supply_labibb_amoled {
+	qcom,panel-supply-entry@2 {
+		reg = <2>;
+		qcom,supply-name = "lab";
+		qcom,supply-min-voltage = <4600000>;
+		qcom,supply-max-voltage = <6100000>;
+		qcom,supply-enable-load = <100000>;
+		qcom,supply-disable-load = <100>;
+	};
+
+	qcom,panel-supply-entry@3 {
+		reg = <3>;
+		qcom,supply-name = "ibb";
+		qcom,supply-min-voltage = <4000000>;
+		qcom,supply-max-voltage = <6300000>;
+		qcom,supply-enable-load = <100000>;
+		qcom,supply-disable-load = <100>;
+	};
+
+	qcom,panel-supply-entry@4 {
+		reg = <4>;
+		qcom,supply-name = "oledb";
+		qcom,supply-min-voltage = <5000000>;
+		qcom,supply-max-voltage = <8100000>;
+		qcom,supply-enable-load = <100000>;
+		qcom,supply-disable-load = <100>;
+	};
+};
+
 &dsi_rm67195_amoled_fhd_cmd_display {
 	qcom,dsi-display-active;
+	lab-supply = <&lab_regulator>;
+	ibb-supply = <&ibb_regulator>;
+	oledb-supply = <&pm660a_oledb>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index 8dbd063..0f849d7 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -124,33 +124,6 @@
 			qcom,supply-enable-load = <13200>;
 			qcom,supply-disable-load = <80>;
 		};
-
-		qcom,panel-supply-entry@2 {
-			reg = <2>;
-			qcom,supply-name = "lab";
-			qcom,supply-min-voltage = <4600000>;
-			qcom,supply-max-voltage = <6100000>;
-			qcom,supply-enable-load = <100000>;
-			qcom,supply-disable-load = <100>;
-		};
-
-		qcom,panel-supply-entry@3 {
-			reg = <3>;
-			qcom,supply-name = "ibb";
-			qcom,supply-min-voltage = <4000000>;
-			qcom,supply-max-voltage = <6300000>;
-			qcom,supply-enable-load = <100000>;
-			qcom,supply-disable-load = <100>;
-		};
-
-		qcom,panel-supply-entry@4 {
-			reg = <4>;
-			qcom,supply-name = "oledb";
-			qcom,supply-min-voltage = <5000000>;
-			qcom,supply-max-voltage = <8100000>;
-			qcom,supply-enable-load = <100000>;
-			qcom,supply-disable-load = <100>;
-		};
 	};
 
 	dsi_dual_nt35597_truly_video_display: qcom,dsi-display@0 {
@@ -422,9 +395,6 @@
 		qcom,dsi-panel = <&dsi_rm67195_amoled_fhd_cmd>;
 		vddio-supply = <&pm660_l11>;
 		vdda-3p3-supply = <&pm660l_l6>;
-		lab-supply = <&lab_regulator>;
-		ibb-supply = <&ibb_regulator>;
-		oledb-supply = <&pm660a_oledb>;
 	};
 
 	dsi_nt35695b_truly_fhd_video_display: qcom,dsi-display@13 {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
index 2b80c22..a918687 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
@@ -140,8 +140,8 @@
 		qcom,sde-has-dest-scaler;
 		qcom,sde-max-dest-scaler-input-linewidth = <2048>;
 		qcom,sde-max-dest-scaler-output-linewidth = <2560>;
-		qcom,sde-max-bw-low-kbps = <9600000>;
-		qcom,sde-max-bw-high-kbps = <9600000>;
+		qcom,sde-max-bw-low-kbps = <6800000>;
+		qcom,sde-max-bw-high-kbps = <6800000>;
 		qcom,sde-dram-channels = <2>;
 		qcom,sde-num-nrt-paths = <0>;
 		qcom,sde-dspp-ad-version = <0x00040000>;
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/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 12365b3..75702c5 100644
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -286,7 +286,6 @@
 CONFIG_SERIAL_MSM_CONSOLE=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_MSM_LEGACY=y
-CONFIG_MSM_ADSPRPC=y
 CONFIG_MSM_RDBG=m
 CONFIG_I2C_CHARDEV=y
 CONFIG_SPI=y
@@ -401,14 +400,8 @@
 CONFIG_QCOM_SECURE_BUFFER=y
 CONFIG_QCOM_EARLY_RANDOM=y
 CONFIG_MSM_SMEM=y
-CONFIG_MSM_GLINK=y
-CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
-CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
-CONFIG_MSM_GLINK_SPI_XPRT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
-CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index 8757cc3..789d403 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -296,7 +296,6 @@
 CONFIG_SERIAL_MSM_CONSOLE=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_MSM_LEGACY=y
-CONFIG_MSM_ADSPRPC=y
 CONFIG_MSM_RDBG=m
 CONFIG_I2C_CHARDEV=y
 CONFIG_SPI=y
@@ -417,15 +416,9 @@
 CONFIG_QCOM_SECURE_BUFFER=y
 CONFIG_QCOM_EARLY_RANDOM=y
 CONFIG_MSM_SMEM=y
-CONFIG_MSM_GLINK=y
-CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
-CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
-CONFIG_MSM_GLINK_SPI_XPRT=y
 CONFIG_TRACER_PKT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
-CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index bd42455..9a43bb6 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -430,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
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 718c415..822324d 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -434,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
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/Kconfig b/drivers/char/Kconfig
index 49fb8e5..1ea2053 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -582,6 +582,16 @@
 
 source "drivers/s390/char/Kconfig"
 
+config MSM_SMD_PKT
+	bool "Enable device interface for some SMD packet ports"
+	default n
+	depends on MSM_SMD
+	help
+	  smd_pkt driver provides the interface for the userspace clients
+	  to communicate over smd via device nodes. This enable the
+	  usersapce clients to read and write to some smd packets channel
+	  for MSM chipset.
+
 config TILE_SROM
 	bool "Character-device access via hypervisor to the Tilera SPI ROM"
 	depends on TILE
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 19c3c98..81283c4 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,7 @@
 obj-$(CONFIG_VIRTIO_CONSOLE)	+= virtio_console.o
 obj-$(CONFIG_RAW_DRIVER)	+= raw.o
 obj-$(CONFIG_SGI_SNSC)		+= snsc.o snsc_event.o
+obj-$(CONFIG_MSM_SMD_PKT)	+= msm_smd_pkt.o
 obj-$(CONFIG_MSPEC)		+= mspec.o
 obj-$(CONFIG_MMTIMER)		+= mmtimer.o
 obj-$(CONFIG_UV_MMTIMER)	+= uv_mmtimer.o
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 6377677..9cecb03 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -198,8 +198,10 @@
 			continue;
 
 		found = 1;
-		driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
-		atomic_inc(&driver->data_ready_notif[i]);
+		if (!(driver->data_ready[i] & USER_SPACE_DATA_TYPE)) {
+			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
+			atomic_inc(&driver->data_ready_notif[i]);
+		}
 		pr_debug("diag: wake up logging process\n");
 		wake_up_interruptible(&driver->wait_q);
 	}
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 919ea0f..a1c9d68 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1848,9 +1848,10 @@
 		mutex_unlock(&driver->diagchar_mutex);
 		return -EINVAL;
 	}
-
-	driver->data_ready[i] |= DEINIT_TYPE;
-	atomic_inc(&driver->data_ready_notif[i]);
+	if (!(driver->data_ready[i] & DEINIT_TYPE)) {
+		driver->data_ready[i] |= DEINIT_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
+	}
 	mutex_unlock(&driver->diagchar_mutex);
 	wake_up_interruptible(&driver->wait_q);
 
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 4195b40..da13912 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -492,7 +492,8 @@
 
 	mutex_lock(&driver->diagchar_mutex);
 	for (i = 0; i < driver->num_clients; i++)
-		if (driver->client_map[i].pid != 0) {
+		if (driver->client_map[i].pid != 0 &&
+			!(driver->data_ready[i] & type)) {
 			driver->data_ready[i] |= type;
 			atomic_inc(&driver->data_ready_notif[i]);
 		}
@@ -511,9 +512,11 @@
 				if (driver->client_map[j].pid != 0 &&
 					driver->client_map[j].pid ==
 					driver->md_session_map[i]->pid) {
-					driver->data_ready[j] |= type;
-					atomic_inc(
+					if (!(driver->data_ready[i] & type)) {
+						driver->data_ready[j] |= type;
+						atomic_inc(
 						&driver->data_ready_notif[j]);
+					}
 					break;
 				}
 			}
@@ -528,8 +531,10 @@
 	mutex_lock(&driver->diagchar_mutex);
 	for (i = 0; i < driver->num_clients; i++)
 		if (driver->client_map[i].pid == process_id) {
-			driver->data_ready[i] |= data_type;
-			atomic_inc(&driver->data_ready_notif[i]);
+			if (!(driver->data_ready[i] & data_type)) {
+				driver->data_ready[i] |= data_type;
+				atomic_inc(&driver->data_ready_notif[i]);
+			}
 			break;
 		}
 	wake_up_interruptible(&driver->wait_q);
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
new file mode 100644
index 0000000..ff77cb2
--- /dev/null
+++ b/drivers/char/msm_smd_pkt.c
@@ -0,0 +1,1397 @@
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*
+ * SMD Packet Driver -- Provides a binary SMD non-muxed packet port
+ *                       interface.
+ */
+
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/msm_smd_pkt.h>
+#include <linux/poll.h>
+#include <soc/qcom/smd.h>
+#include <soc/qcom/smsm.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <asm/ioctls.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/ipc_logging.h>
+
+#define MODULE_NAME "msm_smdpkt"
+#define DEVICE_NAME "smdpkt"
+#define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */
+
+struct smd_pkt_dev {
+	struct list_head dev_list;
+	char dev_name[SMD_MAX_CH_NAME_LEN];
+	char ch_name[SMD_MAX_CH_NAME_LEN];
+	uint32_t edge;
+
+	struct cdev cdev;
+	struct device *devicep;
+	void *pil;
+
+	struct smd_channel *ch;
+	struct mutex ch_lock;
+	struct mutex rx_lock;
+	struct mutex tx_lock;
+	wait_queue_head_t ch_read_wait_queue;
+	wait_queue_head_t ch_write_wait_queue;
+	wait_queue_head_t ch_opened_wait_queue;
+
+	int i;
+	int ref_cnt;
+
+	int blocking_write;
+	int is_open;
+	int poll_mode;
+	unsigned int ch_size;
+	uint open_modem_wait;
+
+	int has_reset;
+	int do_reset_notification;
+	struct completion ch_allocated;
+	struct wakeup_source pa_ws;	/* Packet Arrival Wakeup Source */
+	struct work_struct packet_arrival_work;
+	spinlock_t pa_spinlock;
+	int ws_locked;
+};
+
+
+struct smd_pkt_driver {
+	struct list_head list;
+	int ref_cnt;
+	char pdriver_name[SMD_MAX_CH_NAME_LEN];
+	struct platform_driver driver;
+};
+
+static DEFINE_MUTEX(smd_pkt_driver_lock_lha1);
+static LIST_HEAD(smd_pkt_driver_list);
+
+struct class *smd_pkt_classp;
+static dev_t smd_pkt_number;
+static struct delayed_work loopback_work;
+static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
+static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
+static uint32_t is_modem_smsm_inited(void);
+
+static DEFINE_MUTEX(smd_pkt_dev_lock_lha1);
+static LIST_HEAD(smd_pkt_dev_list);
+static int num_smd_pkt_ports;
+
+#define SMD_PKT_IPC_LOG_PAGE_CNT 2
+static void *smd_pkt_ilctxt;
+
+static int msm_smd_pkt_debug_mask;
+module_param_named(debug_mask, msm_smd_pkt_debug_mask, int, 0664);
+
+enum {
+	SMD_PKT_STATUS = 1U << 0,
+	SMD_PKT_READ = 1U << 1,
+	SMD_PKT_WRITE = 1U << 2,
+	SMD_PKT_POLL = 1U << 5,
+};
+
+#define DEBUG
+
+#ifdef DEBUG
+
+#define SMD_PKT_LOG_STRING(x...) \
+do { \
+	if (smd_pkt_ilctxt) \
+		ipc_log_string(smd_pkt_ilctxt, "<SMD_PKT>: "x); \
+} while (0)
+
+#define D_STATUS(x...) \
+do { \
+	if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
+		pr_info("Status: "x); \
+	SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_READ(x...) \
+do { \
+	if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
+		pr_info("Read: "x); \
+	SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_WRITE(x...) \
+do { \
+	if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
+		pr_info("Write: "x); \
+	SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define D_POLL(x...) \
+do { \
+	if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
+		pr_info("Poll: "x); \
+	SMD_PKT_LOG_STRING(x); \
+} while (0)
+
+#define E_SMD_PKT_SSR(x) \
+do { \
+	if (x->do_reset_notification) \
+		pr_err("%s notifying reset for smd_pkt_dev id:%d\n", \
+			__func__, x->i); \
+} while (0)
+#else
+#define D_STATUS(x...) do {} while (0)
+#define D_READ(x...) do {} while (0)
+#define D_WRITE(x...) do {} while (0)
+#define D_POLL(x...) do {} while (0)
+#define E_SMD_PKT_SSR(x) do {} while (0)
+#endif
+
+static ssize_t open_timeout_store(struct device *d,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t n)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+	unsigned long tmp;
+
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+		if (smd_pkt_devp->devicep == d) {
+			if (!kstrtoul(buf, 10, &tmp)) {
+				smd_pkt_devp->open_modem_wait = tmp;
+				mutex_unlock(&smd_pkt_dev_lock_lha1);
+				return n;
+			}
+			mutex_unlock(&smd_pkt_dev_lock_lha1);
+			pr_err("%s: unable to convert: %s to an int\n",
+						__func__, buf);
+			return -EINVAL;
+		}
+	}
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+	pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+	return -EINVAL;
+}
+
+static ssize_t open_timeout_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+		if (smd_pkt_devp->devicep == d) {
+			mutex_unlock(&smd_pkt_dev_lock_lha1);
+			return snprintf(buf, PAGE_SIZE, "%d\n",
+					smd_pkt_devp->open_modem_wait);
+		}
+	}
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+	pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+	return -EINVAL;
+
+}
+
+static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
+
+/**
+ * loopback_edge_store() - Set the edge type for loopback device
+ * @d:		Linux device structure
+ * @attr:	Device attribute structure
+ * @buf:	Input string
+ * @n:		Length of the input string
+ *
+ * This function is used to set the loopback device edge runtime
+ * by writing to the loopback_edge node.
+ */
+static ssize_t loopback_edge_store(struct device *d,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t n)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+	unsigned long tmp;
+
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+		if (smd_pkt_devp->devicep == d) {
+			if (!kstrtoul(buf, 10, &tmp)) {
+				smd_pkt_devp->edge = tmp;
+				mutex_unlock(&smd_pkt_dev_lock_lha1);
+				return n;
+			}
+			mutex_unlock(&smd_pkt_dev_lock_lha1);
+			pr_err("%s: unable to convert: %s to an int\n",
+						__func__, buf);
+			return -EINVAL;
+		}
+	}
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+	pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+	return -EINVAL;
+}
+
+/**
+ * loopback_edge_show() - Get the edge type for loopback device
+ * @d:		Linux device structure
+ * @attr:	Device attribute structure
+ * @buf:	Output buffer
+ *
+ * This function is used to get the loopback device edge runtime
+ * by reading the loopback_edge node.
+ */
+static ssize_t loopback_edge_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+		if (smd_pkt_devp->devicep == d) {
+			mutex_unlock(&smd_pkt_dev_lock_lha1);
+			return snprintf(buf, PAGE_SIZE, "%d\n",
+					smd_pkt_devp->edge);
+		}
+	}
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+	pr_err("%s: unable to match device to valid smd_pkt port\n", __func__);
+	return -EINVAL;
+
+}
+
+static DEVICE_ATTR(loopback_edge, 0664, loopback_edge_show,
+						loopback_edge_store);
+
+static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
+{
+	smd_pkt_devp->do_reset_notification = 0;
+
+	return -ENETRESET;
+}
+
+static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
+{
+	smd_pkt_devp->do_reset_notification = 1;
+	smd_pkt_devp->has_reset = 1;
+
+	smd_pkt_devp->is_open = 0;
+
+	wake_up(&smd_pkt_devp->ch_read_wait_queue);
+	wake_up(&smd_pkt_devp->ch_write_wait_queue);
+	wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
+	D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+}
+
+static void loopback_probe_worker(struct work_struct *work)
+{
+
+	/* Wait for the modem SMSM to be inited for the SMD
+	 ** Loopback channel to be allocated at the modem. Since
+	 ** the wait need to be done atmost once, using msleep
+	 ** doesn't degrade the performance.
+	 */
+	if (!is_modem_smsm_inited())
+		schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
+	else
+		smsm_change_state(SMSM_APPS_STATE,
+			  0, SMSM_SMD_LOOPBACK);
+
+}
+
+static void packet_arrival_worker(struct work_struct *work)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+	unsigned long flags;
+
+	smd_pkt_devp = container_of(work, struct smd_pkt_dev,
+				    packet_arrival_work);
+	mutex_lock(&smd_pkt_devp->ch_lock);
+	spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+	if (smd_pkt_devp->ch && smd_pkt_devp->ws_locked) {
+		D_READ("%s locking smd_pkt_dev id:%d wakeup source\n",
+			__func__, smd_pkt_devp->i);
+		/*
+		 * Keep system awake long enough to allow userspace client
+		 * to process the packet.
+		 */
+		__pm_wakeup_event(&smd_pkt_devp->pa_ws, WAKEUPSOURCE_TIMEOUT);
+	}
+	spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+	mutex_unlock(&smd_pkt_devp->ch_lock);
+}
+
+static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
+					     unsigned long arg)
+{
+	int ret;
+	struct smd_pkt_dev *smd_pkt_devp;
+	uint32_t val;
+
+	smd_pkt_devp = file->private_data;
+	if (!smd_pkt_devp)
+		return -EINVAL;
+
+	mutex_lock(&smd_pkt_devp->ch_lock);
+	switch (cmd) {
+	case TIOCMGET:
+		D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
+			 __func__, smd_pkt_devp->i);
+		ret = smd_tiocmget(smd_pkt_devp->ch);
+		break;
+	case TIOCMSET:
+		ret = get_user(val, (uint32_t *)arg);
+		if (ret) {
+			pr_err("Error getting TIOCMSET value\n");
+			mutex_unlock(&smd_pkt_devp->ch_lock);
+			return ret;
+		}
+		D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d arg[0x%x]\n",
+			 __func__, smd_pkt_devp->i, val);
+		ret = smd_tiocmset(smd_pkt_devp->ch, val, ~val);
+		break;
+	case SMD_PKT_IOCTL_BLOCKING_WRITE:
+		ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
+		break;
+	default:
+		pr_err_ratelimited("%s: Unrecognized ioctl command %d\n",
+			__func__, cmd);
+		ret = -ENOIOCTLCMD;
+	}
+	mutex_unlock(&smd_pkt_devp->ch_lock);
+
+	return ret;
+}
+
+ssize_t smd_pkt_read(struct file *file,
+		       char __user *_buf,
+		       size_t count,
+		       loff_t *ppos)
+{
+	int r;
+	int bytes_read;
+	int pkt_size;
+	struct smd_pkt_dev *smd_pkt_devp;
+	unsigned long flags;
+	void *buf;
+
+	smd_pkt_devp = file->private_data;
+
+	if (!smd_pkt_devp) {
+		pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!smd_pkt_devp->ch) {
+		pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		return -EINVAL;
+	}
+
+	if (smd_pkt_devp->do_reset_notification) {
+		/* notify client that a reset occurred */
+		E_SMD_PKT_SSR(smd_pkt_devp);
+		return notify_reset(smd_pkt_devp);
+	}
+	D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %zu\n",
+		__func__, smd_pkt_devp->i, count);
+
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+wait_for_packet:
+	r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
+				     !smd_pkt_devp->ch ||
+				     (smd_cur_packet_size(smd_pkt_devp->ch) > 0
+				      && smd_read_avail(smd_pkt_devp->ch)) ||
+				     smd_pkt_devp->has_reset);
+
+	mutex_lock(&smd_pkt_devp->rx_lock);
+	if (smd_pkt_devp->has_reset) {
+		mutex_unlock(&smd_pkt_devp->rx_lock);
+		E_SMD_PKT_SSR(smd_pkt_devp);
+		kfree(buf);
+		return notify_reset(smd_pkt_devp);
+	}
+
+	if (!smd_pkt_devp->ch) {
+		mutex_unlock(&smd_pkt_devp->rx_lock);
+		pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	if (r < 0) {
+		mutex_unlock(&smd_pkt_devp->rx_lock);
+		/* qualify error message */
+		if (r != -ERESTARTSYS) {
+			/* we get this anytime a signal comes in */
+			pr_err_ratelimited("%s: wait_event_interruptible on smd_pkt_dev id:%d ret %i\n",
+				__func__, smd_pkt_devp->i, r);
+		}
+		kfree(buf);
+		return r;
+	}
+
+	/* Here we have a whole packet waiting for us */
+	pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
+
+	if (!pkt_size) {
+		pr_err_ratelimited("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
+			__func__, smd_pkt_devp->i);
+		mutex_unlock(&smd_pkt_devp->rx_lock);
+		goto wait_for_packet;
+	}
+
+	if (pkt_size < 0) {
+		pr_err_ratelimited("%s: Error %d obtaining packet size for Channel %s",
+				__func__, pkt_size, smd_pkt_devp->ch_name);
+		kfree(buf);
+		return pkt_size;
+	}
+
+	if ((uint32_t)pkt_size > count) {
+		pr_err_ratelimited("%s: failure on smd_pkt_dev id: %d - packet size %d > buffer size %zu,",
+			__func__, smd_pkt_devp->i,
+			pkt_size, count);
+		mutex_unlock(&smd_pkt_devp->rx_lock);
+		kfree(buf);
+		return -ETOOSMALL;
+	}
+
+	bytes_read = 0;
+	do {
+		r = smd_read(smd_pkt_devp->ch,
+					 (buf + bytes_read),
+					 (pkt_size - bytes_read));
+		if (r < 0) {
+			mutex_unlock(&smd_pkt_devp->rx_lock);
+			if (smd_pkt_devp->has_reset) {
+				E_SMD_PKT_SSR(smd_pkt_devp);
+				return notify_reset(smd_pkt_devp);
+			}
+			pr_err_ratelimited("%s Error while reading %d\n",
+				__func__, r);
+			kfree(buf);
+			return r;
+		}
+		bytes_read += r;
+		if (pkt_size != bytes_read)
+			wait_event(smd_pkt_devp->ch_read_wait_queue,
+				   smd_read_avail(smd_pkt_devp->ch) ||
+				   smd_pkt_devp->has_reset);
+		if (smd_pkt_devp->has_reset) {
+			mutex_unlock(&smd_pkt_devp->rx_lock);
+			E_SMD_PKT_SSR(smd_pkt_devp);
+			kfree(buf);
+			return notify_reset(smd_pkt_devp);
+		}
+	} while (pkt_size != bytes_read);
+	mutex_unlock(&smd_pkt_devp->rx_lock);
+
+	mutex_lock(&smd_pkt_devp->ch_lock);
+	spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+	if (smd_pkt_devp->poll_mode &&
+	    !smd_cur_packet_size(smd_pkt_devp->ch)) {
+		__pm_relax(&smd_pkt_devp->pa_ws);
+		smd_pkt_devp->ws_locked = 0;
+		smd_pkt_devp->poll_mode = 0;
+		D_READ("%s unlocked smd_pkt_dev id:%d wakeup_source\n",
+			__func__, smd_pkt_devp->i);
+	}
+	spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+	mutex_unlock(&smd_pkt_devp->ch_lock);
+
+	r = copy_to_user(_buf, buf, bytes_read);
+	if (r) {
+		kfree(buf);
+		return -EFAULT;
+	}
+	D_READ("Finished %s on smd_pkt_dev id:%d  %d bytes\n",
+		__func__, smd_pkt_devp->i, bytes_read);
+	kfree(buf);
+
+	/* check and wakeup read threads waiting on this device */
+	check_and_wakeup_reader(smd_pkt_devp);
+
+	return bytes_read;
+}
+
+ssize_t smd_pkt_write(struct file *file,
+		       const char __user *_buf,
+		       size_t count,
+		       loff_t *ppos)
+{
+	int r = 0, bytes_written;
+	struct smd_pkt_dev *smd_pkt_devp;
+	DEFINE_WAIT(write_wait);
+	void *buf;
+
+	smd_pkt_devp = file->private_data;
+
+	if (!smd_pkt_devp) {
+		pr_err_ratelimited("%s on NULL smd_pkt_dev\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!smd_pkt_devp->ch) {
+		pr_err_ratelimited("%s on a closed smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		return -EINVAL;
+	}
+
+	if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
+		E_SMD_PKT_SSR(smd_pkt_devp);
+		/* notify client that a reset occurred */
+		return notify_reset(smd_pkt_devp);
+	}
+	D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %zu\n",
+		__func__, smd_pkt_devp->i, count);
+
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	r = copy_from_user(buf, _buf, count);
+	if (r) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	mutex_lock(&smd_pkt_devp->tx_lock);
+	if (!smd_pkt_devp->blocking_write) {
+		if (smd_write_avail(smd_pkt_devp->ch) < count) {
+			pr_err_ratelimited("%s: Not enough space in smd_pkt_dev id:%d\n",
+				   __func__, smd_pkt_devp->i);
+			mutex_unlock(&smd_pkt_devp->tx_lock);
+			kfree(buf);
+			return -ENOMEM;
+		}
+	}
+
+	r = smd_write_start(smd_pkt_devp->ch, count);
+	if (r < 0) {
+		mutex_unlock(&smd_pkt_devp->tx_lock);
+		pr_err_ratelimited("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
+			__func__, r, smd_pkt_devp->i);
+		kfree(buf);
+		return r;
+	}
+
+	bytes_written = 0;
+	do {
+		prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
+				&write_wait, TASK_UNINTERRUPTIBLE);
+		if (!smd_write_segment_avail(smd_pkt_devp->ch) &&
+		    !smd_pkt_devp->has_reset) {
+			smd_enable_read_intr(smd_pkt_devp->ch);
+			schedule();
+		}
+		finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
+		smd_disable_read_intr(smd_pkt_devp->ch);
+
+		if (smd_pkt_devp->has_reset) {
+			mutex_unlock(&smd_pkt_devp->tx_lock);
+			E_SMD_PKT_SSR(smd_pkt_devp);
+			kfree(buf);
+			return notify_reset(smd_pkt_devp);
+		}
+		r = smd_write_segment(smd_pkt_devp->ch,
+				      (void *)(buf + bytes_written),
+				      (count - bytes_written));
+		if (r < 0) {
+			mutex_unlock(&smd_pkt_devp->tx_lock);
+			if (smd_pkt_devp->has_reset) {
+				E_SMD_PKT_SSR(smd_pkt_devp);
+				return notify_reset(smd_pkt_devp);
+			}
+			pr_err_ratelimited("%s on smd_pkt_dev id:%d failed r:%d\n",
+				__func__, smd_pkt_devp->i, r);
+			kfree(buf);
+			return r;
+		}
+		bytes_written += r;
+	} while (bytes_written != count);
+	smd_write_end(smd_pkt_devp->ch);
+	mutex_unlock(&smd_pkt_devp->tx_lock);
+	D_WRITE("Finished %s on smd_pkt_dev id:%d %zu bytes\n",
+		__func__, smd_pkt_devp->i, count);
+
+	kfree(buf);
+	return count;
+}
+
+static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+	unsigned int mask = 0;
+
+	smd_pkt_devp = file->private_data;
+	if (!smd_pkt_devp) {
+		pr_err_ratelimited("%s on a NULL device\n", __func__);
+		return POLLERR;
+	}
+
+	smd_pkt_devp->poll_mode = 1;
+	poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
+	mutex_lock(&smd_pkt_devp->ch_lock);
+	if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
+		mutex_unlock(&smd_pkt_devp->ch_lock);
+		return POLLERR;
+	}
+
+	if (smd_read_avail(smd_pkt_devp->ch)) {
+		mask |= POLLIN | POLLRDNORM;
+		D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
+			__func__, smd_pkt_devp->i);
+	}
+	mutex_unlock(&smd_pkt_devp->ch_lock);
+
+	return mask;
+}
+
+static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
+{
+	int sz;
+	unsigned long flags;
+
+	if (!smd_pkt_devp) {
+		pr_err("%s on a NULL device\n", __func__);
+		return;
+	}
+
+	if (!smd_pkt_devp->ch) {
+		pr_err("%s on a closed smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		return;
+	}
+
+	sz = smd_cur_packet_size(smd_pkt_devp->ch);
+	if (sz == 0) {
+		D_READ("%s: No packet in smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		return;
+	}
+	if (!smd_read_avail(smd_pkt_devp->ch)) {
+		D_READ(
+			"%s: packet size is %d in smd_pkt_dev id:%d - but the data isn't here\n",
+			__func__, sz, smd_pkt_devp->i);
+		return;
+	}
+
+	/* here we have a packet of size sz ready */
+	spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+	__pm_stay_awake(&smd_pkt_devp->pa_ws);
+	smd_pkt_devp->ws_locked = 1;
+	spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+	wake_up(&smd_pkt_devp->ch_read_wait_queue);
+	schedule_work(&smd_pkt_devp->packet_arrival_work);
+	D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+}
+
+static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
+{
+	int sz;
+
+	if (!smd_pkt_devp) {
+		pr_err("%s on a NULL device\n", __func__);
+		return;
+	}
+
+	if (!smd_pkt_devp->ch) {
+		pr_err("%s on a closed smd_pkt_dev id:%d\n",
+			__func__, smd_pkt_devp->i);
+		return;
+	}
+
+	sz = smd_write_segment_avail(smd_pkt_devp->ch);
+	if (sz) {
+		D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
+			__func__, sz, smd_pkt_devp->i);
+		smd_disable_read_intr(smd_pkt_devp->ch);
+		wake_up(&smd_pkt_devp->ch_write_wait_queue);
+	}
+}
+
+static void ch_notify(void *priv, unsigned int event)
+{
+	struct smd_pkt_dev *smd_pkt_devp = priv;
+
+	if (smd_pkt_devp->ch == 0) {
+		if (event != SMD_EVENT_CLOSE)
+			pr_err("%s on a closed smd_pkt_dev id:%d\n",
+					__func__, smd_pkt_devp->i);
+		return;
+	}
+
+	switch (event) {
+	case SMD_EVENT_DATA: {
+		D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
+			 __func__, smd_pkt_devp->i);
+		check_and_wakeup_reader(smd_pkt_devp);
+		if (smd_pkt_devp->blocking_write)
+			check_and_wakeup_writer(smd_pkt_devp);
+		break;
+	}
+	case SMD_EVENT_OPEN:
+		D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
+			  __func__, smd_pkt_devp->i);
+		smd_pkt_devp->has_reset = 0;
+		smd_pkt_devp->is_open = 1;
+		wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
+		break;
+	case SMD_EVENT_CLOSE:
+		D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
+			  __func__, smd_pkt_devp->i);
+		smd_pkt_devp->is_open = 0;
+		/* put port into reset state */
+		clean_and_signal(smd_pkt_devp);
+		if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK"))
+			schedule_delayed_work(&loopback_work,
+					msecs_to_jiffies(1000));
+		break;
+	}
+}
+
+static int smd_pkt_dummy_probe(struct platform_device *pdev)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_for_each_entry(smd_pkt_devp, &smd_pkt_dev_list, dev_list) {
+		if (smd_pkt_devp->edge == pdev->id
+		    && !strcmp(pdev->name, smd_pkt_devp->ch_name)) {
+			complete_all(&smd_pkt_devp->ch_allocated);
+			D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
+				 __func__, smd_pkt_devp->i);
+			break;
+		}
+	}
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+	return 0;
+}
+
+static uint32_t is_modem_smsm_inited(void)
+{
+	uint32_t modem_state;
+	uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
+
+	modem_state = smsm_get_state(SMSM_MODEM_STATE);
+	return (modem_state & ready_state) == ready_state;
+}
+
+/**
+ * smd_pkt_add_driver() - Add platform drivers for smd pkt device
+ *
+ * @smd_pkt_devp: pointer to the smd pkt device structure
+ *
+ * @returns:	0 for success, standard Linux error code otherwise
+ *
+ * This function is used to register platform driver once for all
+ * smd pkt devices which have same names and increment the reference
+ * count for 2nd to nth devices.
+ */
+static int smd_pkt_add_driver(struct smd_pkt_dev *smd_pkt_devp)
+{
+	int r = 0;
+	struct smd_pkt_driver *smd_pkt_driverp;
+	struct smd_pkt_driver *item;
+
+	if (!smd_pkt_devp) {
+		pr_err("%s on a NULL device\n", __func__);
+		return -EINVAL;
+	}
+	D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__,
+					smd_pkt_devp->ch_name);
+
+	mutex_lock(&smd_pkt_driver_lock_lha1);
+	list_for_each_entry(item, &smd_pkt_driver_list, list) {
+		if (!strcmp(item->pdriver_name, smd_pkt_devp->ch_name)) {
+			D_STATUS("%s:%s Already Platform driver reg. cnt:%d\n",
+				__func__, smd_pkt_devp->ch_name, item->ref_cnt);
+			++item->ref_cnt;
+			goto exit;
+		}
+	}
+
+	smd_pkt_driverp = kzalloc(sizeof(*smd_pkt_driverp), GFP_KERNEL);
+	if (IS_ERR_OR_NULL(smd_pkt_driverp)) {
+		pr_err("%s: kzalloc() failed for smd_pkt_driver[%s]\n",
+			__func__, smd_pkt_devp->ch_name);
+		r = -ENOMEM;
+		goto exit;
+	}
+
+	smd_pkt_driverp->driver.probe = smd_pkt_dummy_probe;
+	scnprintf(smd_pkt_driverp->pdriver_name, SMD_MAX_CH_NAME_LEN,
+		  "%s", smd_pkt_devp->ch_name);
+	smd_pkt_driverp->driver.driver.name = smd_pkt_driverp->pdriver_name;
+	smd_pkt_driverp->driver.driver.owner = THIS_MODULE;
+	r = platform_driver_register(&smd_pkt_driverp->driver);
+	if (r) {
+		pr_err("%s: %s Platform driver reg. failed\n",
+			__func__, smd_pkt_devp->ch_name);
+		kfree(smd_pkt_driverp);
+		goto exit;
+	}
+	++smd_pkt_driverp->ref_cnt;
+	list_add(&smd_pkt_driverp->list, &smd_pkt_driver_list);
+
+exit:
+	D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name);
+	mutex_unlock(&smd_pkt_driver_lock_lha1);
+	return r;
+}
+
+/**
+ * smd_pkt_remove_driver() - Remove the platform drivers for smd pkt device
+ *
+ * @smd_pkt_devp: pointer to the smd pkt device structure
+ *
+ * This function is used to decrement the reference count on
+ * platform drivers for smd pkt devices and removes the drivers
+ * when the reference count becomes zero.
+ */
+static void smd_pkt_remove_driver(struct smd_pkt_dev *smd_pkt_devp)
+{
+	struct smd_pkt_driver *smd_pkt_driverp;
+	bool found_item = false;
+
+	if (!smd_pkt_devp) {
+		pr_err("%s on a NULL device\n", __func__);
+		return;
+	}
+
+	D_STATUS("Begin %s on smd_pkt_ch[%s]\n", __func__,
+					smd_pkt_devp->ch_name);
+	mutex_lock(&smd_pkt_driver_lock_lha1);
+	list_for_each_entry(smd_pkt_driverp, &smd_pkt_driver_list, list) {
+		if (!strcmp(smd_pkt_driverp->pdriver_name,
+					smd_pkt_devp->ch_name)) {
+			found_item = true;
+			D_STATUS("%s:%s Platform driver cnt:%d\n",
+				__func__, smd_pkt_devp->ch_name,
+				smd_pkt_driverp->ref_cnt);
+			if (smd_pkt_driverp->ref_cnt > 0)
+				--smd_pkt_driverp->ref_cnt;
+			else
+				pr_warn("%s reference count <= 0\n", __func__);
+			break;
+		}
+	}
+	if (!found_item)
+		pr_err("%s:%s No item found in list.\n",
+				__func__, smd_pkt_devp->ch_name);
+
+	if (found_item && smd_pkt_driverp->ref_cnt == 0) {
+		platform_driver_unregister(&smd_pkt_driverp->driver);
+		smd_pkt_driverp->driver.probe = NULL;
+		list_del(&smd_pkt_driverp->list);
+		kfree(smd_pkt_driverp);
+	}
+	mutex_unlock(&smd_pkt_driver_lock_lha1);
+	D_STATUS("End %s on smd_pkt_ch[%s]\n", __func__, smd_pkt_devp->ch_name);
+}
+
+int smd_pkt_open(struct inode *inode, struct file *file)
+{
+	int r = 0;
+	struct smd_pkt_dev *smd_pkt_devp;
+	const char *peripheral = NULL;
+
+	smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
+
+	if (!smd_pkt_devp) {
+		pr_err_ratelimited("%s on a NULL device\n", __func__);
+		return -EINVAL;
+	}
+	D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
+
+	file->private_data = smd_pkt_devp;
+
+	mutex_lock(&smd_pkt_devp->ch_lock);
+	if (smd_pkt_devp->ch == 0) {
+		unsigned int open_wait_rem;
+
+		open_wait_rem = smd_pkt_devp->open_modem_wait * 1000;
+		reinit_completion(&smd_pkt_devp->ch_allocated);
+
+		r = smd_pkt_add_driver(smd_pkt_devp);
+		if (r) {
+			pr_err_ratelimited("%s: %s Platform driver reg. failed\n",
+				__func__, smd_pkt_devp->ch_name);
+			goto out;
+		}
+
+		peripheral = smd_edge_to_pil_str(smd_pkt_devp->edge);
+		if (!IS_ERR_OR_NULL(peripheral)) {
+			smd_pkt_devp->pil = subsystem_get(peripheral);
+			if (IS_ERR(smd_pkt_devp->pil)) {
+				r = PTR_ERR(smd_pkt_devp->pil);
+				pr_err_ratelimited("%s failed on smd_pkt_dev id:%d - subsystem_get failed for %s\n",
+					__func__, smd_pkt_devp->i, peripheral);
+				/*
+				 * Sleep inorder to reduce the frequency of
+				 * retry by user-space modules and to avoid
+				 * possible watchdog bite.
+				 */
+				msleep(open_wait_rem);
+				goto release_pd;
+			}
+		}
+
+		/* Wait for the modem SMSM to be inited for the SMD
+		 ** Loopback channel to be allocated at the modem. Since
+		 ** the wait need to be done atmost once, using msleep
+		 ** doesn't degrade the performance.
+		 */
+		if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) {
+			if (!is_modem_smsm_inited())
+				msleep(5000);
+			smsm_change_state(SMSM_APPS_STATE,
+					  0, SMSM_SMD_LOOPBACK);
+			msleep(100);
+		}
+
+		/*
+		 * Wait for a packet channel to be allocated so we know
+		 * the modem is ready enough.
+		 */
+		if (open_wait_rem) {
+			r = wait_for_completion_interruptible_timeout(
+				&smd_pkt_devp->ch_allocated,
+				msecs_to_jiffies(open_wait_rem));
+			if (r >= 0)
+				open_wait_rem = jiffies_to_msecs(r);
+			if (r == 0)
+				r = -ETIMEDOUT;
+			if (r == -ERESTARTSYS) {
+				pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d allocation interrupted\n",
+					__func__, smd_pkt_devp->i);
+				goto release_pil;
+			}
+			if (r < 0) {
+				pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d allocation failed rc:%d\n",
+					__func__, smd_pkt_devp->i, r);
+				goto release_pil;
+			}
+		}
+
+		r = smd_named_open_on_edge(smd_pkt_devp->ch_name,
+					   smd_pkt_devp->edge,
+					   &smd_pkt_devp->ch,
+					   smd_pkt_devp,
+					   ch_notify);
+		if (r < 0) {
+			pr_err_ratelimited("%s: %s open failed %d\n", __func__,
+			       smd_pkt_devp->ch_name, r);
+			goto release_pil;
+		}
+
+		open_wait_rem = max_t(unsigned int, 2000, open_wait_rem);
+		r = wait_event_interruptible_timeout(
+				smd_pkt_devp->ch_opened_wait_queue,
+				smd_pkt_devp->is_open,
+				msecs_to_jiffies(open_wait_rem));
+		if (r == 0)
+			r = -ETIMEDOUT;
+
+		if (r < 0) {
+			/* close the ch to sync smd's state with smd_pkt */
+			smd_close(smd_pkt_devp->ch);
+			smd_pkt_devp->ch = NULL;
+		}
+
+		if (r == -ERESTARTSYS) {
+			pr_info_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN interrupted\n",
+				__func__, smd_pkt_devp->i);
+		} else if (r < 0) {
+			pr_err_ratelimited("%s: wait on smd_pkt_dev id:%d OPEN event failed rc:%d\n",
+				__func__, smd_pkt_devp->i, r);
+		} else if (!smd_pkt_devp->is_open) {
+			pr_err_ratelimited("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
+				__func__, smd_pkt_devp->i);
+			r = -ENODEV;
+		} else {
+			smd_disable_read_intr(smd_pkt_devp->ch);
+			smd_pkt_devp->ch_size =
+				smd_write_avail(smd_pkt_devp->ch);
+			r = 0;
+			smd_pkt_devp->ref_cnt++;
+			D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
+				 __func__, smd_pkt_devp->i);
+		}
+	} else {
+		smd_pkt_devp->ref_cnt++;
+	}
+release_pil:
+	if (peripheral && (r < 0)) {
+		subsystem_put(smd_pkt_devp->pil);
+		smd_pkt_devp->pil = NULL;
+	}
+
+release_pd:
+	if (r < 0)
+		smd_pkt_remove_driver(smd_pkt_devp);
+out:
+	mutex_unlock(&smd_pkt_devp->ch_lock);
+
+
+	return r;
+}
+
+int smd_pkt_release(struct inode *inode, struct file *file)
+{
+	int r = 0;
+	struct smd_pkt_dev *smd_pkt_devp = file->private_data;
+	unsigned long flags;
+
+	if (!smd_pkt_devp) {
+		pr_err_ratelimited("%s on a NULL device\n", __func__);
+		return -EINVAL;
+	}
+	D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
+		 __func__, smd_pkt_devp->i);
+
+	mutex_lock(&smd_pkt_devp->ch_lock);
+	mutex_lock(&smd_pkt_devp->rx_lock);
+	mutex_lock(&smd_pkt_devp->tx_lock);
+	if (smd_pkt_devp->ref_cnt > 0)
+		smd_pkt_devp->ref_cnt--;
+
+	if (smd_pkt_devp->ch != 0 && smd_pkt_devp->ref_cnt == 0) {
+		clean_and_signal(smd_pkt_devp);
+		r = smd_close(smd_pkt_devp->ch);
+		smd_pkt_devp->ch = 0;
+		smd_pkt_devp->blocking_write = 0;
+		smd_pkt_devp->poll_mode = 0;
+		smd_pkt_remove_driver(smd_pkt_devp);
+		if (smd_pkt_devp->pil)
+			subsystem_put(smd_pkt_devp->pil);
+		smd_pkt_devp->has_reset = 0;
+		smd_pkt_devp->do_reset_notification = 0;
+		spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
+		if (smd_pkt_devp->ws_locked) {
+			__pm_relax(&smd_pkt_devp->pa_ws);
+			smd_pkt_devp->ws_locked = 0;
+		}
+		spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
+	}
+	mutex_unlock(&smd_pkt_devp->tx_lock);
+	mutex_unlock(&smd_pkt_devp->rx_lock);
+	mutex_unlock(&smd_pkt_devp->ch_lock);
+
+	if (flush_work(&smd_pkt_devp->packet_arrival_work))
+		D_STATUS("%s: Flushed work for smd_pkt_dev id:%d\n", __func__,
+				smd_pkt_devp->i);
+
+	D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
+		 __func__, smd_pkt_devp->i);
+
+	return r;
+}
+
+static const struct file_operations smd_pkt_fops = {
+	.owner = THIS_MODULE,
+	.open = smd_pkt_open,
+	.release = smd_pkt_release,
+	.read = smd_pkt_read,
+	.write = smd_pkt_write,
+	.poll = smd_pkt_poll,
+	.unlocked_ioctl = smd_pkt_ioctl,
+	.compat_ioctl = smd_pkt_ioctl,
+};
+
+static int smd_pkt_init_add_device(struct smd_pkt_dev *smd_pkt_devp, int i)
+{
+	int r = 0;
+
+	smd_pkt_devp->i = i;
+
+	init_waitqueue_head(&smd_pkt_devp->ch_read_wait_queue);
+	init_waitqueue_head(&smd_pkt_devp->ch_write_wait_queue);
+	smd_pkt_devp->is_open = 0;
+	smd_pkt_devp->poll_mode = 0;
+	smd_pkt_devp->ws_locked = 0;
+	init_waitqueue_head(&smd_pkt_devp->ch_opened_wait_queue);
+
+	spin_lock_init(&smd_pkt_devp->pa_spinlock);
+	mutex_init(&smd_pkt_devp->ch_lock);
+	mutex_init(&smd_pkt_devp->rx_lock);
+	mutex_init(&smd_pkt_devp->tx_lock);
+	wakeup_source_init(&smd_pkt_devp->pa_ws, smd_pkt_devp->dev_name);
+	INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
+	init_completion(&smd_pkt_devp->ch_allocated);
+
+	cdev_init(&smd_pkt_devp->cdev, &smd_pkt_fops);
+	smd_pkt_devp->cdev.owner = THIS_MODULE;
+
+	r = cdev_add(&smd_pkt_devp->cdev, (smd_pkt_number + i), 1);
+	if (r) {
+		pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d ret:%i\n",
+			__func__, i, r);
+		return r;
+	}
+
+	smd_pkt_devp->devicep =
+		device_create(smd_pkt_classp,
+			      NULL,
+			      (smd_pkt_number + i),
+			      NULL,
+			      smd_pkt_devp->dev_name);
+
+	if (IS_ERR_OR_NULL(smd_pkt_devp->devicep)) {
+		pr_err("%s: device_create() failed for smd_pkt_dev id:%d\n",
+			__func__, i);
+		r = -ENOMEM;
+		cdev_del(&smd_pkt_devp->cdev);
+		wakeup_source_trash(&smd_pkt_devp->pa_ws);
+		return r;
+	}
+	if (device_create_file(smd_pkt_devp->devicep,
+				&dev_attr_open_timeout))
+		pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n",
+			__func__, i);
+
+	if (!strcmp(smd_pkt_devp->ch_name, "LOOPBACK")) {
+		if (device_create_file(smd_pkt_devp->devicep,
+					&dev_attr_loopback_edge))
+			pr_err("%s: unable to create device attr for smd_pkt_dev id:%d\n",
+				__func__, i);
+	}
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_add(&smd_pkt_devp->dev_list, &smd_pkt_dev_list);
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+	return r;
+}
+
+static void smd_pkt_core_deinit(void)
+{
+	struct smd_pkt_dev *smd_pkt_devp;
+	struct smd_pkt_dev *index;
+
+	mutex_lock(&smd_pkt_dev_lock_lha1);
+	list_for_each_entry_safe(smd_pkt_devp, index, &smd_pkt_dev_list,
+							dev_list) {
+		cdev_del(&smd_pkt_devp->cdev);
+		list_del(&smd_pkt_devp->dev_list);
+		device_destroy(smd_pkt_classp,
+			       MKDEV(MAJOR(smd_pkt_number), smd_pkt_devp->i));
+		kfree(smd_pkt_devp);
+	}
+	mutex_unlock(&smd_pkt_dev_lock_lha1);
+
+	if (!IS_ERR_OR_NULL(smd_pkt_classp))
+		class_destroy(smd_pkt_classp);
+
+	unregister_chrdev_region(MAJOR(smd_pkt_number), num_smd_pkt_ports);
+}
+
+static int smd_pkt_alloc_chrdev_region(void)
+{
+	int r = alloc_chrdev_region(&smd_pkt_number,
+			       0,
+			       num_smd_pkt_ports,
+			       DEVICE_NAME);
+
+	if (r) {
+		pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
+			__func__, r);
+		return r;
+	}
+
+	smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
+	if (IS_ERR(smd_pkt_classp)) {
+		pr_err("%s: class_create() failed ENOMEM\n", __func__);
+		r = -ENOMEM;
+		unregister_chrdev_region(MAJOR(smd_pkt_number),
+						num_smd_pkt_ports);
+		return r;
+	}
+
+	return 0;
+}
+
+static int parse_smdpkt_devicetree(struct device_node *node,
+					struct smd_pkt_dev *smd_pkt_devp)
+{
+	int edge;
+	char *key;
+	const char *ch_name;
+	const char *dev_name;
+	const char *remote_ss;
+
+	key = "qcom,smdpkt-remote";
+	remote_ss = of_get_property(node, key, NULL);
+	if (!remote_ss)
+		goto error;
+
+	edge = smd_remote_ss_to_edge(remote_ss);
+	if (edge < 0)
+		goto error;
+
+	smd_pkt_devp->edge = edge;
+	D_STATUS("%s: %s = %d", __func__, key, edge);
+
+	key = "qcom,smdpkt-port-name";
+	ch_name = of_get_property(node, key, NULL);
+	if (!ch_name)
+		goto error;
+
+	strlcpy(smd_pkt_devp->ch_name, ch_name, SMD_MAX_CH_NAME_LEN);
+	D_STATUS("%s ch_name = %s\n", __func__, ch_name);
+
+	key = "qcom,smdpkt-dev-name";
+	dev_name = of_get_property(node, key, NULL);
+	if (!dev_name)
+		goto error;
+
+	strlcpy(smd_pkt_devp->dev_name, dev_name, SMD_MAX_CH_NAME_LEN);
+	D_STATUS("%s dev_name = %s\n", __func__, dev_name);
+
+	return 0;
+
+error:
+	pr_err("%s: missing key: %s\n", __func__, key);
+	return -ENODEV;
+
+}
+
+static int smd_pkt_devicetree_init(struct platform_device *pdev)
+{
+	int ret;
+	int i = 0;
+	struct device_node *node;
+	struct smd_pkt_dev *smd_pkt_devp;
+	int subnode_num = 0;
+
+	for_each_child_of_node(pdev->dev.of_node, node)
+		++subnode_num;
+
+	num_smd_pkt_ports = subnode_num;
+
+	ret = smd_pkt_alloc_chrdev_region();
+	if (ret) {
+		pr_err("%s: smd_pkt_alloc_chrdev_region() failed ret:%i\n",
+			__func__, ret);
+		return ret;
+	}
+
+	for_each_child_of_node(pdev->dev.of_node, node) {
+		smd_pkt_devp = kzalloc(sizeof(struct smd_pkt_dev), GFP_KERNEL);
+		if (IS_ERR_OR_NULL(smd_pkt_devp)) {
+			pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
+				__func__, i);
+			ret = -ENOMEM;
+			goto error_destroy;
+		}
+
+		ret = parse_smdpkt_devicetree(node, smd_pkt_devp);
+		if (ret) {
+			pr_err(" failed to parse_smdpkt_devicetree %d\n", i);
+			kfree(smd_pkt_devp);
+			goto error_destroy;
+		}
+
+		ret = smd_pkt_init_add_device(smd_pkt_devp, i);
+		if (ret < 0) {
+			pr_err("add device failed for idx:%d ret=%d\n", i, ret);
+			kfree(smd_pkt_devp);
+			goto error_destroy;
+		}
+		i++;
+	}
+
+	INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
+
+	D_STATUS("SMD Packet Port Driver Initialized.\n");
+	return 0;
+
+error_destroy:
+	smd_pkt_core_deinit();
+	return ret;
+}
+
+static int msm_smd_pkt_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	if (pdev) {
+		if (pdev->dev.of_node) {
+			D_STATUS("%s device tree implementation\n", __func__);
+			ret = smd_pkt_devicetree_init(pdev);
+			if (ret)
+				pr_err("%s: device tree init failed\n",
+					__func__);
+		}
+	}
+
+	return 0;
+}
+
+static const struct of_device_id msm_smd_pkt_match_table[] = {
+	{ .compatible = "qcom,smdpkt" },
+	{},
+};
+
+static struct platform_driver msm_smd_pkt_driver = {
+	.probe = msm_smd_pkt_probe,
+	.driver = {
+		.name = MODULE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smd_pkt_match_table,
+	 },
+};
+
+static int __init smd_pkt_init(void)
+{
+	int rc;
+
+	INIT_LIST_HEAD(&smd_pkt_dev_list);
+	INIT_LIST_HEAD(&smd_pkt_driver_list);
+	rc = platform_driver_register(&msm_smd_pkt_driver);
+	if (rc) {
+		pr_err("%s: msm_smd_driver register failed %d\n",
+			 __func__, rc);
+		return rc;
+	}
+
+	smd_pkt_ilctxt = ipc_log_context_create(SMD_PKT_IPC_LOG_PAGE_CNT,
+						"smd_pkt", 0);
+	return 0;
+}
+
+static void __exit smd_pkt_cleanup(void)
+{
+	smd_pkt_core_deinit();
+}
+
+module_init(smd_pkt_init);
+module_exit(smd_pkt_cleanup);
+
+MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 7e665ca..fb0b504 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -1188,11 +1188,11 @@
 
 	/* Check if per-core DCVS is enabled/not */
 	val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
-	if (val && BIT(0))
+	if (val & BIT(0))
 		pwrcl_clk.per_core_dcvs = true;
 
 	val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
-	if (val && BIT(0))
+	if (val & BIT(0))
 		perfcl_clk.per_core_dcvs = true;
 
 	rc = clk_osm_read_lut(pdev, &l3_clk);
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index 94a8e6a..433f768 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -1207,6 +1207,7 @@
 	void *tre = ch_ring->base +
 		(ch_ring->el_size * imed_event->tre_index);
 	struct msm_gpi_dma_async_tx_cb_param *tx_cb_param;
+	unsigned long flags;
 
 	/*
 	 * If channel not active don't process event but let
@@ -1221,13 +1222,13 @@
 		return;
 	}
 
-	spin_lock_irq(&gpii_chan->vc.lock);
+	spin_lock_irqsave(&gpii_chan->vc.lock, flags);
 	vd = vchan_next_desc(&gpii_chan->vc);
 	if (!vd) {
 		struct gpi_ere *gpi_ere;
 		struct msm_gpi_tre *gpi_tre;
 
-		spin_unlock_irq(&gpii_chan->vc.lock);
+		spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 		GPII_ERR(gpii, gpii_chan->chid,
 			 "event without a pending descriptor!\n");
 		gpi_ere = (struct gpi_ere *)imed_event;
@@ -1247,7 +1248,7 @@
 
 	/* Event TR RP gen. don't match descriptor TR */
 	if (gpi_desc->wp != tre) {
-		spin_unlock_irq(&gpii_chan->vc.lock);
+		spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 		GPII_ERR(gpii, gpii_chan->chid,
 			 "EOT/EOB received for wrong TRE 0x%0llx != 0x%0llx\n",
 			 to_physical(ch_ring, gpi_desc->wp),
@@ -1258,7 +1259,7 @@
 	}
 
 	list_del(&vd->node);
-	spin_unlock_irq(&gpii_chan->vc.lock);
+	spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 
 	sg_tre = gpi_desc->sg_tre;
 	client_tre = ((struct sg_tre *)sg_tre)->ptr;
@@ -1300,9 +1301,9 @@
 		tx_cb_param->status = imed_event->status;
 	}
 
-	spin_lock_irq(&gpii_chan->vc.lock);
+	spin_lock_irqsave(&gpii_chan->vc.lock, flags);
 	vchan_cookie_complete(vd);
-	spin_unlock_irq(&gpii_chan->vc.lock);
+	spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 }
 
 /* processing transfer completion events */
@@ -1318,6 +1319,7 @@
 	struct msm_gpi_dma_async_tx_cb_param *tx_cb_param;
 	struct gpi_desc *gpi_desc;
 	void *sg_tre = NULL;
+	unsigned long flags;
 
 	/* only process events on active channel */
 	if (unlikely(gpii_chan->pm_state != ACTIVE_STATE)) {
@@ -1329,12 +1331,12 @@
 		return;
 	}
 
-	spin_lock_irq(&gpii_chan->vc.lock);
+	spin_lock_irqsave(&gpii_chan->vc.lock, flags);
 	vd = vchan_next_desc(&gpii_chan->vc);
 	if (!vd) {
 		struct gpi_ere *gpi_ere;
 
-		spin_unlock_irq(&gpii_chan->vc.lock);
+		spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 		GPII_ERR(gpii, gpii_chan->chid,
 			 "Event without a pending descriptor!\n");
 		gpi_ere = (struct gpi_ere *)compl_event;
@@ -1350,7 +1352,7 @@
 
 	/* TRE Event generated didn't match descriptor's TRE */
 	if (gpi_desc->wp != ev_rp) {
-		spin_unlock_irq(&gpii_chan->vc.lock);
+		spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 		GPII_ERR(gpii, gpii_chan->chid,
 			 "EOT\EOB received for wrong TRE 0x%0llx != 0x%0llx\n",
 			 to_physical(ch_ring, gpi_desc->wp),
@@ -1361,7 +1363,7 @@
 	}
 
 	list_del(&vd->node);
-	spin_unlock_irq(&gpii_chan->vc.lock);
+	spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 
 	sg_tre = gpi_desc->sg_tre;
 	client_tre = ((struct sg_tre *)sg_tre)->ptr;
@@ -1393,9 +1395,9 @@
 		tx_cb_param->status = compl_event->status;
 	}
 
-	spin_lock_irq(&gpii_chan->vc.lock);
+	spin_lock_irqsave(&gpii_chan->vc.lock, flags);
 	vchan_cookie_complete(vd);
-	spin_unlock_irq(&gpii_chan->vc.lock);
+	spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
 }
 
 /* process all events */
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/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index b0a06e1..6391a22 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -265,6 +265,9 @@
 	struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
 	struct dsi_display_mode dsi_mode, cur_dsi_mode;
 	struct drm_display_mode cur_mode;
+	struct drm_crtc_state *crtc_state;
+
+	crtc_state = container_of(mode, struct drm_crtc_state, mode);
 
 	if (!bridge || !mode || !adjusted_mode) {
 		pr_err("Invalid params\n");
@@ -280,9 +283,10 @@
 		return false;
 	}
 
-	if (bridge->encoder && bridge->encoder->crtc) {
+	if (bridge->encoder && bridge->encoder->crtc &&
+			crtc_state->crtc) {
 
-		convert_to_dsi_mode(&bridge->encoder->crtc->state->mode,
+		convert_to_dsi_mode(&crtc_state->crtc->state->mode,
 							&cur_dsi_mode);
 		rc = dsi_display_validate_mode_vrr(c_bridge->display,
 					&cur_dsi_mode, &dsi_mode);
@@ -290,7 +294,7 @@
 			pr_debug("[%s] vrr mode mismatch failure rc=%d\n",
 				c_bridge->display->name, rc);
 
-		cur_mode = bridge->encoder->crtc->mode;
+		cur_mode = crtc_state->crtc->mode;
 
 		if (!drm_mode_equal(&cur_mode, adjusted_mode) &&
 			(!(dsi_mode.dsi_mode_flags &
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index f68f64d..a7073cb 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -741,7 +741,7 @@
 	for (i = 0; i < sde_crtc->num_mixers; i++) {
 		split_dim_layer.flags = dim_layer->flags;
 
-		sde_kms_rect_intersect(&cstate->lm_bounds[i], &dim_layer->rect,
+		sde_kms_rect_intersect(&cstate->lm_roi[i], &dim_layer->rect,
 					&split_dim_layer.rect);
 		if (sde_kms_rect_is_null(&split_dim_layer.rect)) {
 			/*
@@ -764,9 +764,26 @@
 		} else {
 			split_dim_layer.rect.x =
 					split_dim_layer.rect.x -
-						cstate->lm_bounds[i].x;
+						cstate->lm_roi[i].x;
+			split_dim_layer.rect.y =
+					split_dim_layer.rect.y -
+						cstate->lm_roi[i].y;
 		}
 
+		SDE_EVT32_VERBOSE(DRMID(crtc),
+				cstate->lm_roi[i].x,
+				cstate->lm_roi[i].y,
+				cstate->lm_roi[i].w,
+				cstate->lm_roi[i].h,
+				dim_layer->rect.x,
+				dim_layer->rect.y,
+				dim_layer->rect.w,
+				dim_layer->rect.h,
+				split_dim_layer.rect.x,
+				split_dim_layer.rect.y,
+				split_dim_layer.rect.w,
+				split_dim_layer.rect.h);
+
 		SDE_DEBUG("split_dim_layer - LM:%d, rect:{%d,%d,%d,%d}}\n",
 			i, split_dim_layer.rect.x, split_dim_layer.rect.y,
 			split_dim_layer.rect.w, split_dim_layer.rect.h);
@@ -3957,12 +3974,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.c b/drivers/gpu/msm/adreno.c
index 46256b4..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 */
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_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/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/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_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index e23ed7a..8421873 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -761,6 +761,15 @@
  */
 int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable);
 
+/* sde_rotator_resource_ctrl_enabled - check if resource control is enabled
+ * @mgr: Pointer to rotator manager
+ * Return: true if enabled; false otherwise
+ */
+static inline int sde_rotator_resource_ctrl_enabled(struct sde_rot_mgr *mgr)
+{
+	return mgr->regulator_enable;
+}
+
 /*
  * sde_rotator_cancel_all_requests - cancel all outstanding requests
  * @mgr: Pointer to rotator manager
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index b9158e1..fb74dab 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -1208,18 +1208,29 @@
 	mutex_lock(&dbg->buflock);
 
 	/* Enable Clock for register access */
+	sde_rot_mgr_lock(dbg->mgr);
+	if (!sde_rotator_resource_ctrl_enabled(dbg->mgr)) {
+		SDEROT_WARN("resource ctrl is not enabled\n");
+		sde_rot_mgr_unlock(dbg->mgr);
+		goto debug_write_error;
+	}
 	sde_rotator_clk_ctrl(dbg->mgr, true);
 
 	writel_relaxed(data, dbg->base + off);
 
 	/* Disable Clock after register access */
 	sde_rotator_clk_ctrl(dbg->mgr, false);
+	sde_rot_mgr_unlock(dbg->mgr);
 
 	mutex_unlock(&dbg->buflock);
 
 	SDEROT_DBG("addr=%zx data=%x\n", off, data);
 
 	return count;
+
+debug_write_error:
+	mutex_unlock(&dbg->buflock);
+	return 0;
 }
 
 static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
@@ -1257,6 +1268,12 @@
 		tot = 0;
 
 		/* Enable clock for register access */
+		sde_rot_mgr_lock(dbg->mgr);
+		if (!sde_rotator_resource_ctrl_enabled(dbg->mgr)) {
+			SDEROT_WARN("resource ctrl is not enabled\n");
+			sde_rot_mgr_unlock(dbg->mgr);
+			goto debug_read_error;
+		}
 		sde_rotator_clk_ctrl(dbg->mgr, true);
 
 		for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
@@ -1276,6 +1293,7 @@
 		}
 		/* Disable clock after register access */
 		sde_rotator_clk_ctrl(dbg->mgr, false);
+		sde_rot_mgr_unlock(dbg->mgr);
 
 		dbg->buf_len = tot;
 	}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 523ff5b..dd0c04d 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1201,6 +1201,8 @@
 	list_add_tail(&request->list, &ctx->retired_list);
 	spin_unlock(&ctx->list_lock);
 
+	wake_up(&ctx->wait_queue);
+
 	SDEROT_DBG("retire request s:%d.%d\n",
 				ctx->session_id, ctx->retired_sequence_id);
 }
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 2d8fdda..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,
 	},
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 7ca2fd6..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:
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/core/core.c b/drivers/mmc/core/core.c
index b26971f..c172be9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -3472,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/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_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index c760f75..f755b3f 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -450,7 +450,7 @@
 {
 	struct ipa_context *ctx = NULL;
 
-	IPADBG("ENTER\n");
+	IPADBG_LOW("ENTER\n");
 	ctx = container_of(inode->i_cdev, struct ipa_context, cdev);
 	filp->private_data = ctx;
 
@@ -3051,11 +3051,11 @@
 
 void _ipa_enable_clks_v2_0(void)
 {
-	IPADBG("enabling gcc_ipa_clk\n");
+	IPADBG_LOW("enabling gcc_ipa_clk\n");
 	if (ipa_clk) {
 		clk_prepare(ipa_clk);
 		clk_enable(ipa_clk);
-		IPADBG("curr_ipa_clk_rate=%d", ipa_ctx->curr_ipa_clk_rate);
+		IPADBG_LOW("curr_ipa_clk_rate=%d", ipa_ctx->curr_ipa_clk_rate);
 		clk_set_rate(ipa_clk, ipa_ctx->curr_ipa_clk_rate);
 		ipa_uc_notify_clk_state(true);
 	} else {
@@ -3187,7 +3187,7 @@
 
 void _ipa_disable_clks_v2_0(void)
 {
-	IPADBG("disabling gcc_ipa_clk\n");
+	IPADBG_LOW("disabling gcc_ipa_clk\n");
 	ipa_suspend_apps_pipes(true);
 	ipa_sps_irq_control_all(false);
 	ipa_uc_notify_clk_state(false);
@@ -3208,7 +3208,7 @@
 */
 void ipa_disable_clks(void)
 {
-	IPADBG("disabling IPA clocks and bus voting\n");
+	IPADBG_LOW("disabling IPA clocks and bus voting\n");
 
 	ipa_ctx->ctrl->ipa_disable_clks();
 
@@ -3352,7 +3352,7 @@
 	ipa_ctx->ipa_active_clients.cnt++;
 	if (ipa_ctx->ipa_active_clients.cnt == 1)
 		ipa_enable_clks();
-	IPADBG("active clients = %d\n", ipa_ctx->ipa_active_clients.cnt);
+	IPADBG_LOW("active clients = %d\n", ipa_ctx->ipa_active_clients.cnt);
 	ipa_active_clients_unlock();
 }
 
@@ -3384,7 +3384,7 @@
 	ipa2_active_clients_log_inc(id, true);
 
 	ipa_ctx->ipa_active_clients.cnt++;
-	IPADBG("active clients = %d\n", ipa_ctx->ipa_active_clients.cnt);
+	IPADBG_LOW("active clients = %d\n", ipa_ctx->ipa_active_clients.cnt);
 bail:
 	ipa_active_clients_trylock_unlock(&flags);
 
@@ -3412,7 +3412,7 @@
 	ipa_active_clients_lock();
 	ipa2_active_clients_log_dec(id, false);
 	ipa_ctx->ipa_active_clients.cnt--;
-	IPADBG("active clients = %d\n", ipa_ctx->ipa_active_clients.cnt);
+	IPADBG_LOW("active clients = %d\n", ipa_ctx->ipa_active_clients.cnt);
 	if (ipa_ctx->ipa_active_clients.cnt == 0) {
 		if (ipa_ctx->tag_process_before_gating) {
 			IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info,
@@ -3452,7 +3452,7 @@
 	ipa_ctx->wakelock_ref_cnt.cnt |= (1 << ref_client);
 	if (ipa_ctx->wakelock_ref_cnt.cnt)
 		__pm_stay_awake(&ipa_ctx->w_lock);
-	IPADBG("active wakelock ref cnt = %d client enum %d\n",
+	IPADBG_LOW("active wakelock ref cnt = %d client enum %d\n",
 		ipa_ctx->wakelock_ref_cnt.cnt, ref_client);
 	spin_unlock_irqrestore(&ipa_ctx->wakelock_ref_cnt.spinlock, flags);
 }
@@ -3473,7 +3473,7 @@
 		return;
 	spin_lock_irqsave(&ipa_ctx->wakelock_ref_cnt.spinlock, flags);
 	ipa_ctx->wakelock_ref_cnt.cnt &= ~(1 << ref_client);
-	IPADBG("active wakelock ref cnt = %d client enum %d\n",
+	IPADBG_LOW("active wakelock ref cnt = %d client enum %d\n",
 		ipa_ctx->wakelock_ref_cnt.cnt, ref_client);
 	if (ipa_ctx->wakelock_ref_cnt.cnt == 0)
 		__pm_relax(&ipa_ctx->w_lock);
@@ -3517,7 +3517,7 @@
 	enum ipa_voltage_level needed_voltage;
 	u32 clk_rate;
 
-	IPADBG("floor_voltage=%d, bandwidth_mbps=%u",
+	IPADBG_LOW("floor_voltage=%d, bandwidth_mbps=%u",
 					floor_voltage, bandwidth_mbps);
 
 	if (floor_voltage < IPA_VOLTAGE_UNSPECIFIED ||
@@ -3527,7 +3527,7 @@
 	}
 
 	if (ipa_ctx->enable_clock_scaling) {
-		IPADBG("Clock scaling is enabled\n");
+		IPADBG_LOW("Clock scaling is enabled\n");
 		if (bandwidth_mbps >=
 			ipa_ctx->ctrl->clock_scaling_bw_threshold_turbo)
 			needed_voltage = IPA_VOLTAGE_TURBO;
@@ -3537,7 +3537,7 @@
 		else
 			needed_voltage = IPA_VOLTAGE_SVS;
 	} else {
-		IPADBG("Clock scaling is disabled\n");
+		IPADBG_LOW("Clock scaling is disabled\n");
 		needed_voltage = IPA_VOLTAGE_NOMINAL;
 	}
 
@@ -3559,13 +3559,13 @@
 	}
 
 	if (clk_rate == ipa_ctx->curr_ipa_clk_rate) {
-		IPADBG("Same voltage\n");
+		IPADBG_LOW("Same voltage\n");
 		return 0;
 	}
 
 	ipa_active_clients_lock();
 	ipa_ctx->curr_ipa_clk_rate = clk_rate;
-	IPADBG("setting clock rate to %u\n", ipa_ctx->curr_ipa_clk_rate);
+	IPADBG_LOW("setting clock rate to %u\n", ipa_ctx->curr_ipa_clk_rate);
 	if (ipa_ctx->ipa_active_clients.cnt > 0) {
 		struct ipa_active_client_logging_info log_info;
 
@@ -3588,11 +3588,10 @@
 		/* remove the vote added here */
 		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
 	} else {
-		IPADBG("clocks are gated, not setting rate\n");
-		 ipa_active_clients_unlock();
+		IPADBG_LOW("clocks are gated, not setting rate\n");
 	}
-	IPADBG("Done\n");
-
+	ipa_active_clients_unlock();
+	IPADBG_LOW("Done\n");
 	return 0;
 }
 
@@ -3888,6 +3887,13 @@
 		goto fail_mem_ctx;
 	}
 
+	ipa_ctx->logbuf = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa", 0);
+	if (ipa_ctx->logbuf == NULL) {
+		IPAERR("failed to get logbuf\n");
+		result = -ENOMEM;
+		goto fail_logbuf;
+	}
+
 	ipa_ctx->pdev = ipa_dev;
 	ipa_ctx->uc_pdev = ipa_dev;
 	ipa_ctx->smmu_present = smmu_info.present;
@@ -4423,6 +4429,8 @@
 fail_bind:
 	kfree(ipa_ctx->ctrl);
 fail_mem_ctrl:
+	ipc_log_context_destroy(ipa_ctx->logbuf);
+fail_logbuf:
 	kfree(ipa_ctx);
 	ipa_ctx = NULL;
 fail_mem_ctx:
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index a249567..c018fc9 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -1817,6 +1817,44 @@
 	return count;
 }
 
+static ssize_t ipa_enable_ipc_low(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	s8 option = 0;
+
+	if (sizeof(dbg_buff) < count + 1)
+		return -EFAULT;
+
+	missing = copy_from_user(dbg_buff, ubuf, count);
+	if (missing)
+		return -EFAULT;
+
+	dbg_buff[count] = '\0';
+	if (kstrtos8(dbg_buff, 0, &option))
+		return -EFAULT;
+
+	if (option) {
+		if (!ipa_ctx->logbuf_low) {
+			ipa_ctx->logbuf_low =
+				ipc_log_context_create(IPA_IPC_LOG_PAGES,
+				"ipa_low", 0);
+		}
+
+		if (ipa_ctx->logbuf_low == NULL) {
+			IPAERR("failed to get logbuf_low\n");
+			return -EFAULT;
+		}
+
+	} else {
+		if (ipa_ctx->logbuf_low)
+			ipc_log_context_destroy(ipa_ctx->logbuf_low);
+			ipa_ctx->logbuf_low = NULL;
+	}
+
+	return count;
+}
+
 const struct file_operations ipa_gen_reg_ops = {
 	.read = ipa_read_gen_reg,
 };
@@ -1895,6 +1933,10 @@
 	.write = ipa2_clear_active_clients_log,
 };
 
+const struct file_operations ipa_ipc_low_ops = {
+	.write = ipa_enable_ipc_low,
+};
+
 const struct file_operations ipa_rx_poll_time_ops = {
 	.read = ipa_read_rx_polling_timeout,
 	.write = ipa_write_rx_polling_timeout,
@@ -2110,6 +2152,13 @@
 		goto fail;
 	}
 
+	file = debugfs_create_file("enable_low_prio_print", write_only_mode,
+		dent, 0, &ipa_ipc_low_ops);
+	if (!file) {
+		IPAERR("could not create enable_low_prio_print file\n");
+		goto fail;
+	}
+
 	return;
 
 fail:
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dma.c
index bee6331..6a3d870 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dma.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dma.c
@@ -32,16 +32,39 @@
 #define IPADMA_DRV_NAME "ipa_dma"
 
 #define IPADMA_DBG(fmt, args...) \
-	pr_debug(IPADMA_DRV_NAME " %s:%d " fmt, \
-		 __func__, __LINE__, ## args)
+	do { \
+		pr_debug(IPADMA_DRV_NAME " %s:%d " fmt, \
+		__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+#define IPADMA_DBG_LOW(fmt, args...) \
+	do { \
+		pr_debug(IPADMA_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
 #define IPADMA_ERR(fmt, args...) \
-	pr_err(IPADMA_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+	do { \
+		pr_err(IPADMA_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPADMA_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
 
 #define IPADMA_FUNC_ENTRY() \
-	IPADMA_DBG("ENTRY\n")
+	IPADMA_DBG_LOW("ENTRY\n")
 
 #define IPADMA_FUNC_EXIT() \
-	IPADMA_DBG("EXIT\n")
+	IPADMA_DBG_LOW("EXIT\n")
+
 
 #ifdef CONFIG_DEBUG_FS
 #define IPADMA_MAX_MSG_LEN 1024
@@ -270,7 +293,7 @@
 	}
 	mutex_lock(&ipa_dma_ctx->enable_lock);
 	if (ipa_dma_ctx->is_enabled) {
-		IPADMA_DBG("Already enabled.\n");
+		IPADMA_ERR("Already enabled.\n");
 		mutex_unlock(&ipa_dma_ctx->enable_lock);
 		return -EPERM;
 	}
@@ -296,7 +319,7 @@
 		IPADMA_DBG("pending uc\n");
 		return true;
 	}
-	IPADMA_DBG("no pending work\n");
+	IPADMA_DBG_LOW("no pending work\n");
 	return false;
 }
 
@@ -324,7 +347,7 @@
 	mutex_lock(&ipa_dma_ctx->enable_lock);
 	spin_lock_irqsave(&ipa_dma_ctx->pending_lock, flags);
 	if (!ipa_dma_ctx->is_enabled) {
-		IPADMA_DBG("Already disabled.\n");
+		IPADMA_ERR("Already disabled.\n");
 		spin_unlock_irqrestore(&ipa_dma_ctx->pending_lock, flags);
 		mutex_unlock(&ipa_dma_ctx->enable_lock);
 		return -EPERM;
@@ -371,6 +394,8 @@
 
 	IPADMA_FUNC_ENTRY();
 
+	IPADMA_DBG_LOW("dest =  0x%llx, src = 0x%llx, len = %d\n",
+		dest, src, len);
 	if (ipa_dma_ctx == NULL) {
 		IPADMA_ERR("IPADMA isn't initialized, can't memcpy\n");
 		return -EPERM;
@@ -398,7 +423,7 @@
 	if (atomic_read(&ipa_dma_ctx->sync_memcpy_pending_cnt) >=
 		IPA_DMA_MAX_PENDING_SYNC) {
 		atomic_dec(&ipa_dma_ctx->sync_memcpy_pending_cnt);
-		IPADMA_DBG("Reached pending requests limit\n");
+		IPADMA_ERR("Reached pending requests limit\n");
 		return -EFAULT;
 	}
 
@@ -531,6 +556,8 @@
 	unsigned long flags;
 
 	IPADMA_FUNC_ENTRY();
+	IPADMA_DBG_LOW("dest =  0x%llx, src = 0x%llx, len = %d\n",
+		dest, src, len);
 	if (ipa_dma_ctx == NULL) {
 		IPADMA_ERR("IPADMA isn't initialized, can't memcpy\n");
 		return -EPERM;
@@ -562,7 +589,7 @@
 	if (atomic_read(&ipa_dma_ctx->async_memcpy_pending_cnt) >=
 		IPA_DMA_MAX_PENDING_ASYNC) {
 		atomic_dec(&ipa_dma_ctx->async_memcpy_pending_cnt);
-		IPADMA_DBG("Reached pending requests limit\n");
+		IPADMA_ERR("Reached pending requests limit\n");
 		return -EFAULT;
 	}
 
@@ -692,7 +719,7 @@
 
 	IPADMA_FUNC_ENTRY();
 	if (!ipa_dma_ctx) {
-		IPADMA_DBG("IPADMA isn't initialized\n");
+		IPADMA_ERR("IPADMA isn't initialized\n");
 		return;
 	}
 
@@ -836,7 +863,7 @@
 	switch (in_num) {
 	case 0:
 		if (ipa_dma_work_pending())
-			IPADMA_DBG("Note, there are pending memcpy\n");
+			IPADMA_ERR("Note, there are pending memcpy\n");
 
 		atomic_set(&ipa_dma_ctx->total_async_memcpy, 0);
 		atomic_set(&ipa_dma_ctx->total_sync_memcpy, 0);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 80b97e7..3cb86d0 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -346,7 +346,7 @@
 	if (desc->type == IPA_IMM_CMD_DESC) {
 		sps_flags |= SPS_IOVEC_FLAG_IMME;
 		len = desc->opcode;
-		IPADBG("sending cmd=%d pyld_len=%d sps_flags=%x\n",
+		IPADBG_LOW("sending cmd=%d pyld_len=%d sps_flags=%x\n",
 				desc->opcode, desc->len, sps_flags);
 		IPA_DUMP_BUFF(desc->pyld, dma_address, desc->len);
 	} else {
@@ -627,7 +627,7 @@
 		WARN_ON(1);
 		return;
 	}
-	IPADBG("got ack for cmd=%d\n", desc->opcode);
+	IPADBG_LOW("got ack for cmd=%d\n", desc->opcode);
 	complete(&desc->xfer_done);
 }
 
@@ -644,11 +644,12 @@
 int ipa_send_cmd(u16 num_desc, struct ipa_desc *descr)
 {
 	struct ipa_desc *desc;
-	int result = 0;
+	int i, result = 0;
 	struct ipa_sys_context *sys;
 	int ep_idx;
 
-	IPADBG("sending command\n");
+	for (i = 0; i < num_desc; i++)
+		IPADBG_LOW("sending imm cmd %d\n", descr[i].opcode);
 
 	ep_idx = ipa2_get_ep_mapping(IPA_CLIENT_APPS_CMD_PROD);
 	if (-1 == ep_idx) {
@@ -709,7 +710,7 @@
 	struct ipa_sys_context *sys = (struct ipa_sys_context *)notify->user;
 	int ret;
 
-	IPADBG("event %d notified\n", notify->event_id);
+	IPADBG_LOW("event %d notified\n", notify->event_id);
 
 	switch (notify->event_id) {
 	case SPS_EVENT_EOT:
@@ -752,7 +753,7 @@
 {
 	struct ipa_tx_pkt_wrapper *tx_pkt;
 
-	IPADBG("event %d notified\n", notify->event_id);
+	IPADBG_LOW("event %d notified\n", notify->event_id);
 
 	switch (notify->event_id) {
 	case SPS_EVENT_EOT:
@@ -1599,7 +1600,7 @@
 	struct sk_buff *skb = (struct sk_buff *)user1;
 	int ep_idx = user2;
 
-	IPADBG("skb=%p ep=%d\n", skb, ep_idx);
+	IPADBG_LOW("skb=%p ep=%d\n", skb, ep_idx);
 
 	IPA_STATS_INC_CNT(ipa_ctx->stats.tx_pkts_compl);
 
@@ -1920,7 +1921,7 @@
 	int ret;
 	u32 rx_len_cached = 0;
 
-	IPADBG("\n");
+	IPADBG_LOW("\n");
 
 	spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
 	rx_len_cached = sys->len;
@@ -2350,7 +2351,7 @@
 	}
 
 	if (sys->len_partial) {
-		IPADBG("len_partial %d\n", sys->len_partial);
+		IPADBG_LOW("len_partial %d\n", sys->len_partial);
 		buf = skb_push(skb, sys->len_partial);
 		memcpy(buf, sys->prev_skb->data, sys->len_partial);
 		sys->len_partial = 0;
@@ -2363,7 +2364,7 @@
 	 * (status+data)
 	 */
 	if (sys->len_rem) {
-		IPADBG("rem %d skb %d pad %d\n", sys->len_rem, skb->len,
+		IPADBG_LOW("rem %d skb %d pad %d\n", sys->len_rem, skb->len,
 				sys->len_pad);
 		if (sys->len_rem <= skb->len) {
 			if (sys->prev_skb) {
@@ -2414,7 +2415,7 @@
 begin:
 	while (skb->len) {
 		sys->drop_packet = false;
-		IPADBG("LEN_REM %d\n", skb->len);
+		IPADBG_LOW("LEN_REM %d\n", skb->len);
 
 		if (skb->len < IPA_PKT_STATUS_SIZE) {
 			WARN_ON(sys->prev_skb != NULL);
@@ -2425,7 +2426,7 @@
 		}
 
 		status = (struct ipa_hw_pkt_status *)skb->data;
-		IPADBG("STATUS opcode=%d src=%d dst=%d len=%d\n",
+		IPADBG_LOW("STATUS opcode=%d src=%d dst=%d len=%d\n",
 				status->status_opcode, status->endp_src_idx,
 				status->endp_dest_idx, status->pkt_len);
 		if (sys->status_stat) {
@@ -2463,7 +2464,7 @@
 		if (status->status_mask & IPA_HW_PKT_STATUS_MASK_TAG_VALID) {
 			struct ipa_tag_completion *comp;
 
-			IPADBG("TAG packet arrived\n");
+			IPADBG_LOW("TAG packet arrived\n");
 			if (status->tag_f_2 == IPA_COOKIE) {
 				skb_pull(skb, IPA_PKT_STATUS_SIZE);
 				if (skb->len < sizeof(comp)) {
@@ -2503,7 +2504,7 @@
 			if (skb->len == IPA_PKT_STATUS_SIZE &&
 					!status->exception) {
 				WARN_ON(sys->prev_skb != NULL);
-				IPADBG("Ins header in next buffer\n");
+				IPADBG_LOW("Ins header in next buffer\n");
 				sys->prev_skb = skb_copy(skb, GFP_KERNEL);
 				sys->len_partial =	 skb->len;
 				return rc;
@@ -2514,12 +2515,13 @@
 
 			len = status->pkt_len + pad_len_byte +
 				IPA_SIZE_DL_CSUM_META_TRAILER;
-			IPADBG("pad %d pkt_len %d len %d\n", pad_len_byte,
+			IPADBG_LOW("pad %d pkt_len %d len %d\n", pad_len_byte,
 					status->pkt_len, len);
 
 			if (status->exception ==
 					IPA_HW_PKT_STATUS_EXCEPTION_DEAGGR) {
-				IPADBG("Dropping packet on DeAggr Exception\n");
+				IPADBG_LOW("Dropping packet");
+				IPADBG_LOW(" on DeAggr Exception\n");
 				sys->drop_packet = true;
 			}
 
@@ -2528,7 +2530,7 @@
 			skb2 = ipa_skb_copy_for_client(skb, skb2_len);
 			if (likely(skb2)) {
 				if (skb->len < len + IPA_PKT_STATUS_SIZE) {
-					IPADBG("SPL skb len %d len %d\n",
+					IPADBG_LOW("SPL skb len %d len %d\n",
 							skb->len, len);
 					sys->prev_skb = skb2;
 					sys->len_rem = len - skb->len +
@@ -2538,7 +2540,7 @@
 				} else {
 					skb_trim(skb2, status->pkt_len +
 							IPA_PKT_STATUS_SIZE);
-					IPADBG("rx avail for %d\n",
+					IPADBG_LOW("rx avail for %d\n",
 							status->endp_dest_idx);
 					if (sys->drop_packet) {
 						dev_kfree_skb_any(skb2);
@@ -2582,11 +2584,12 @@
 			}
 			/* TX comp */
 			ipa_wq_write_done_status(src_pipe);
-			IPADBG("tx comp imp for %d\n", src_pipe);
+			IPADBG_LOW("tx comp imp for %d\n", src_pipe);
 		} else {
 			/* TX comp */
 			ipa_wq_write_done_status(status->endp_src_idx);
-			IPADBG("tx comp exp for %d\n", status->endp_src_idx);
+			IPADBG_LOW
+				("tx comp exp for %d\n", status->endp_src_idx);
 			skb_pull(skb, IPA_PKT_STATUS_SIZE);
 			IPA_STATS_INC_CNT(ipa_ctx->stats.stat_compl);
 			IPA_STATS_DEC_CNT(
@@ -2622,13 +2625,13 @@
 {
 	struct sk_buff *skb2;
 
-	IPADBG("rem %d skb %d\n", sys->len_rem, skb->len);
+	IPADBG_LOW("rem %d skb %d\n", sys->len_rem, skb->len);
 	if (sys->len_rem <= skb->len) {
 		if (sys->prev_skb) {
 			skb2 = join_prev_skb(sys->prev_skb, skb,
 					sys->len_rem);
 			if (likely(skb2)) {
-				IPADBG(
+				IPADBG_LOW(
 					"removing Status element from skb and sending to WAN client");
 				skb_pull(skb2, IPA_PKT_STATUS_SIZE);
 				skb2->truesize = skb2->len +
@@ -2691,14 +2694,14 @@
 
 
 	while (skb->len) {
-		IPADBG("LEN_REM %d\n", skb->len);
+		IPADBG_LOW("LEN_REM %d\n", skb->len);
 		if (skb->len < IPA_PKT_STATUS_SIZE) {
 			IPAERR("status straddles buffer\n");
 			WARN_ON(1);
 			goto bail;
 		}
 		status = (struct ipa_hw_pkt_status *)skb->data;
-		IPADBG("STATUS opcode=%d src=%d dst=%d len=%d\n",
+		IPADBG_LOW("STATUS opcode=%d src=%d dst=%d len=%d\n",
 				status->status_opcode, status->endp_src_idx,
 				status->endp_dest_idx, status->pkt_len);
 
@@ -2729,7 +2732,7 @@
 			goto bail;
 		}
 		if (status->pkt_len == 0) {
-			IPADBG("Skip aggr close status\n");
+			IPADBG_LOW("Skip aggr close status\n");
 			skb_pull(skb, IPA_PKT_STATUS_SIZE);
 			IPA_STATS_DEC_CNT(ipa_ctx->stats.rx_pkts);
 			IPA_STATS_INC_CNT(ipa_ctx->stats.wan_aggr_close);
@@ -2756,11 +2759,11 @@
 
 		/*QMAP is BE: convert the pkt_len field from BE to LE*/
 		pkt_len_with_pad = ntohs((qmap_hdr>>16) & 0xffff);
-		IPADBG("pkt_len with pad %d\n", pkt_len_with_pad);
+		IPADBG_LOW("pkt_len with pad %d\n", pkt_len_with_pad);
 		/*get the CHECKSUM_PROCESS bit*/
 		checksum_trailer_exists = status->status_mask &
 				IPA_HW_PKT_STATUS_MASK_CKSUM_PROCESS;
-		IPADBG("checksum_trailer_exists %d\n",
+		IPADBG_LOW("checksum_trailer_exists %d\n",
 				checksum_trailer_exists);
 
 		frame_len = IPA_PKT_STATUS_SIZE +
@@ -2768,7 +2771,7 @@
 			    pkt_len_with_pad;
 		if (checksum_trailer_exists)
 			frame_len += IPA_DL_CHECKSUM_LENGTH;
-		IPADBG("frame_len %d\n", frame_len);
+		IPADBG_LOW("frame_len %d\n", frame_len);
 
 		skb2 = skb_clone(skb, GFP_KERNEL);
 		if (likely(skb2)) {
@@ -2777,16 +2780,16 @@
 			 * payload split across 2 buff
 			 */
 			if (skb->len < frame_len) {
-				IPADBG("SPL skb len %d len %d\n",
+				IPADBG_LOW("SPL skb len %d len %d\n",
 						skb->len, frame_len);
 				sys->prev_skb = skb2;
 				sys->len_rem = frame_len - skb->len;
 				skb_pull(skb, skb->len);
 			} else {
 				skb_trim(skb2, frame_len);
-				IPADBG("rx avail for %d\n",
+				IPADBG_LOW("rx avail for %d\n",
 						status->endp_dest_idx);
-				IPADBG(
+				IPADBG_LOW(
 					"removing Status element from skb and sending to WAN client");
 				skb_pull(skb2, IPA_PKT_STATUS_SIZE);
 				skb2->truesize = skb2->len +
@@ -2927,7 +2930,7 @@
 	 *  ------------------------------------------
 	 */
 	*(u16 *)rx_skb->cb = ((metadata >> 16) & 0xFFFF);
-	IPADBG("meta_data: 0x%x cb: 0x%x\n",
+	IPADBG_LOW("meta_data: 0x%x cb: 0x%x\n",
 			metadata, *(u32 *)rx_skb->cb);
 
 	ep->client_notify(ep->priv, IPA_RECEIVE, (unsigned long)(rx_skb));
@@ -3030,7 +3033,7 @@
 static void ipa_dma_memcpy_notify(struct ipa_sys_context *sys,
 	struct sps_iovec *iovec)
 {
-	IPADBG("ENTER.\n");
+	IPADBG_LOW("ENTER.\n");
 	if (unlikely(list_empty(&sys->head_desc_list))) {
 		IPAERR("descriptor list is empty!\n");
 		WARN_ON(1);
@@ -3077,7 +3080,8 @@
 		if (IPA_CLIENT_IS_APPS_CONS(rx_pkt->sys->ep->client))
 			atomic_set(&ipa_ctx->sps_pm.eot_activity, 1);
 		rx_pkt->len = notify->data.transfer.iovec.size;
-		IPADBG("event %d notified sys=%p len=%u\n", notify->event_id,
+		IPADBG_LOW
+			("event %d notified sys=%p len=%u\n", notify->event_id,
 				notify->user, rx_pkt->len);
 		queue_work(rx_pkt->sys->wq, &rx_pkt->work);
 		break;
@@ -3383,15 +3387,15 @@
 	struct ipa_tx_data_desc *dd = (struct ipa_tx_data_desc *)user1;
 	int ep_idx = user2;
 
-	IPADBG("Received data desc anchor:%p\n", dd);
+	IPADBG_LOW("Received data desc anchor:%p\n", dd);
 
 	atomic_inc(&ipa_ctx->ep[ep_idx].avail_fifo_desc);
 	ipa_ctx->ep[ep_idx].wstats.rx_pkts_status_rcvd++;
 
   /* wlan host driver waits till tx complete before unload */
-	IPADBG("ep=%d fifo_desc_free_count=%d\n",
+	IPADBG_LOW("ep=%d fifo_desc_free_count=%d\n",
 		ep_idx, atomic_read(&ipa_ctx->ep[ep_idx].avail_fifo_desc));
-	IPADBG("calling client notify callback with priv:%p\n",
+	IPADBG_LOW("calling client notify callback with priv:%p\n",
 		ipa_ctx->ep[ep_idx].priv);
 
 	if (ipa_ctx->ep[ep_idx].client_notify) {
@@ -3455,7 +3459,7 @@
 		return -EINVAL;
 	}
 
-	IPADBG("Received data desc anchor:%p\n", data_desc);
+	IPADBG_LOW("Received data desc anchor:%p\n", data_desc);
 
 	spin_lock_bh(&ipa_ctx->wc_memb.ipa_tx_mul_spinlock);
 
@@ -3464,7 +3468,7 @@
 		IPAERR("dest EP does not exist.\n");
 		goto fail_send;
 	}
-	IPADBG("ep idx:%d\n", ep_idx);
+	IPADBG_LOW("ep idx:%d\n", ep_idx);
 	sys = ipa_ctx->ep[ep_idx].sys;
 
 	if (unlikely(ipa_ctx->ep[ep_idx].valid == 0)) {
@@ -3478,7 +3482,7 @@
 	list_for_each_entry(entry, &data_desc->link, link) {
 		num_desc++;
 	}
-	IPADBG("Number of Data Descriptors:%d", num_desc);
+	IPADBG_LOW("Number of Data Descriptors:%d", num_desc);
 
 	if (atomic_read(&sys->ep->avail_fifo_desc) < num_desc) {
 		IPAERR("Insufficient data descriptors available\n");
@@ -3488,7 +3492,7 @@
 	/* Assign callback only for last data descriptor */
 	cnt = 0;
 	list_for_each_entry(entry, &data_desc->link, link) {
-		IPADBG("Parsing data desc :%d\n", cnt);
+		IPADBG_LOW("Parsing data desc :%d\n", cnt);
 		cnt++;
 		((u8 *)entry->pyld_buffer)[IPA_WLAN_HDR_QMAP_ID_OFFSET] =
 			(u8)sys->ep->cfg.meta.qmap_id;
@@ -3497,18 +3501,18 @@
 		desc.type = IPA_DATA_DESC_SKB;
 		desc.user1 = data_desc;
 		desc.user2 = ep_idx;
-		IPADBG("priv:%p pyld_buf:0x%p pyld_len:%d\n",
+		IPADBG_LOW("priv:%p pyld_buf:0x%p pyld_len:%d\n",
 			entry->priv, desc.pyld, desc.len);
 
 		/* In case of last descriptor populate callback */
 		if (cnt == num_desc) {
-			IPADBG("data desc:%p\n", data_desc);
+			IPADBG_LOW("data desc:%p\n", data_desc);
 			desc.callback = ipa_tx_client_rx_notify_release;
 		} else {
 			desc.callback = ipa_tx_client_rx_pkt_status;
 		}
 
-		IPADBG("calling ipa_send_one()\n");
+		IPADBG_LOW("calling ipa_send_one()\n");
 		if (ipa_send_one(sys, &desc, true)) {
 			IPAERR("fail to send skb\n");
 			sys->ep->wstats.rx_pkt_leak += (cnt-1);
@@ -3520,7 +3524,7 @@
 			atomic_dec(&sys->ep->avail_fifo_desc);
 
 		sys->ep->wstats.rx_pkts_rcvd++;
-		IPADBG("ep=%d fifo desc=%d\n",
+		IPADBG_LOW("ep=%d fifo desc=%d\n",
 			ep_idx, atomic_read(&sys->ep->avail_fifo_desc));
 	}
 
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index 0a079f4..bc54023 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
@@ -209,7 +209,7 @@
 		}
 	}
 
-	IPADBG("en_rule 0x%x, action=%d, rt_idx=%d, uc=%d, retain_hdr=%d\n",
+	IPADBG_LOW("en_rule 0x%x, action=%d, rt_idx=%d, uc=%d, retain_hdr=%d\n",
 			en_rule,
 			hdr->u.hdr.action,
 			hdr->u.hdr.rt_tbl_idx,
@@ -601,7 +601,7 @@
 
 	tbl = &ipa_ctx->glob_flt_tbl[ip];
 	if (tbl->prev_mem.phys_base) {
-		IPADBG("reaping glob flt tbl (prev) ip=%d\n", ip);
+		IPADBG_LOW("reaping glob flt tbl (prev) ip=%d\n", ip);
 		dma_free_coherent(ipa_ctx->pdev, tbl->prev_mem.size,
 				tbl->prev_mem.base, tbl->prev_mem.phys_base);
 		memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
@@ -609,7 +609,7 @@
 
 	if (list_empty(&tbl->head_flt_rule_list)) {
 		if (tbl->curr_mem.phys_base) {
-			IPADBG("reaping glob flt tbl (curr) ip=%d\n", ip);
+			IPADBG_LOW("reaping glob flt tbl (curr) ip=%d\n", ip);
 			dma_free_coherent(ipa_ctx->pdev, tbl->curr_mem.size,
 					tbl->curr_mem.base,
 					tbl->curr_mem.phys_base);
@@ -620,7 +620,8 @@
 	for (i = 0; i < ipa_ctx->ipa_num_pipes; i++) {
 		tbl = &ipa_ctx->flt_tbl[i][ip];
 		if (tbl->prev_mem.phys_base) {
-			IPADBG("reaping flt tbl (prev) pipe=%d ip=%d\n", i, ip);
+			IPADBG_LOW("reaping flt tbl");
+			IPADBG_LOW("(prev) pipe=%d ip=%d\n", i, ip);
 			dma_free_coherent(ipa_ctx->pdev, tbl->prev_mem.size,
 					tbl->prev_mem.base,
 					tbl->prev_mem.phys_base);
@@ -629,7 +630,8 @@
 
 		if (list_empty(&tbl->head_flt_rule_list)) {
 			if (tbl->curr_mem.phys_base) {
-				IPADBG("reaping flt tbl (curr) pipe=%d ip=%d\n",
+				IPADBG_LOW("reaping flt tbl");
+				IPADBG_LOW("(curr) pipe=%d ip=%d\n",
 						i, ip);
 				dma_free_coherent(ipa_ctx->pdev,
 						tbl->curr_mem.size,
@@ -899,7 +901,7 @@
 
 	for (i = 0; i < 6; i++) {
 		if (ipa_ctx->skip_ep_cfg_shadow[i]) {
-			IPADBG("skip %d\n", i);
+			IPADBG_LOW("skip %d\n", i);
 			continue;
 		}
 
@@ -908,7 +910,7 @@
 			ipa2_get_ep_mapping(IPA_CLIENT_APPS_CMD_PROD) == i ||
 			(ipa2_get_ep_mapping(IPA_CLIENT_APPS_LAN_WAN_PROD) == i
 			&& ipa_ctx->modem_cfg_emb_pipe_flt)) {
-			IPADBG("skip %d\n", i);
+			IPADBG_LOW("skip %d\n", i);
 			continue;
 		}
 
@@ -934,12 +936,12 @@
 
 	for (i = 11; i < ipa_ctx->ipa_num_pipes; i++) {
 		if (ipa_ctx->skip_ep_cfg_shadow[i]) {
-			IPADBG("skip %d\n", i);
+			IPADBG_LOW("skip %d\n", i);
 			continue;
 		}
 		if (ipa2_get_ep_mapping(IPA_CLIENT_APPS_LAN_WAN_PROD) == i &&
 			ipa_ctx->modem_cfg_emb_pipe_flt) {
-			IPADBG("skip %d\n", i);
+			IPADBG_LOW("skip %d\n", i);
 			continue;
 		}
 		if (ip == IPA_IP_v4) {
@@ -1074,7 +1076,7 @@
 	}
 	*rule_hdl = id;
 	entry->id = id;
-	IPADBG("add flt rule rule_cnt=%d\n", tbl->rule_cnt);
+	IPADBG_LOW("add flt rule rule_cnt=%d\n", tbl->rule_cnt);
 
 	return 0;
 ipa_insert_failed:
@@ -1108,7 +1110,7 @@
 	entry->tbl->rule_cnt--;
 	if (entry->rt_tbl)
 		entry->rt_tbl->ref_cnt--;
-	IPADBG("del flt rule rule_cnt=%d\n", entry->tbl->rule_cnt);
+	IPADBG_LOW("del flt rule rule_cnt=%d\n", entry->tbl->rule_cnt);
 	entry->cookie = 0;
 	kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
 
@@ -1194,7 +1196,7 @@
 	}
 
 	tbl = &ipa_ctx->glob_flt_tbl[ip];
-	IPADBG("add global flt rule ip=%d\n", ip);
+	IPADBG_LOW("add global flt rule ip=%d\n", ip);
 
 	return __ipa_add_flt_rule(tbl, ip, rule, add_rear, rule_hdl);
 }
@@ -1221,7 +1223,7 @@
 		IPADBG("ep not connected ep_idx=%d\n", ipa_ep_idx);
 
 	tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][ip];
-	IPADBG("add ep flt rule ip=%d ep=%d\n", ip, ep);
+	IPADBG_LOW("add ep flt rule ip=%d ep=%d\n", ip, ep);
 
 	return __ipa_add_flt_rule(tbl, ip, rule, add_rear, rule_hdl);
 }
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 2f72d88..5569979 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -43,7 +43,7 @@
 		IPAERR("hdr tbl empty\n");
 		return -EPERM;
 	}
-	IPADBG("tbl_sz=%d\n", ipa_ctx->hdr_tbl.end);
+	IPADBG_LOW("tbl_sz=%d\n", ipa_ctx->hdr_tbl.end);
 
 	mem->base = dma_alloc_coherent(ipa_ctx->pdev, mem->size,
 			&mem->phys_base, GFP_KERNEL);
@@ -57,7 +57,7 @@
 			link) {
 		if (entry->is_hdr_proc_ctx)
 			continue;
-		IPADBG("hdr of len %d ofst=%d\n", entry->hdr_len,
+		IPADBG_LOW("hdr of len %d ofst=%d\n", entry->hdr_len,
 				entry->offset_entry->offset);
 		memcpy(mem->base + entry->offset_entry->offset, entry->hdr,
 				entry->hdr_len);
@@ -74,7 +74,7 @@
 	list_for_each_entry(entry,
 			&ipa_ctx->hdr_proc_ctx_tbl.head_proc_ctx_entry_list,
 			link) {
-		IPADBG("processing type %d ofst=%d\n",
+		IPADBG_LOW("processing type %d ofst=%d\n",
 			entry->type, entry->offset_entry->offset);
 		if (entry->type == IPA_HDR_PROC_NONE) {
 			struct ipa_hdr_proc_ctx_add_hdr_seq *ctx;
@@ -88,7 +88,7 @@
 				entry->hdr->phys_base :
 				hdr_base_addr +
 				entry->hdr->offset_entry->offset;
-			IPADBG("header address 0x%x\n",
+			IPADBG_LOW("header address 0x%x\n",
 				ctx->hdr_add.hdr_addr);
 			ctx->end.type = IPA_PROC_CTX_TLV_TYPE_END;
 			ctx->end.length = 0;
@@ -105,7 +105,7 @@
 				entry->hdr->phys_base :
 				hdr_base_addr +
 				entry->hdr->offset_entry->offset;
-			IPADBG("header address 0x%x\n",
+			IPADBG_LOW("header address 0x%x\n",
 				ctx->hdr_add.hdr_addr);
 			ctx->cmd.type = IPA_PROC_CTX_TLV_TYPE_PROC_CMD;
 			ctx->cmd.length = 0;
@@ -117,7 +117,7 @@
 				ctx->cmd.value = IPA_HDR_UCP_802_3_TO_ETHII;
 			else if (entry->type == IPA_HDR_PROC_802_3_TO_802_3)
 				ctx->cmd.value = IPA_HDR_UCP_802_3_TO_802_3;
-			IPADBG("command id %d\n", ctx->cmd.value);
+			IPADBG_LOW("command id %d\n", ctx->cmd.value);
 			ctx->end.type = IPA_PROC_CTX_TLV_TYPE_END;
 			ctx->end.length = 0;
 			ctx->end.value = 0;
@@ -144,7 +144,7 @@
 	/* make sure table is aligned */
 	mem->size += IPA_HDR_PROC_CTX_TABLE_ALIGNMENT_BYTE;
 
-	IPADBG("tbl_sz=%d\n", ipa_ctx->hdr_proc_ctx_tbl.end);
+	IPADBG_LOW("tbl_sz=%d\n", ipa_ctx->hdr_proc_ctx_tbl.end);
 
 	mem->base = dma_alloc_coherent(ipa_ctx->pdev, mem->size,
 			&mem->phys_base, GFP_KERNEL);
@@ -554,7 +554,7 @@
 	int needed_len;
 	int mem_size;
 
-	IPADBG("processing type %d hdr_hdl %d\n",
+	IPADBG_LOW("processing type %d hdr_hdl %d\n",
 		proc_ctx->type, proc_ctx->hdr_hdl);
 
 	if (!HDR_PROC_TYPE_IS_VALID(proc_ctx->type)) {
@@ -633,7 +633,7 @@
 	entry->offset_entry = offset;
 	list_add(&entry->link, &htbl->head_proc_ctx_entry_list);
 	htbl->proc_ctx_cnt++;
-	IPADBG("add proc ctx of sz=%d cnt=%d ofst=%d\n", needed_len,
+	IPADBG_LOW("add proc ctx of sz=%d cnt=%d ofst=%d\n", needed_len,
 			htbl->proc_ctx_cnt, offset->offset);
 
 	id = ipa_id_alloc(entry);
@@ -774,12 +774,12 @@
 	list_add(&entry->link, &htbl->head_hdr_entry_list);
 	htbl->hdr_cnt++;
 	if (entry->is_hdr_proc_ctx)
-		IPADBG("add hdr of sz=%d hdr_cnt=%d phys_base=%pa\n",
+		IPADBG_LOW("add hdr of sz=%d hdr_cnt=%d phys_base=%pa\n",
 			hdr->hdr_len,
 			htbl->hdr_cnt,
 			&entry->phys_base);
 	else
-		IPADBG("add hdr of sz=%d hdr_cnt=%d ofst=%d\n",
+		IPADBG_LOW("add hdr of sz=%d hdr_cnt=%d ofst=%d\n",
 			hdr->hdr_len,
 			htbl->hdr_cnt,
 			entry->offset_entry->offset);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 67b0be6..7816afe 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -65,11 +65,37 @@
 
 
 #define IPA_MAX_NUM_REQ_CACHE 10
+#define IPA_IPC_LOG_PAGES 50
 
 #define IPADBG(fmt, args...) \
-	pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+	do { \
+		pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
+		if (ipa_ctx) { \
+			IPA_IPC_LOGGING(ipa_ctx->logbuf, \
+				DRV_NAME " %s:%d " fmt, ## args); \
+			IPA_IPC_LOGGING(ipa_ctx->logbuf_low, \
+				DRV_NAME " %s:%d " fmt, ## args); \
+			} \
+	} while (0)
+
+#define IPADBG_LOW(fmt, args...) \
+	do { \
+		pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
+		if (ipa_ctx) \
+			IPA_IPC_LOGGING(ipa_ctx->logbuf_low, \
+				DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
 #define IPAERR(fmt, args...) \
-	pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+	do { \
+		pr_err(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
+		if (ipa_ctx) { \
+			IPA_IPC_LOGGING(ipa_ctx->logbuf, \
+				DRV_NAME " %s:%d " fmt, ## args); \
+			IPA_IPC_LOGGING(ipa_ctx->logbuf_low, \
+				DRV_NAME " %s:%d " fmt, ## args); \
+		} \
+	} while (0)
 
 #define IPAERR_RL(fmt, args...) \
 	do { \
@@ -1040,6 +1066,8 @@
  * @use_ipa_teth_bridge: use tethering bridge driver
  * @ipa_bam_remote_mode: ipa bam is in remote mode
  * @modem_cfg_emb_pipe_flt: modem configure embedded pipe filtering rules
+ * @logbuf: ipc log buffer for high priority messages
+ * @logbuf_low: ipc log buffer for low priority messages
  * @ipa_wdi2: using wdi-2.0
  * @ipa_bus_hdl: msm driver handle for the data path bus
  * @ctrl: holds the core specific operations based on
@@ -1132,6 +1160,8 @@
 	/* featurize if memory footprint becomes a concern */
 	struct ipa_stats stats;
 	void *smem_pipe_mem;
+	void *logbuf;
+	void *logbuf_low;
 	u32 ipa_bus_hdl;
 	struct ipa_controller *ctrl;
 	struct idr ipa_idr;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c b/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c
index 17f577a..c17dee9 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_interrupts.c
@@ -103,11 +103,12 @@
 
 	switch (interrupt_info.interrupt) {
 	case IPA_TX_SUSPEND_IRQ:
+		IPADBG_LOW("processing TX_SUSPEND interrupt work-around\n");
 		suspend_data = ipa_read_reg(ipa_ctx->mmio,
 					IPA_IRQ_SUSPEND_INFO_EE_n_ADDR(ipa_ee));
 		if (!is_valid_ep(suspend_data))
 			return 0;
-
+		IPADBG_LOW("get interrupt %d\n", suspend_data);
 		suspend_interrupt_data =
 			kzalloc(sizeof(*suspend_interrupt_data), GFP_ATOMIC);
 		if (!suspend_interrupt_data) {
@@ -167,9 +168,11 @@
 	u32 i = 0;
 	u32 en;
 	bool uc_irq;
-
 	en = ipa_read_reg(ipa_ctx->mmio, IPA_IRQ_EN_EE_n_ADDR(ipa_ee));
 	reg = ipa_read_reg(ipa_ctx->mmio, IPA_IRQ_STTS_EE_n_ADDR(ipa_ee));
+	IPADBG_LOW(
+		"ISR enter\n isr_ctx = %d EN reg = 0x%x STTS reg = 0x%x\n",
+		isr_context, en, reg);
 	while (en & reg) {
 		bmsk = 1;
 		for (i = 0; i < IPA_IRQ_NUM_MAX; i++) {
@@ -206,21 +209,22 @@
 		reg = ipa_read_reg(ipa_ctx->mmio,
 				IPA_IRQ_STTS_EE_n_ADDR(ipa_ee));
 	}
+	IPADBG_LOW("Exit\n");
 }
 
 static void ipa_interrupt_defer(struct work_struct *work)
 {
-	IPADBG("processing interrupts in wq\n");
+	IPADBG_LOW("processing interrupts in wq\n");
 	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
 	ipa_process_interrupts(false);
 	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
-	IPADBG("Done\n");
+	IPADBG_LOW("Done\n");
 }
 
 static irqreturn_t ipa_isr(int irq, void *ctxt)
 {
 	unsigned long flags;
-
+	IPADBG_LOW("Enter\n");
 	/* defer interrupt handling in case IPA is not clocked on */
 	if (ipa_active_clients_trylock(&flags) == 0) {
 		IPADBG("defer interrupt processing\n");
@@ -235,7 +239,7 @@
 	}
 
 	ipa_process_interrupts(true);
-
+	IPADBG_LOW("Exit\n");
 bail:
 	ipa_active_clients_trylock_unlock(&flags);
 	return IRQ_HANDLED;
@@ -260,7 +264,7 @@
 	u32 bmsk;
 	int irq_num;
 
-	IPADBG("in ipa2_add_interrupt_handler\n");
+	IPADBG_LOW("in ipa2_add_interrupt_handler\n");
 	if (interrupt < IPA_BAD_SNOC_ACCESS_IRQ ||
 		interrupt >= IPA_IRQ_MAX) {
 		IPAERR("invalid interrupt number %d\n", interrupt);
@@ -284,7 +288,7 @@
 	bmsk = 1 << irq_num;
 	val |= bmsk;
 	ipa_write_reg(ipa_ctx->mmio, IPA_IRQ_EN_EE_n_ADDR(ipa_ee), val);
-	IPADBG("wrote IPA_IRQ_EN_EE_n_ADDR register. reg = %d\n", val);
+	IPADBG_LOW("wrote IPA_IRQ_EN_EE_n_ADDR register. reg = %d\n", val);
 	return 0;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index e6048d1..9e68843 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
@@ -558,9 +558,8 @@
 			list_del(&msg->link);
 		}
 
-		IPADBG("msg=%p\n", msg);
-
 		if (msg) {
+			IPADBG("msg=%pK\n", msg);
 			locked = 0;
 			mutex_unlock(&ipa_ctx->msg_lock);
 			if (copy_to_user(buf, &msg->meta,
@@ -588,6 +587,7 @@
 			IPA_STATS_INC_CNT(
 				ipa_ctx->stats.msg_r[msg->meta.msg_type]);
 			kfree(msg);
+			msg = NULL;
 		}
 
 		ret = -EAGAIN;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
index e8f25c9..0ab4a68 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
@@ -20,16 +20,40 @@
 #include "ipa_i.h"
 #include "ipa_qmi_service.h"
 
-#define IPA_MHI_DRV_NAME
+#define IPA_MHI_DRV_NAME "ipa_mhi"
 #define IPA_MHI_DBG(fmt, args...) \
-	pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
-		 __func__, __LINE__, ## args)
+	do { \
+		pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+#define IPA_MHI_DBG_LOW(fmt, args...) \
+	do { \
+		pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
 #define IPA_MHI_ERR(fmt, args...) \
-	pr_err(IPA_MHI_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+	do { \
+		pr_err(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+				IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+				IPA_MHI_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
 #define IPA_MHI_FUNC_ENTRY() \
-	IPA_MHI_DBG("ENTRY\n")
+	IPA_MHI_DBG_LOW("ENTRY\n")
 #define IPA_MHI_FUNC_EXIT() \
-	IPA_MHI_DBG("EXIT\n")
+	IPA_MHI_DBG_LOW("EXIT\n")
+
 
 bool ipa2_mhi_sps_channel_empty(enum ipa_client_type client)
 {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index 825c538..f8a0ded 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -312,7 +312,7 @@
 	int rc;
 
 	do {
-		IPAWANDBG("Notified about a Receive Event");
+		IPAWANDBG_LOW("Notified about a Receive Event");
 		rc = qmi_recv_msg(ipa_svc_handle);
 	} while (rc == 0);
 	if (rc != -ENOMSG)
@@ -386,7 +386,7 @@
 		req_id, result, error);
 		return result;
 	}
-	IPAWANDBG("Received %s successfully\n", resp_type);
+	IPAWANDBG_LOW("Received %s successfully\n", resp_type);
 	return 0;
 }
 
@@ -766,7 +766,7 @@
 	int rc;
 
 	do {
-		IPAWANDBG("Notified about a Receive Event");
+		IPAWANDBG_LOW("Notified about a Receive Event");
 		rc = qmi_recv_msg(ipa_q6_clnt);
 	} while (rc == 0);
 	if (rc != -ENOMSG)
@@ -778,7 +778,7 @@
 {
 	switch (event) {
 	case QMI_RECV_MSG:
-		IPAWANDBG("client qmi recv message called");
+		IPAWANDBG_LOW("client qmi recv message called");
 		if (!atomic_read(&workqueues_stopped))
 			queue_delayed_work(ipa_clnt_resp_workqueue,
 					   &work_recv_msg_client, 0);
@@ -1149,7 +1149,7 @@
 	resp_desc.msg_id = QMI_IPA_GET_DATA_STATS_RESP_V01;
 	resp_desc.ei_array = ipa_get_data_stats_resp_msg_data_v01_ei;
 
-	IPAWANDBG("Sending QMI_IPA_GET_DATA_STATS_REQ_V01\n");
+	IPAWANDBG_LOW("Sending QMI_IPA_GET_DATA_STATS_REQ_V01\n");
 	if (unlikely(!ipa_q6_clnt))
 		return -ETIMEDOUT;
 	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
@@ -1158,7 +1158,7 @@
 			sizeof(struct ipa_get_data_stats_resp_msg_v01),
 			QMI_SEND_STATS_REQ_TIMEOUT_MS);
 
-	IPAWANDBG("QMI_IPA_GET_DATA_STATS_RESP_V01 received\n");
+	IPAWANDBG_LOW("QMI_IPA_GET_DATA_STATS_RESP_V01 received\n");
 
 	return ipa_check_qmi_response(rc,
 		QMI_IPA_GET_DATA_STATS_REQ_V01, resp->resp.result,
@@ -1179,7 +1179,7 @@
 	resp_desc.msg_id = QMI_IPA_GET_APN_DATA_STATS_RESP_V01;
 	resp_desc.ei_array = ipa_get_apn_data_stats_resp_msg_data_v01_ei;
 
-	IPAWANDBG("Sending QMI_IPA_GET_APN_DATA_STATS_REQ_V01\n");
+	IPAWANDBG_LOW("Sending QMI_IPA_GET_APN_DATA_STATS_REQ_V01\n");
 	if (unlikely(!ipa_q6_clnt))
 		return -ETIMEDOUT;
 	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
@@ -1188,7 +1188,7 @@
 			sizeof(struct ipa_get_apn_data_stats_resp_msg_v01),
 			QMI_SEND_STATS_REQ_TIMEOUT_MS);
 
-	IPAWANDBG("QMI_IPA_GET_APN_DATA_STATS_RESP_V01 received\n");
+	IPAWANDBG_LOW("QMI_IPA_GET_APN_DATA_STATS_RESP_V01 received\n");
 
 	return ipa_check_qmi_response(rc,
 		QMI_IPA_GET_APN_DATA_STATS_REQ_V01, resp->resp.result,
@@ -1212,7 +1212,7 @@
 	resp_desc.msg_id = QMI_IPA_SET_DATA_USAGE_QUOTA_RESP_V01;
 	resp_desc.ei_array = ipa_set_data_usage_quota_resp_msg_data_v01_ei;
 
-	IPAWANDBG("Sending QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01\n");
+	IPAWANDBG_LOW("Sending QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01\n");
 	if (unlikely(!ipa_q6_clnt))
 		return -ETIMEDOUT;
 	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
@@ -1220,7 +1220,7 @@
 			&resp_desc, &resp, sizeof(resp),
 			QMI_SEND_STATS_REQ_TIMEOUT_MS);
 
-	IPAWANDBG("QMI_IPA_SET_DATA_USAGE_QUOTA_RESP_V01 received\n");
+	IPAWANDBG_LOW("QMI_IPA_SET_DATA_USAGE_QUOTA_RESP_V01 received\n");
 
 	return ipa_check_qmi_response(rc,
 		QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01, resp.resp.result,
@@ -1247,14 +1247,14 @@
 	resp_desc.msg_id = QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_V01;
 	resp_desc.ei_array = ipa_stop_data_usage_quota_resp_msg_data_v01_ei;
 
-	IPAWANDBG("Sending QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_V01\n");
+	IPAWANDBG_LOW("Sending QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_V01\n");
 	if (unlikely(!ipa_q6_clnt))
 		return -ETIMEDOUT;
 	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, &req, sizeof(req),
 		&resp_desc, &resp, sizeof(resp),
 		QMI_SEND_STATS_REQ_TIMEOUT_MS);
 
-	IPAWANDBG("QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_V01 received\n");
+	IPAWANDBG_LOW("QMI_IPA_STOP_DATA_USAGE_QUOTA_RESP_V01 received\n");
 
 	return ipa_check_qmi_response(rc,
 		QMI_IPA_STOP_DATA_USAGE_QUOTA_REQ_V01, resp.resp.result,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
index 4c504f1..1f5d619 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.h
@@ -31,9 +31,39 @@
 #define SUBSYS_MODEM "modem"
 
 #define IPAWANDBG(fmt, args...) \
-	pr_debug(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+	do { \
+		pr_debug(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+#define IPAWANDBG_LOW(fmt, args...) \
+	do { \
+		pr_debug(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
 #define IPAWANERR(fmt, args...) \
-	pr_err(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+	do { \
+		pr_err(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+#define IPAWANINFO(fmt, args...) \
+	do { \
+		pr_info(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			DEV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
 
 extern struct ipa_qmi_context *ipa_qmi_ctx;
 extern struct mutex ipa_qmi_lock;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 321cc89..c41ddf4 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -94,7 +94,7 @@
 		return -EPERM;
 	}
 
-	IPADBG("en_rule 0x%x\n", en_rule);
+	IPADBG_LOW("en_rule 0x%x\n", en_rule);
 
 	rule_hdr->u.hdr.en_rule = en_rule;
 	ipa_write_32(rule_hdr->u.word, (u8 *)rule_hdr);
@@ -497,7 +497,9 @@
 	set = &ipa_ctx->rt_tbl_set[ip];
 	list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
 		if (tbl->prev_mem.phys_base) {
-			IPADBG("reaping rt tbl name=%s ip=%d\n", tbl->name, ip);
+			IPADBG_LOW("reaping rt");
+			IPADBG_LOW("tbl name=%s ip=%d\n",
+				tbl->name, ip);
 			dma_free_coherent(ipa_ctx->pdev, tbl->prev_mem.size,
 					tbl->prev_mem.base,
 					tbl->prev_mem.phys_base);
@@ -510,8 +512,9 @@
 		list_del(&tbl->link);
 		WARN_ON(tbl->prev_mem.phys_base != 0);
 		if (tbl->curr_mem.phys_base) {
-			IPADBG("reaping sys rt tbl name=%s ip=%d\n", tbl->name,
-					ip);
+			IPADBG_LOW("reaping sys");
+			IPADBG_LOW("rt tbl name=%s ip=%d\n",
+				tbl->name, ip);
 			dma_free_coherent(ipa_ctx->pdev, tbl->curr_mem.size,
 					tbl->curr_mem.base,
 					tbl->curr_mem.phys_base);
@@ -973,7 +976,7 @@
 		list_del(&entry->link);
 		clear_bit(entry->idx, &ipa_ctx->rt_idx_bitmap[ip]);
 		entry->set->tbl_cnt--;
-		IPADBG("del rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
+		IPADBG_LOW("del rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
 				entry->set->tbl_cnt);
 		kmem_cache_free(ipa_ctx->rt_tbl_cache, entry);
 	} else {
@@ -981,7 +984,7 @@
 				&ipa_ctx->reap_rt_tbl_set[ip].head_rt_tbl_list);
 		clear_bit(entry->idx, &ipa_ctx->rt_idx_bitmap[ip]);
 		entry->set->tbl_cnt--;
-		IPADBG("del sys rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
+		IPADBG_LOW("del sys rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
 				entry->set->tbl_cnt);
 	}
 
@@ -1062,7 +1065,8 @@
 		WARN_ON(1);
 		goto ipa_insert_failed;
 	}
-	IPADBG("add rt rule tbl_idx=%d rule_cnt=%d\n", tbl->idx, tbl->rule_cnt);
+	IPADBG_LOW("add rt rule tbl_idx=%d", tbl->idx);
+	IPADBG_LOW("rule_cnt=%d\n", tbl->rule_cnt);
 	*rule_hdl = id;
 	entry->id = id;
 
@@ -1147,7 +1151,7 @@
 		__ipa_release_hdr_proc_ctx(entry->proc_ctx->id);
 	list_del(&entry->link);
 	entry->tbl->rule_cnt--;
-	IPADBG("del rt rule tbl_idx=%d rule_cnt=%d\n", entry->tbl->idx,
+	IPADBG_LOW("del rt rule tbl_idx=%d rule_cnt=%d\n", entry->tbl->idx,
 			entry->tbl->rule_cnt);
 	if (entry->tbl->rule_cnt == 0 && entry->tbl->ref_cnt == 0) {
 		if (__ipa_del_rt_tbl(entry->tbl))
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index e611abd..980b1f3 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -1643,6 +1643,7 @@
 	 * OFFSET_MEQ32_0 with mask of 0 and val of 0 and offset 0
 	 */
 	if (attrib->attrib_mask == 0) {
+		IPADBG_LOW("building default rule\n");
 		if (ipa_ofst_meq32[ofst_meq32] == -1) {
 			IPAERR("ran out of meq32 eq\n");
 			return -EPERM;
@@ -4886,13 +4887,17 @@
 
 static void *ipa2_get_ipc_logbuf(void)
 {
-	/* no support for IPC logging in IPAv2 */
+	if (ipa_ctx)
+		return ipa_ctx->logbuf;
+
 	return NULL;
 }
 
 static void *ipa2_get_ipc_logbuf_low(void)
 {
-	/* no support for IPC logging in IPAv2 */
+	if (ipa_ctx)
+		return ipa_ctx->logbuf_low;
+
 	return NULL;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 8ea1d99..92177f1 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1079,7 +1079,7 @@
 	struct ipa_tx_meta meta;
 
 	if (skb->protocol != htons(ETH_P_MAP)) {
-		IPAWANDBG
+		IPAWANDBG_LOW
 		("SW filtering out none QMAP packet received from %s",
 		current->comm);
 		dev_kfree_skb_any(skb);
@@ -1104,7 +1104,8 @@
 	if (atomic_read(&wwan_ptr->outstanding_pkts) >=
 					wwan_ptr->outstanding_high) {
 		if (!qmap_check) {
-			IPAWANDBG("pending(%d)/(%d)- stop(%d), qmap_chk(%d)\n",
+			IPAWANDBG_LOW
+				("pending(%d)/(%d)- stop(%d), qmap_chk(%d)\n",
 				atomic_read(&wwan_ptr->outstanding_pkts),
 				wwan_ptr->outstanding_high,
 				netif_queue_stopped(dev),
@@ -1198,7 +1199,8 @@
 		netif_queue_stopped(wwan_ptr->net) &&
 		atomic_read(&wwan_ptr->outstanding_pkts) <
 					(wwan_ptr->outstanding_low)) {
-		IPAWANDBG("Outstanding low (%d) - wake up queue\n",
+		IPAWANDBG_LOW
+			("Outstanding low (%d) - wake up queue\n",
 				wwan_ptr->outstanding_low);
 		netif_wake_queue(wwan_ptr->net);
 	}
@@ -1228,7 +1230,7 @@
 		int result;
 		unsigned int packet_len = skb->len;
 
-		IPAWANDBG("Rx packet was received");
+		IPAWANDBG_LOW("Rx packet was received");
 		skb->dev = ipa_netdevs[0];
 		skb->protocol = htons(ETH_P_MAP);
 
@@ -1803,10 +1805,10 @@
 {
 	switch (event) {
 	case IPA_RM_RESOURCE_GRANTED:
-		IPAWANDBG("%s: Q6_PROD GRANTED CB\n", __func__);
+		IPAWANDBG_LOW("%s: Q6_PROD GRANTED CB\n", __func__);
 		break;
 	case IPA_RM_RESOURCE_RELEASED:
-		IPAWANDBG("%s: Q6_PROD RELEASED CB\n", __func__);
+		IPAWANDBG_LOW("%s: Q6_PROD RELEASED CB\n", __func__);
 		break;
 	default:
 		return;
@@ -1915,7 +1917,7 @@
  */
 static void ipa_rm_resource_granted(void *dev)
 {
-	IPAWANDBG("Resource Granted - starting queue\n");
+	IPAWANDBG_LOW("Resource Granted - starting queue\n");
 	schedule_work(&ipa_tx_wakequeue_work);
 }
 
@@ -2291,7 +2293,7 @@
 	struct net_device *netdev = ipa_netdevs[0];
 	struct wwan_private *wwan_ptr = netdev_priv(netdev);
 
-	IPAWANDBG("Enter...\n");
+	IPAWANDBG_LOW("Enter...\n");
 	/* Do not allow A7 to suspend in case there are oustanding packets */
 	if (atomic_read(&wwan_ptr->outstanding_pkts) != 0) {
 		IPAWANDBG("Outstanding packets, postponing AP suspend.\n");
@@ -2302,7 +2304,7 @@
 	netif_tx_lock_bh(netdev);
 	ipa_rm_release_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
 	netif_tx_unlock_bh(netdev);
-	IPAWANDBG("Exit\n");
+	IPAWANDBG_LOW("Exit\n");
 
 	return 0;
 }
@@ -2321,9 +2323,9 @@
 {
 	struct net_device *netdev = ipa_netdevs[0];
 
-	IPAWANDBG("Enter...\n");
+	IPAWANDBG_LOW("Enter...\n");
 	netif_wake_queue(netdev);
-	IPAWANDBG("Exit\n");
+	IPAWANDBG_LOW("Exit\n");
 
 	return 0;
 }
@@ -2425,6 +2427,7 @@
 			return NOTIFY_DONE;
 		}
 	}
+	IPAWANDBG_LOW("Exit\n");
 	return NOTIFY_DONE;
 }
 
@@ -2868,7 +2871,7 @@
 		IPAWANERR("reset the pipe stats\n");
 	} else {
 		/* print tethered-client enum */
-		IPAWANDBG("Tethered-client enum(%d)\n", data->ipa_client);
+		IPAWANDBG_LOW("Tethered-client enum(%d)\n", data->ipa_client);
 	}
 
 	rc = ipa_qmi_get_data_stats(req, resp);
@@ -2886,10 +2889,11 @@
 	if (resp->dl_dst_pipe_stats_list_valid) {
 		for (pipe_len = 0; pipe_len < resp->dl_dst_pipe_stats_list_len;
 			pipe_len++) {
-			IPAWANDBG("Check entry(%d) dl_dst_pipe(%d)\n",
+			IPAWANDBG_LOW("Check entry(%d) dl_dst_pipe(%d)\n",
 				pipe_len, resp->dl_dst_pipe_stats_list
 					[pipe_len].pipe_index);
-			IPAWANDBG("dl_p_v4(%lu)v6(%lu) dl_b_v4(%lu)v6(%lu)\n",
+			IPAWANDBG_LOW
+				("dl_p_v4(%lu)v6(%lu) dl_b_v4(%lu)v6(%lu)\n",
 				(unsigned long int) resp->
 				dl_dst_pipe_stats_list[pipe_len].
 				num_ipv4_packets,
@@ -2925,7 +2929,7 @@
 			}
 		}
 	}
-	IPAWANDBG("v4_rx_p(%lu) v6_rx_p(%lu) v4_rx_b(%lu) v6_rx_b(%lu)\n",
+	IPAWANDBG_LOW("v4_rx_p(%lu) v6_rx_p(%lu) v4_rx_b(%lu) v6_rx_b(%lu)\n",
 		(unsigned long int) data->ipv4_rx_packets,
 		(unsigned long int) data->ipv6_rx_packets,
 		(unsigned long int) data->ipv4_rx_bytes,
@@ -2934,11 +2938,12 @@
 	if (resp->ul_src_pipe_stats_list_valid) {
 		for (pipe_len = 0; pipe_len < resp->ul_src_pipe_stats_list_len;
 			pipe_len++) {
-			IPAWANDBG("Check entry(%d) ul_dst_pipe(%d)\n",
+			IPAWANDBG_LOW("Check entry(%d) ul_dst_pipe(%d)\n",
 				pipe_len,
 				resp->ul_src_pipe_stats_list[pipe_len].
 				pipe_index);
-			IPAWANDBG("ul_p_v4(%lu)v6(%lu)ul_b_v4(%lu)v6(%lu)\n",
+			IPAWANDBG_LOW
+				("ul_p_v4(%lu)v6(%lu)ul_b_v4(%lu)v6(%lu)\n",
 				(unsigned long int) resp->
 				ul_src_pipe_stats_list[pipe_len].
 				num_ipv4_packets,
@@ -2974,7 +2979,7 @@
 			}
 		}
 	}
-	IPAWANDBG("tx_p_v4(%lu)v6(%lu)tx_b_v4(%lu) v6(%lu)\n",
+	IPAWANDBG_LOW("tx_p_v4(%lu)v6(%lu)tx_b_v4(%lu) v6(%lu)\n",
 		(unsigned long int) data->ipv4_tx_packets,
 		(unsigned long  int) data->ipv6_tx_packets,
 		(unsigned long int) data->ipv4_tx_bytes,
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c
index 793529d..5ef3063 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa_fd_ioctl.c
@@ -149,8 +149,7 @@
 		break;
 
 	case WAN_IOC_POLL_TETHERING_STATS:
-		IPAWANDBG("device %s got WAN_IOCTL_POLL_TETHERING_STATS :>>>\n",
-			  DRIVER_NAME);
+		IPAWANDBG_LOW("got WAN_IOCTL_POLL_TETHERING_STATS :>>>\n");
 		pyld_sz = sizeof(struct wan_ioctl_poll_tethering_stats);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
@@ -174,8 +173,7 @@
 		break;
 
 	case WAN_IOC_SET_DATA_QUOTA:
-		IPAWANDBG("device %s got WAN_IOCTL_SET_DATA_QUOTA :>>>\n",
-			  DRIVER_NAME);
+		IPAWANDBG_LOW("got WAN_IOCTL_SET_DATA_QUOTA :>>>\n");
 		pyld_sz = sizeof(struct wan_ioctl_set_data_quota);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
@@ -199,8 +197,7 @@
 		break;
 
 	case WAN_IOC_SET_TETHER_CLIENT_PIPE:
-		IPAWANDBG("device %s got WAN_IOC_SET_TETHER_CLIENT_PIPE :>>>\n",
-				DRIVER_NAME);
+		IPAWANDBG_LOW("got WAN_IOC_SET_TETHER_CLIENT_PIPE :>>>\n");
 		pyld_sz = sizeof(struct wan_ioctl_set_tether_client_pipe);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
@@ -220,8 +217,7 @@
 		break;
 
 	case WAN_IOC_QUERY_TETHER_STATS:
-		IPAWANDBG("device %s got WAN_IOC_QUERY_TETHER_STATS :>>>\n",
-				DRIVER_NAME);
+		IPAWANDBG_LOW("got WAN_IOC_QUERY_TETHER_STATS :>>>\n");
 		pyld_sz = sizeof(struct wan_ioctl_query_tether_stats);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
@@ -273,8 +269,7 @@
 		break;
 
 	case WAN_IOC_RESET_TETHER_STATS:
-		IPAWANDBG("device %s got WAN_IOC_RESET_TETHER_STATS :>>>\n",
-				DRIVER_NAME);
+		IPAWANDBG_LOW("got WAN_IOC_RESET_TETHER_STATS :>>>\n");
 		pyld_sz = sizeof(struct wan_ioctl_reset_tether_stats);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
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_pm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
index fea9b3b..3bf0327 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
@@ -1029,10 +1029,9 @@
 			IPA_PM_DBG_STATE(client->hdl, client->name,
 				client->state);
 			spin_unlock_irqrestore(&client->state_lock, flags);
-		} else if ((client->state ==
-			IPA_PM_ACTIVATED_PENDING_DEACTIVATION) ||
-			(client->state ==
-			IPA_PM_ACTIVATED_PENDING_RESCHEDULE)) {
+		} else if (client->state ==
+			IPA_PM_ACTIVATED_PENDING_DEACTIVATION ||
+			IPA_PM_ACTIVATED_PENDING_RESCHEDULE) {
 			run_algorithm = true;
 			client->state = IPA_PM_DEACTIVATED;
 			IPA_PM_DBG_STATE(client->hdl, client->name,
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/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 94736d4..bec16dd 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -402,7 +402,7 @@
  */
 void geni_cancel_m_cmd(void __iomem *base)
 {
-	geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_S_CMD_CTRL_REG);
+	geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_M_CMD_CTRL_REG);
 }
 EXPORT_SYMBOL(geni_cancel_m_cmd);
 
@@ -684,16 +684,14 @@
 	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
 		return -ENODEV;
 
-	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
-	if (ret) {
-		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
-			"%s: Error %d pinctrl_select_state\n", __func__, ret);
-		return ret;
-	}
 	ret = se_geni_clks_off(rsc);
 	if (ret)
 		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
 			"%s: Error %d turning off clocks\n", __func__, ret);
+	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
+	if (ret)
+		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
+			"%s: Error %d pinctrl_select_state\n", __func__, ret);
 	return ret;
 }
 EXPORT_SYMBOL(se_geni_resources_off);
@@ -802,19 +800,20 @@
 	if (unlikely(!geni_se_dev))
 		return -EPROBE_DEFER;
 
-	ret = se_geni_clks_on(rsc);
-	if (ret) {
-		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
-			"%s: Error %d during clks_on\n", __func__, ret);
-		return ret;
-	}
-
 	ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
 	if (ret) {
 		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
 			"%s: Error %d pinctrl_select_state\n", __func__, ret);
-		se_geni_clks_off(rsc);
+		return ret;
 	}
+
+	ret = se_geni_clks_on(rsc);
+	if (ret) {
+		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
+			"%s: Error %d during clks_on\n", __func__, ret);
+		pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
+	}
+
 	return ret;
 }
 EXPORT_SYMBOL(se_geni_resources_on);
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/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index d672d5f..f457eea 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/regmap.h>
 #include <linux/module.h>
 #include <linux/notifier.h>
@@ -37,6 +38,7 @@
 
 #define REG_REVISION_2			0x01
 #define REG_PERPH_TYPE			0x04
+#define REG_INT_RT_STS			0x10
 
 #define QPNP_LAB_TYPE			0x24
 #define QPNP_IBB_TYPE			0x20
@@ -77,8 +79,8 @@
 /* LAB register bits definitions */
 
 /* REG_LAB_STATUS1 */
-#define LAB_STATUS1_VREG_OK_MASK	BIT(7)
-#define LAB_STATUS1_VREG_OK		BIT(7)
+#define LAB_STATUS1_VREG_OK_BIT		BIT(7)
+#define LAB_STATUS1_SC_DETECT_BIT	BIT(6)
 
 /* REG_LAB_SWIRE_PGM_CTL */
 #define LAB_EN_SWIRE_PGM_VOUT		BIT(7)
@@ -188,8 +190,8 @@
 /* IBB register bits definition */
 
 /* REG_IBB_STATUS1 */
-#define IBB_STATUS1_VREG_OK_MASK	BIT(7)
-#define IBB_STATUS1_VREG_OK		BIT(7)
+#define IBB_STATUS1_VREG_OK_BIT		BIT(7)
+#define IBB_STATUS1_SC_DETECT_BIT	BIT(6)
 
 /* REG_IBB_VOLTAGE */
 #define IBB_VOLTAGE_OVERRIDE_EN		BIT(7)
@@ -557,12 +559,15 @@
 	struct mutex			lab_mutex;
 
 	int				lab_vreg_ok_irq;
+	int				lab_sc_irq;
+
 	int				curr_volt;
 	int				min_volt;
 
 	int				step_size;
 	int				slew_rate;
 	int				soft_start;
+	int				sc_wait_time_ms;
 
 	int				vreg_enabled;
 };
@@ -572,6 +577,8 @@
 	struct regulator_dev		*rdev;
 	struct mutex			ibb_mutex;
 
+	int				ibb_sc_irq;
+
 	int				curr_volt;
 	int				min_volt;
 
@@ -602,6 +609,9 @@
 	struct mutex			bus_mutex;
 	enum qpnp_labibb_mode		mode;
 	struct work_struct		lab_vreg_ok_work;
+	struct delayed_work		sc_err_recovery_work;
+	struct hrtimer			sc_err_check_timer;
+	int				sc_err_count;
 	bool				standalone;
 	bool				ttw_en;
 	bool				in_ttw_mode;
@@ -612,6 +622,8 @@
 	bool				skip_2nd_swire_cmd;
 	bool				pfm_enable;
 	bool				notify_lab_vreg_ok_sts;
+	bool				detect_lab_sc;
+	bool				sc_detected;
 	u32				swire_2nd_cmd_delay;
 	u32				swire_ibb_ps_enable_delay;
 };
@@ -2178,8 +2190,10 @@
 	u8 val;
 	struct qpnp_labibb *labibb  = container_of(work, struct qpnp_labibb,
 							lab_vreg_ok_work);
+	if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL)
+		retries = labibb->lab_vreg.sc_wait_time_ms / 5;
 
-	while (retries--) {
+	while (retries) {
 		rc = qpnp_labibb_read(labibb, labibb->lab_base +
 					REG_LAB_STATUS1, &val, 1);
 		if (rc < 0) {
@@ -2188,17 +2202,105 @@
 			return;
 		}
 
-		if (val & LAB_STATUS1_VREG_OK) {
+		if (val & LAB_STATUS1_VREG_OK_BIT) {
 			raw_notifier_call_chain(&labibb_notifier,
 						LAB_VREG_OK, NULL);
 			break;
 		}
 
 		usleep_range(dly, dly + 100);
+		retries--;
 	}
 
-	if (!retries)
-		pr_err("LAB_VREG_OK not set, failed to notify\n");
+	if (!retries) {
+		if (labibb->detect_lab_sc) {
+			pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n");
+			/* Disable LAB module */
+			val = 0;
+			rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_MODULE_RDY, &val, 1);
+			if (rc < 0) {
+				pr_err("write register %x failed rc = %d\n",
+					REG_LAB_MODULE_RDY, rc);
+				return;
+			}
+			raw_notifier_call_chain(&labibb_notifier,
+						LAB_VREG_NOT_OK, NULL);
+			labibb->sc_detected = true;
+			labibb->lab_vreg.vreg_enabled = 0;
+			labibb->ibb_vreg.vreg_enabled = 0;
+		} else {
+			pr_err("LAB_VREG_OK not set, failed to notify\n");
+		}
+	}
+}
+
+static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb)
+{
+	int rc;
+	u8 val;
+
+	val = LAB_ENABLE_CTL_EN;
+	rc = qpnp_labibb_write(labibb,
+		labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
+	if (rc < 0) {
+		pr_err("Write register %x failed rc = %d\n",
+					REG_LAB_ENABLE_CTL, rc);
+		return rc;
+	}
+
+	udelay(labibb->lab_vreg.soft_start);
+
+	rc = qpnp_labibb_read(labibb, labibb->lab_base +
+				REG_LAB_STATUS1, &val, 1);
+	if (rc < 0) {
+		pr_err("Read register %x failed rc = %d\n",
+					REG_LAB_STATUS1, rc);
+		return rc;
+	}
+
+	if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
+		pr_err("Can't enable LAB standalone\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb)
+{
+	int rc, delay, retries = 10;
+	u8 val;
+
+	rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+	if (rc < 0) {
+		pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+		return rc;
+	}
+
+	delay = labibb->ibb_vreg.soft_start;
+	while (retries--) {
+		/* Wait for a small period before reading IBB_STATUS1 */
+		usleep_range(delay, delay + 100);
+
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+				REG_IBB_STATUS1, &val, 1);
+		if (rc < 0) {
+			pr_err("Read register %x failed rc = %d\n",
+				REG_IBB_STATUS1, rc);
+			return rc;
+		}
+
+		if (val & IBB_STATUS1_VREG_OK_BIT)
+			break;
+	}
+
+	if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
+		pr_err("Can't enable IBB standalone\n");
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
 static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
@@ -2242,7 +2344,7 @@
 		labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start,
 				labibb->ibb_vreg.pwrup_dly, dly);
 
-	if (!(val & LAB_STATUS1_VREG_OK)) {
+	if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
 		pr_err("failed for LAB %x\n", val);
 		goto err_out;
 	}
@@ -2259,7 +2361,7 @@
 			goto err_out;
 		}
 
-		if (val & IBB_STATUS1_VREG_OK) {
+		if (val & IBB_STATUS1_VREG_OK_BIT) {
 			enabled = true;
 			break;
 		}
@@ -2330,7 +2432,7 @@
 			return rc;
 		}
 
-		if (!(val & IBB_STATUS1_VREG_OK)) {
+		if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
 			disabled = true;
 			break;
 		}
@@ -2359,10 +2461,13 @@
 static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
 {
 	int rc;
-	u8 val;
-
 	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
 
+	if (labibb->sc_detected) {
+		pr_info("Short circuit detected: disabled LAB/IBB rails\n");
+		return 0;
+	}
+
 	if (labibb->skip_2nd_swire_cmd) {
 		rc = qpnp_ibb_ps_config(labibb, false);
 		if (rc < 0) {
@@ -2372,38 +2477,18 @@
 	}
 
 	if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) {
-
 		if (!labibb->standalone)
 			return qpnp_labibb_regulator_enable(labibb);
 
-		val = LAB_ENABLE_CTL_EN;
-		rc = qpnp_labibb_write(labibb,
-			labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
-		if (rc < 0) {
-			pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n",
-				REG_LAB_ENABLE_CTL, rc);
+		rc = qpnp_lab_enable_standalone(labibb);
+		if (rc) {
+			pr_err("enable lab standalone failed, rc=%d\n", rc);
 			return rc;
 		}
-
-		udelay(labibb->lab_vreg.soft_start);
-
-		rc = qpnp_labibb_read(labibb, labibb->lab_base +
-					REG_LAB_STATUS1, &val, 1);
-		if (rc < 0) {
-			pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n",
-				REG_LAB_STATUS1, rc);
-			return rc;
-		}
-
-		if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) {
-			pr_err("qpnp_lab_regulator_enable failed\n");
-			return -EINVAL;
-		}
-
 		labibb->lab_vreg.vreg_enabled = 1;
 	}
 
-	if (labibb->notify_lab_vreg_ok_sts)
+	if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc)
 		schedule_work(&labibb->lab_vreg_ok_work);
 
 	return 0;
@@ -2444,6 +2529,163 @@
 	return labibb->lab_vreg.vreg_enabled;
 }
 
+static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb)
+{
+	int rc;
+
+	if (labibb->skip_2nd_swire_cmd) {
+		rc = qpnp_ibb_ps_config(labibb, false);
+		if (rc < 0) {
+			pr_err("Failed to disable IBB PS rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (!labibb->swire_control) {
+		if (!labibb->standalone)
+			return qpnp_labibb_regulator_enable(labibb);
+
+		rc = qpnp_ibb_enable_standalone(labibb);
+		if (rc < 0) {
+			pr_err("enable ibb standalone failed, rc=%d\n", rc);
+			return rc;
+		}
+		labibb->ibb_vreg.vreg_enabled = 1;
+
+		rc = qpnp_lab_enable_standalone(labibb);
+		if (rc < 0) {
+			pr_err("enable lab standalone failed, rc=%d\n", rc);
+			return rc;
+		}
+		labibb->lab_vreg.vreg_enabled = 1;
+	}
+
+	return 0;
+}
+
+#define SC_ERR_RECOVERY_DELAY_MS	250
+#define SC_ERR_COUNT_INTERVAL_SEC	1
+#define POLLING_SCP_DONE_COUNT		2
+#define POLLING_SCP_DONE_INTERVAL_MS	5
+static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb)
+{
+	int rc;
+	u16 reg;
+	u8 sc_err_mask, val;
+	char *str;
+	struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb;
+	bool in_sc_err, lab_en, ibb_en, scp_done = false;
+	int count;
+
+	if (irq == labibb->lab_vreg.lab_sc_irq) {
+		reg = labibb->lab_base + REG_LAB_STATUS1;
+		sc_err_mask = LAB_STATUS1_SC_DETECT_BIT;
+		str = "LAB";
+	} else if (irq == labibb->ibb_vreg.ibb_sc_irq) {
+		reg = labibb->ibb_base + REG_IBB_STATUS1;
+		sc_err_mask = IBB_STATUS1_SC_DETECT_BIT;
+		str = "IBB";
+	} else {
+		return IRQ_HANDLED;
+	}
+
+	rc = qpnp_labibb_read(labibb, reg, &val, 1);
+	if (rc < 0) {
+		pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
+		return IRQ_HANDLED;
+	}
+	pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val);
+
+	in_sc_err = !!(val & sc_err_mask);
+
+	/*
+	 * The SC fault would trigger PBS to disable regulators
+	 * for protection. This would cause the SC_DETECT status being
+	 * cleared so that it's not able to get the SC fault status.
+	 * Check if LAB/IBB regulators are enabled in the driver but
+	 * disabled in hardware, this means a SC fault had happened
+	 * and SCP handling is completed by PBS.
+	 */
+	if (!in_sc_err) {
+		count = POLLING_SCP_DONE_COUNT;
+		do {
+			reg = labibb->lab_base + REG_LAB_ENABLE_CTL;
+			rc = qpnp_labibb_read(labibb, reg, &val, 1);
+			if (rc < 0) {
+				pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
+				return IRQ_HANDLED;
+			}
+			lab_en = !!(val & LAB_ENABLE_CTL_EN);
+
+			reg = labibb->ibb_base + REG_IBB_ENABLE_CTL;
+			rc = qpnp_labibb_read(labibb, reg, &val, 1);
+			if (rc < 0) {
+				pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
+				return IRQ_HANDLED;
+			}
+			ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN);
+			if (lab_en || ibb_en)
+				msleep(POLLING_SCP_DONE_INTERVAL_MS);
+			else
+				break;
+		} while ((lab_en || ibb_en) && count--);
+
+		if (labibb->lab_vreg.vreg_enabled
+				&& labibb->ibb_vreg.vreg_enabled
+				&& !lab_en && !ibb_en) {
+			pr_debug("LAB/IBB has been disabled by SCP\n");
+			scp_done = true;
+		}
+	}
+
+	if (in_sc_err || scp_done) {
+		if (hrtimer_active(&labibb->sc_err_check_timer) ||
+			hrtimer_callback_running(&labibb->sc_err_check_timer)) {
+			labibb->sc_err_count++;
+		} else {
+			labibb->sc_err_count = 1;
+			hrtimer_start(&labibb->sc_err_check_timer,
+					ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0),
+					HRTIMER_MODE_REL);
+		}
+		schedule_delayed_work(&labibb->sc_err_recovery_work,
+				msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS));
+	}
+
+	return IRQ_HANDLED;
+}
+
+#define SC_FAULT_COUNT_MAX		4
+static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer)
+{
+	struct qpnp_labibb *labibb = container_of(timer,
+			struct qpnp_labibb, sc_err_check_timer);
+	/*
+	 * if SC fault triggers more than 4 times in 1 second,
+	 * then disable the IRQs and leave as it.
+	 */
+	if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) {
+		disable_irq(labibb->lab_vreg.lab_sc_irq);
+		disable_irq(labibb->ibb_vreg.ibb_sc_irq);
+	}
+
+	return HRTIMER_NORESTART;
+}
+
+static void labibb_sc_err_recovery_work(struct work_struct *work)
+{
+	struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb,
+					sc_err_recovery_work.work);
+	int rc;
+
+	labibb->ibb_vreg.vreg_enabled = 0;
+	labibb->lab_vreg.vreg_enabled = 0;
+	rc = qpnp_labibb_force_enable(labibb);
+	if (rc < 0)
+		pr_err("force enable labibb failed, rc=%d\n", rc);
+
+}
+
 static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev,
 				int min_uV, int max_uV, unsigned int *selector)
 {
@@ -2505,7 +2747,7 @@
 			pr_err("Failed to read ibb_status1 reg rc=%d\n", rc);
 			return rc;
 		}
-		if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK)
+		if (reg & IBB_STATUS1_VREG_OK_BIT)
 			break;
 
 		/* poll delay */
@@ -2661,6 +2903,12 @@
 	labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node,
 					"qcom,notify-lab-vreg-ok-sts");
 
+	labibb->lab_vreg.sc_wait_time_ms = -EINVAL;
+	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
+					labibb->detect_lab_sc)
+		of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms",
+					&labibb->lab_vreg.sc_wait_time_ms);
+
 	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start",
 					&(labibb->lab_vreg.soft_start));
 	if (!rc) {
@@ -2833,6 +3081,18 @@
 		}
 	}
 
+	if (labibb->lab_vreg.lab_sc_irq != -EINVAL) {
+		rc = devm_request_threaded_irq(labibb->dev,
+				labibb->lab_vreg.lab_sc_irq, NULL,
+				labibb_sc_err_handler,
+				IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+				"lab-sc-err", labibb);
+		if (rc) {
+			pr_err("Failed to register 'lab-sc-err' irq rc=%d\n",
+						rc);
+			return rc;
+		}
+	}
 	rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY,
 				&val, 1);
 	if (rc < 0) {
@@ -3302,45 +3562,26 @@
 
 static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
 {
-	int rc, delay, retries = 10;
-	u8 val;
+	int rc = 0;
 	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
 
-	if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
+	if (labibb->sc_detected) {
+		pr_info("Short circuit detected: disabled LAB/IBB rails\n");
+		return 0;
+	}
 
+	if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
 		if (!labibb->standalone)
 			return qpnp_labibb_regulator_enable(labibb);
 
-		rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+		rc = qpnp_ibb_enable_standalone(labibb);
 		if (rc < 0) {
-			pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+			pr_err("enable ibb standalone failed, rc=%d\n", rc);
 			return rc;
 		}
-
-		delay = labibb->ibb_vreg.soft_start;
-		while (retries--) {
-			/* Wait for a small period before reading IBB_STATUS1 */
-			usleep_range(delay, delay + 100);
-
-			rc = qpnp_labibb_read(labibb, labibb->ibb_base +
-					REG_IBB_STATUS1, &val, 1);
-			if (rc < 0) {
-				pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n",
-					REG_IBB_STATUS1, rc);
-				return rc;
-			}
-
-			if (val & IBB_STATUS1_VREG_OK)
-				break;
-		}
-
-		if (!(val & IBB_STATUS1_VREG_OK)) {
-			pr_err("qpnp_ibb_regulator_enable failed\n");
-			return -EINVAL;
-		}
-
 		labibb->ibb_vreg.vreg_enabled = 1;
 	}
+
 	return 0;
 }
 
@@ -3389,7 +3630,6 @@
 	return rc;
 }
 
-
 static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev)
 {
 	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
@@ -3611,6 +3851,19 @@
 		labibb->ibb_vreg.pwrdn_dly = 0;
 	}
 
+	if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) {
+		rc = devm_request_threaded_irq(labibb->dev,
+				labibb->ibb_vreg.ibb_sc_irq, NULL,
+				labibb_sc_err_handler,
+				IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+				"ibb-sc-err", labibb);
+		if (rc) {
+			pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n",
+						rc);
+			return rc;
+		}
+	}
+
 	rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY,
 				&val, 1);
 	if (rc < 0) {
@@ -3684,15 +3937,39 @@
 static int qpnp_lab_register_irq(struct device_node *child,
 				struct qpnp_labibb *labibb)
 {
+	int rc = 0;
+
 	if (is_lab_vreg_ok_irq_available(labibb)) {
-		labibb->lab_vreg.lab_vreg_ok_irq =
-					of_irq_get_byname(child, "lab-vreg-ok");
-		if (labibb->lab_vreg.lab_vreg_ok_irq < 0) {
+		rc = of_irq_get_byname(child, "lab-vreg-ok");
+		if (rc < 0) {
 			pr_err("Invalid lab-vreg-ok irq\n");
-			return -EINVAL;
+			return rc;
 		}
+		labibb->lab_vreg.lab_vreg_ok_irq = rc;
 	}
 
+	labibb->lab_vreg.lab_sc_irq = -EINVAL;
+	rc = of_irq_get_byname(child, "lab-sc-err");
+	if (rc < 0)
+		pr_debug("Unable to get lab-sc-err, rc = %d\n", rc);
+	else
+		labibb->lab_vreg.lab_sc_irq = rc;
+
+	return 0;
+}
+
+static int qpnp_ibb_register_irq(struct device_node *child,
+				struct qpnp_labibb *labibb)
+{
+	int rc;
+
+	labibb->ibb_vreg.ibb_sc_irq = -EINVAL;
+	rc = of_irq_get_byname(child, "ibb-sc-err");
+	if (rc < 0)
+		pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc);
+	else
+		labibb->ibb_vreg.ibb_sc_irq = rc;
+
 	return 0;
 }
 
@@ -3788,6 +4065,8 @@
 
 	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
 		labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+		/* Enable polling for LAB short circuit detection for PM660A */
+		labibb->detect_lab_sc = true;
 	} else {
 		rc = of_property_read_string(labibb->dev->of_node,
 				"qcom,qpnp-labibb-mode", &mode_name);
@@ -3896,6 +4175,7 @@
 		case QPNP_IBB_TYPE:
 			labibb->ibb_base = base;
 			labibb->ibb_dig_major = revision;
+			qpnp_ibb_register_irq(child, labibb);
 			rc = register_qpnp_ibb_regulator(labibb, child);
 			if (rc < 0)
 				goto fail_registration;
@@ -3919,6 +4199,11 @@
 	}
 
 	INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work);
+	INIT_DELAYED_WORK(&labibb->sc_err_recovery_work,
+			labibb_sc_err_recovery_work);
+	hrtimer_init(&labibb->sc_err_check_timer,
+			CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	labibb->sc_err_check_timer.function = labibb_check_sc_err_count;
 	dev_set_drvdata(&pdev->dev, labibb);
 	pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n",
 						labibb->lab_vreg.vreg_enabled,
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
index c012f37..bee9a3d 100644
--- a/drivers/regulator/qpnp-oledb-regulator.c
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -27,6 +27,7 @@
 #include <linux/regulator/of_regulator.h>
 #include <linux/regulator/qpnp-labibb-regulator.h>
 #include <linux/qpnp/qpnp-pbs.h>
+#include <linux/qpnp/qpnp-revid.h>
 
 #define QPNP_OLEDB_REGULATOR_DRIVER_NAME	"qcom,qpnp-oledb-regulator"
 #define OLEDB_VOUT_STEP_MV				100
@@ -162,6 +163,7 @@
 	struct notifier_block			oledb_nb;
 	struct mutex				bus_lock;
 	struct device_node			*pbs_dev_node;
+	struct pmic_revid_data			*pmic_rev_id;
 
 	u32					base;
 	u8					mod_enable;
@@ -181,6 +183,8 @@
 	bool					dynamic_ext_pinctl_config;
 	bool					pbs_control;
 	bool					force_pd_control;
+	bool					handle_lab_sc_notification;
+	bool					lab_sc_detected;
 };
 
 static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
@@ -275,6 +279,11 @@
 
 	struct qpnp_oledb *oledb  = rdev_get_drvdata(rdev);
 
+	if (oledb->lab_sc_detected == true) {
+		pr_info("Short circuit detected: Disabled OLEDB rail\n");
+		return 0;
+	}
+
 	if (oledb->ext_pin_control) {
 		rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
 								 &val, 1);
@@ -368,12 +377,19 @@
 		}
 
 		if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) {
-			rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
-							trigger_bitmap);
+			rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
+					OLEDB_SPARE_CTL,
+					OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
 			if (rc < 0) {
-				pr_err("Failed to trigger the PBS sequence\n");
+				pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
 				return rc;
 			}
+
+			rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
+							trigger_bitmap);
+			if (rc < 0)
+				pr_err("Failed to trigger the PBS sequence\n");
+
 			pr_debug("PBS event triggered\n");
 		} else {
 			pr_debug("OLEDB_SPARE_CTL register bit not set\n");
@@ -1085,8 +1101,22 @@
 static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
 {
 	int rc = 0;
+	struct device_node *revid_dev_node;
 	struct device_node *of_node = oledb->dev->of_node;
 
+	revid_dev_node = of_parse_phandle(oledb->dev->of_node,
+					"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		pr_err("Missing qcom,pmic-revid property - driver failed\n");
+		return -EINVAL;
+	}
+
+	oledb->pmic_rev_id = get_revid_data(revid_dev_node);
+	if (IS_ERR(oledb->pmic_rev_id)) {
+		pr_debug("Unable to get revid data\n");
+		return -EPROBE_DEFER;
+	}
+
 	oledb->swire_control =
 			of_property_read_bool(of_node, "qcom,swire-control");
 
@@ -1100,8 +1130,14 @@
 	oledb->pbs_control =
 			of_property_read_bool(of_node, "qcom,pbs-control");
 
-	oledb->force_pd_control =
-			of_property_read_bool(of_node, "qcom,force-pd-control");
+	/* Use the force_pd_control only for PM660A versions <= v2.0 */
+	if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
+				oledb->pmic_rev_id->rev4 <= PM660L_V2P0_REV4) {
+		if (!(oledb->pmic_rev_id->rev4 == PM660L_V2P0_REV4 &&
+			oledb->pmic_rev_id->rev2 > PM660L_V2P0_REV2)) {
+			oledb->force_pd_control = true;
+		}
+	}
 
 	if (oledb->force_pd_control) {
 		oledb->pbs_dev_node = of_parse_phandle(of_node,
@@ -1199,13 +1235,6 @@
 	int rc = 0;
 	u8 val;
 
-	rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
-		    OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
-	if (rc < 0) {
-		pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
-		return rc;
-	}
-
 	val = 1;
 	rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL,
 							&val, 1);
@@ -1227,14 +1256,31 @@
 					unsigned long action, void *data)
 {
 	int rc = 0;
+	u8 val;
 	struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb,
 								oledb_nb);
 
+	if (action == LAB_VREG_NOT_OK) {
+		/* short circuit detected. Disable OLEDB module */
+		val = 0;
+		rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY,
+					&val, 1);
+		if (rc < 0) {
+			pr_err("Failed to write MODULE_RDY rc=%d\n", rc);
+			return NOTIFY_STOP;
+		}
+		oledb->lab_sc_detected = true;
+		oledb->mod_enable = false;
+		pr_crit("LAB SC detected, disabling OLEDB forever!\n");
+	}
+
 	if (action == LAB_VREG_OK) {
 		/* Disable SWIRE pull down control and enable via spmi mode */
 		rc = qpnp_oledb_force_pulldown_config(oledb);
-		if (rc < 0)
+		if (rc < 0) {
+			pr_err("Failed to config force pull down\n");
 			return NOTIFY_STOP;
+		}
 	}
 
 	return NOTIFY_OK;
@@ -1281,7 +1327,11 @@
 		return rc;
 	}
 
-	if (oledb->force_pd_control) {
+	/* Enable LAB short circuit notification support */
+	if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		oledb->handle_lab_sc_notification = true;
+
+	if (oledb->force_pd_control || oledb->handle_lab_sc_notification) {
 		oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb;
 		rc = qpnp_labibb_notifier_register(&oledb->oledb_nb);
 		if (rc < 0) {
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 9750969..bd90802 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -310,6 +310,27 @@
 	  processors in the System on a Chip (SoC) which allows basic
 	  inter-processor communication.
 
+config MSM_SMD
+	depends on MSM_SMEM
+	bool "MSM Shared Memory Driver (SMD)"
+	help
+	  Support for the shared memory interprocessor communication protocol
+	  which provides virual point to point serial channels between processes
+	  on the apps processor and processes on other processors in the SoC.
+	  Also includes support for the Shared Memory State Machine (SMSM)
+	  protocol which provides a mechanism to publish single bit state
+	  information to one or more processors in the SoC.
+
+config MSM_SMD_DEBUG
+	depends on MSM_SMD
+	bool "MSM SMD debug support"
+	help
+	  Support for debugging SMD and SMSM communication between apps and
+	  other processors in the SoC. Debug support primarily consists of
+	  logs consisting of information such as what interrupts were processed,
+	  what channels caused interrupt activity, and when internal state
+	  change events occur.
+
 config MSM_GLINK
 	bool "Generic Link (G-Link)"
 	help
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 768d4d9..4454512 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -32,6 +32,7 @@
 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
+obj-$(CONFIG_MSM_SMD)	+= msm_smd.o smd_debug.o smd_private.o smd_init_dt.o smsm_debug.o
 obj-$(CONFIG_MSM_GLINK) += glink.o glink_debugfs.o glink_ssr.o
 obj-$(CONFIG_MSM_GLINK_LOOPBACK_SERVER) += glink_loopback_server.o
 obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o
diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h
index 9810207..3bcf56e 100644
--- a/drivers/soc/qcom/glink_private.h
+++ b/drivers/soc/qcom/glink_private.h
@@ -699,7 +699,6 @@
  *			received.
  * edge:		The G-Link edge name for the channel associated with
  *			this callback data
- * do_cleanup_data:	Structure containing the G-Link SSR do_cleanup message.
  * cb_kref:		Kref object to maintain cb_data reference.
  */
 struct ssr_notify_data {
@@ -707,7 +706,6 @@
 	unsigned int event;
 	bool responded;
 	const char *edge;
-	struct do_cleanup_msg *do_cleanup_data;
 	struct kref cb_kref;
 };
 
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 4737288..4a3293d 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -254,6 +254,8 @@
 void glink_ssr_notify_rx(void *handle, const void *priv, const void *pkt_priv,
 		const void *ptr, size_t size)
 {
+	struct do_cleanup_msg *do_cleanup_data =
+				(struct do_cleanup_msg *)pkt_priv;
 	struct ssr_notify_data *cb_data = (struct ssr_notify_data *)priv;
 	struct cleanup_done_msg *resp = (struct cleanup_done_msg *)ptr;
 	struct rx_done_ch_work *rx_done_work;
@@ -264,15 +266,15 @@
 				__func__);
 		return;
 	}
+	if (unlikely(!do_cleanup_data))
+		goto missing_do_cleanup_data;
 	if (unlikely(!cb_data))
 		goto missing_cb_data;
-	if (unlikely(!cb_data->do_cleanup_data))
-		goto missing_do_cleanup_data;
 	if (unlikely(!resp))
 		goto missing_response;
-	if (unlikely(resp->version != cb_data->do_cleanup_data->version))
+	if (unlikely(resp->version != do_cleanup_data->version))
 		goto version_mismatch;
-	if (unlikely(resp->seq_num != cb_data->do_cleanup_data->seq_num))
+	if (unlikely(resp->seq_num != do_cleanup_data->seq_num))
 		goto invalid_seq_number;
 	if (unlikely(resp->response != GLINK_SSR_CLEANUP_DONE))
 		goto wrong_response;
@@ -284,10 +286,9 @@
 		"<SSR> %s: Response from %s resp[%d] version[%d] seq_num[%d] restarted[%s]\n",
 			__func__, cb_data->edge, resp->response,
 			resp->version, resp->seq_num,
-			cb_data->do_cleanup_data->name);
+			do_cleanup_data->name);
 
-	kfree(cb_data->do_cleanup_data);
-	cb_data->do_cleanup_data = NULL;
+	kfree(do_cleanup_data);
 	rx_done_work->ptr = ptr;
 	rx_done_work->handle = handle;
 	INIT_WORK(&rx_done_work->work, rx_done_cb_worker);
@@ -306,13 +307,13 @@
 	return;
 version_mismatch:
 	GLINK_SSR_ERR("<SSR> %s: Version mismatch. %s[%d], %s[%d]\n", __func__,
-			"do_cleanup version", cb_data->do_cleanup_data->version,
+			"do_cleanup version", do_cleanup_data->version,
 			"cleanup_done version", resp->version);
 	return;
 invalid_seq_number:
 	GLINK_SSR_ERR("<SSR> %s: Invalid seq. number. %s[%d], %s[%d]\n",
 			__func__, "do_cleanup seq num",
-			cb_data->do_cleanup_data->seq_num,
+			do_cleanup_data->seq_num,
 			"cleanup_done seq_num", resp->seq_num);
 	return;
 wrong_response:
@@ -595,10 +596,8 @@
 		do_cleanup_data->name_len = strlen(ss_info->edge);
 		strlcpy(do_cleanup_data->name, ss_info->edge,
 				do_cleanup_data->name_len + 1);
-		ss_leaf_entry->cb_data->do_cleanup_data = do_cleanup_data;
 
-		ret = glink_queue_rx_intent(handle,
-				(void *)ss_leaf_entry->cb_data,
+		ret = glink_queue_rx_intent(handle, do_cleanup_data,
 				sizeof(struct cleanup_done_msg));
 		if (ret) {
 			GLINK_SSR_ERR(
@@ -607,7 +606,6 @@
 				"queue_rx_intent failed", ret,
 				atomic_read(&responses_remaining));
 			kfree(do_cleanup_data);
-			ss_leaf_entry->cb_data->do_cleanup_data = NULL;
 
 			if (strcmp(ss_leaf_entry->ssr_name, "rpm")) {
 				subsystem_restart(ss_leaf_entry->ssr_name);
@@ -623,12 +621,12 @@
 		}
 
 		if (strcmp(ss_leaf_entry->ssr_name, "rpm"))
-			ret = glink_tx(handle, ss_leaf_entry->cb_data,
+			ret = glink_tx(handle, do_cleanup_data,
 					do_cleanup_data,
 					sizeof(*do_cleanup_data),
 					GLINK_TX_REQ_INTENT);
 		else
-			ret = glink_tx(handle, ss_leaf_entry->cb_data,
+			ret = glink_tx(handle, do_cleanup_data,
 					do_cleanup_data,
 					sizeof(*do_cleanup_data),
 					GLINK_TX_SINGLE_THREADED);
@@ -638,7 +636,6 @@
 					__func__, ret, "resp. remaining",
 					atomic_read(&responses_remaining));
 			kfree(do_cleanup_data);
-			ss_leaf_entry->cb_data->do_cleanup_data = NULL;
 
 			if (strcmp(ss_leaf_entry->ssr_name, "rpm")) {
 				subsystem_restart(ss_leaf_entry->ssr_name);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
index 6c69bec..8e1fc0a 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
@@ -27,6 +27,7 @@
 };
 
 static struct class *bus_floor_class;
+static DEFINE_RT_MUTEX(msm_bus_floor_vote_lock);
 #define MAX_VOTER_NAME	(50)
 #define DEFAULT_NODE_WIDTH	(8)
 #define DBG_NAME(s)	(strnstr(s, "-", 7) + 1)
@@ -64,18 +65,22 @@
 {
 	struct msm_bus_floor_client_type *cl;
 
+	rt_mutex_lock(&msm_bus_floor_vote_lock);
 	cl = dev_get_drvdata(dev);
 
 	if (!cl) {
 		pr_err("%s: Can't find cl", __func__);
+		rt_mutex_unlock(&msm_bus_floor_vote_lock);
 		return 0;
 	}
 
 	if (kstrtoint(buf, 10, &cl->active_only) != 0) {
 		pr_err("%s:return error", __func__);
+		rt_mutex_unlock(&msm_bus_floor_vote_lock);
 		return -EINVAL;
 	}
 
+	rt_mutex_unlock(&msm_bus_floor_vote_lock);
 	return n;
 }
 
@@ -100,20 +105,24 @@
 	struct msm_bus_floor_client_type *cl;
 	int ret = 0;
 
+	rt_mutex_lock(&msm_bus_floor_vote_lock);
 	cl = dev_get_drvdata(dev);
 
 	if (!cl) {
 		pr_err("%s: Can't find cl", __func__);
+		rt_mutex_unlock(&msm_bus_floor_vote_lock);
 		return 0;
 	}
 
 	if (kstrtoull(buf, 10, &cl->cur_vote_hz) != 0) {
 		pr_err("%s:return error", __func__);
+		rt_mutex_unlock(&msm_bus_floor_vote_lock);
 		return -EINVAL;
 	}
 
 	ret = msm_bus_floor_vote_context(dev_name(dev), cl->cur_vote_hz,
 					cl->active_only);
+	rt_mutex_unlock(&msm_bus_floor_vote_lock);
 	return n;
 }
 
@@ -126,15 +135,18 @@
 	char name[10];
 	u64 vote_khz = 0;
 
+	rt_mutex_lock(&msm_bus_floor_vote_lock);
 	cl = dev_get_drvdata(dev);
 
 	if (!cl) {
 		pr_err("%s: Can't find cl", __func__);
+		rt_mutex_unlock(&msm_bus_floor_vote_lock);
 		return 0;
 	}
 
 	if (sscanf(buf, "%9s %llu", name, &vote_khz) != 2) {
 		pr_err("%s:return error", __func__);
+		rt_mutex_unlock(&msm_bus_floor_vote_lock);
 		return -EINVAL;
 	}
 
@@ -142,6 +154,7 @@
 			__func__, name, vote_khz);
 
 	ret = msm_bus_floor_vote(name, vote_khz);
+	rt_mutex_unlock(&msm_bus_floor_vote_lock);
 	return n;
 }
 
diff --git a/drivers/soc/qcom/msm_smd.c b/drivers/soc/qcom/msm_smd.c
new file mode 100644
index 0000000..1631984
--- /dev/null
+++ b/drivers/soc/qcom/msm_smd.c
@@ -0,0 +1,3254 @@
+/* drivers/soc/qcom/msm_smd.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/termios.h>
+#include <linux/ctype.h>
+#include <linux/remote_spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/kfifo.h>
+#include <linux/pm.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/ipc_logging.h>
+
+#include <soc/qcom/ramdump.h>
+#include <soc/qcom/smd.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
+
+#include "smd_private.h"
+#include "smem_private.h"
+
+#define SMSM_SNAPSHOT_CNT 64
+#define SMSM_SNAPSHOT_SIZE ((SMSM_NUM_ENTRIES + 1) * 4 + sizeof(uint64_t))
+#define RSPIN_INIT_WAIT_MS 1000
+#define SMD_FIFO_FULL_RESERVE 4
+#define SMD_FIFO_ADDR_ALIGN_BYTES 3
+
+uint32_t SMSM_NUM_ENTRIES = 8;
+uint32_t SMSM_NUM_HOSTS = 3;
+
+/* Legacy SMSM interrupt notifications */
+#define LEGACY_MODEM_SMSM_MASK (SMSM_RESET | SMSM_INIT | SMSM_SMDINIT)
+
+struct smsm_shared_info {
+	uint32_t *state;
+	uint32_t *intr_mask;
+	uint32_t *intr_mux;
+};
+
+static struct smsm_shared_info smsm_info;
+static struct kfifo smsm_snapshot_fifo;
+static struct wakeup_source smsm_snapshot_ws;
+static int smsm_snapshot_count;
+static DEFINE_SPINLOCK(smsm_snapshot_count_lock);
+
+struct smsm_size_info_type {
+	uint32_t num_hosts;
+	uint32_t num_entries;
+	uint32_t reserved0;
+	uint32_t reserved1;
+};
+
+struct smsm_state_cb_info {
+	struct list_head cb_list;
+	uint32_t mask;
+	void *data;
+	void (*notify)(void *data, uint32_t old_state, uint32_t new_state);
+};
+
+struct smsm_state_info {
+	struct list_head callbacks;
+	uint32_t last_value;
+	uint32_t intr_mask_set;
+	uint32_t intr_mask_clear;
+};
+
+static irqreturn_t smsm_irq_handler(int irq, void *data);
+
+/*
+ * Interrupt configuration consists of static configuration for the supported
+ * processors that is done here along with interrupt configuration that is
+ * added by the separate initialization modules (device tree, platform data, or
+ * hard coded).
+ */
+static struct interrupt_config private_intr_config[NUM_SMD_SUBSYSTEMS] = {
+	[SMD_MODEM] = {
+		.smd.irq_handler = smd_modem_irq_handler,
+		.smsm.irq_handler = smsm_modem_irq_handler,
+	},
+	[SMD_Q6] = {
+		.smd.irq_handler = smd_dsp_irq_handler,
+		.smsm.irq_handler = smsm_dsp_irq_handler,
+	},
+	[SMD_DSPS] = {
+		.smd.irq_handler = smd_dsps_irq_handler,
+		.smsm.irq_handler = smsm_dsps_irq_handler,
+	},
+	[SMD_WCNSS] = {
+		.smd.irq_handler = smd_wcnss_irq_handler,
+		.smsm.irq_handler = smsm_wcnss_irq_handler,
+	},
+	[SMD_MODEM_Q6_FW] = {
+		.smd.irq_handler = smd_modemfw_irq_handler,
+		.smsm.irq_handler = NULL, /* does not support smsm */
+	},
+	[SMD_RPM] = {
+		.smd.irq_handler = smd_rpm_irq_handler,
+		.smsm.irq_handler = NULL, /* does not support smsm */
+	},
+};
+
+struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
+
+#define SMSM_STATE_ADDR(entry)           (smsm_info.state + entry)
+#define SMSM_INTR_MASK_ADDR(entry, host) (smsm_info.intr_mask + \
+					  entry * SMSM_NUM_HOSTS + host)
+#define SMSM_INTR_MUX_ADDR(entry)        (smsm_info.intr_mux + entry)
+
+int msm_smd_debug_mask = MSM_SMD_POWER_INFO | MSM_SMD_INFO |
+							MSM_SMSM_POWER_INFO;
+module_param_named(debug_mask, msm_smd_debug_mask, int, 0664);
+void *smd_log_ctx;
+void *smsm_log_ctx;
+#define NUM_LOG_PAGES 4
+
+#define IPC_LOG_SMD(level, x...) do { \
+	if (smd_log_ctx) \
+		ipc_log_string(smd_log_ctx, x); \
+	else \
+		printk(level x); \
+	} while (0)
+
+#define IPC_LOG_SMSM(level, x...) do { \
+	if (smsm_log_ctx) \
+		ipc_log_string(smsm_log_ctx, x); \
+	else \
+		printk(level x); \
+	} while (0)
+
+#if defined(CONFIG_MSM_SMD_DEBUG)
+#define SMD_DBG(x...) do {				\
+		if (msm_smd_debug_mask & MSM_SMD_DEBUG) \
+			IPC_LOG_SMD(KERN_DEBUG, x);	\
+	} while (0)
+
+#define SMSM_DBG(x...) do {					\
+		if (msm_smd_debug_mask & MSM_SMSM_DEBUG)	\
+			IPC_LOG_SMSM(KERN_DEBUG, x);		\
+	} while (0)
+
+#define SMD_INFO(x...) do {				\
+		if (msm_smd_debug_mask & MSM_SMD_INFO)	\
+			IPC_LOG_SMD(KERN_INFO, x);	\
+	} while (0)
+
+#define SMSM_INFO(x...) do {				\
+		if (msm_smd_debug_mask & MSM_SMSM_INFO) \
+			IPC_LOG_SMSM(KERN_INFO, x);	\
+	} while (0)
+
+#define SMD_POWER_INFO(x...) do {				\
+		if (msm_smd_debug_mask & MSM_SMD_POWER_INFO)	\
+			IPC_LOG_SMD(KERN_INFO, x);		\
+	} while (0)
+
+#define SMSM_POWER_INFO(x...) do {				\
+		if (msm_smd_debug_mask & MSM_SMSM_POWER_INFO)	\
+			IPC_LOG_SMSM(KERN_INFO, x);		\
+	} while (0)
+#else
+#define SMD_DBG(x...) do { } while (0)
+#define SMSM_DBG(x...) do { } while (0)
+#define SMD_INFO(x...) do { } while (0)
+#define SMSM_INFO(x...) do { } while (0)
+#define SMD_POWER_INFO(x...) do { } while (0)
+#define SMSM_POWER_INFO(x...) do { } while (0)
+#endif
+
+static void smd_fake_irq_handler(unsigned long arg);
+static void smsm_cb_snapshot(uint32_t use_wakeup_source);
+
+static struct workqueue_struct *smsm_cb_wq;
+static void notify_smsm_cb_clients_worker(struct work_struct *work);
+static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker);
+static DEFINE_MUTEX(smsm_lock);
+static struct smsm_state_info *smsm_states;
+
+static int smd_stream_write_avail(struct smd_channel *ch);
+static int smd_stream_read_avail(struct smd_channel *ch);
+
+static bool pid_is_on_edge(uint32_t edge_num, unsigned int pid);
+
+static inline void smd_write_intr(unsigned int val, void __iomem *addr)
+{
+	wmb(); /* Make sure memory is visible before dorebell */
+	__raw_writel(val, addr);
+}
+
+/**
+ * smd_memcpy_to_fifo() - copy to SMD channel FIFO
+ * @dest: Destination address
+ * @src: Source address
+ * @num_bytes: Number of bytes to copy
+ *
+ * @return: Address of destination
+ *
+ * This function copies num_bytes from src to dest. This is used as the memcpy
+ * function to copy data to SMD FIFO in case the SMD FIFO is naturally aligned.
+ */
+static void *smd_memcpy_to_fifo(void *dest, const void *src, size_t num_bytes)
+{
+
+	memcpy_toio(dest, src, num_bytes);
+	return dest;
+}
+
+/**
+ * smd_memcpy_from_fifo() - copy from SMD channel FIFO
+ * @dest: Destination address
+ * @src: Source address
+ * @num_bytes: Number of bytes to copy
+ *
+ * @return: Address of destination
+ *
+ * This function copies num_bytes from src to dest. This is used as the memcpy
+ * function to copy data from SMD FIFO in case the SMD FIFO is naturally
+ * aligned.
+ */
+static void *smd_memcpy_from_fifo(void *dest, const void *src, size_t num_bytes)
+{
+	memcpy_fromio(dest, src, num_bytes);
+	return dest;
+}
+
+/**
+ * smd_memcpy32_to_fifo() - Copy to SMD channel FIFO
+ *
+ * @dest: Destination address
+ * @src: Source address
+ * @num_bytes: Number of bytes to copy
+ *
+ * @return: On Success, address of destination
+ *
+ * This function copies num_bytes data from src to dest. This is used as the
+ * memcpy function to copy data to SMD FIFO in case the SMD FIFO is 4 byte
+ * aligned.
+ */
+static void *smd_memcpy32_to_fifo(void *dest, const void *src, size_t num_bytes)
+{
+	uint32_t *dest_local = (uint32_t *)dest;
+	uint32_t *src_local = (uint32_t *)src;
+
+	WARN_ON(num_bytes & SMD_FIFO_ADDR_ALIGN_BYTES);
+	WARN_ON(!dest_local ||
+			((uintptr_t)dest_local & SMD_FIFO_ADDR_ALIGN_BYTES));
+	WARN_ON(!src_local ||
+			((uintptr_t)src_local & SMD_FIFO_ADDR_ALIGN_BYTES));
+	num_bytes /= sizeof(uint32_t);
+
+	while (num_bytes--)
+		__raw_writel_no_log(*src_local++, dest_local++);
+
+	return dest;
+}
+
+/**
+ * smd_memcpy32_from_fifo() - Copy from SMD channel FIFO
+ * @dest: Destination address
+ * @src: Source address
+ * @num_bytes: Number of bytes to copy
+ *
+ * @return: On Success, destination address
+ *
+ * This function copies num_bytes data from SMD FIFO to dest. This is used as
+ * the memcpy function to copy data from SMD FIFO in case the SMD FIFO is 4 byte
+ * aligned.
+ */
+static void *smd_memcpy32_from_fifo(void *dest, const void *src,
+						size_t num_bytes)
+{
+
+	uint32_t *dest_local = (uint32_t *)dest;
+	uint32_t *src_local = (uint32_t *)src;
+
+	WARN_ON(num_bytes & SMD_FIFO_ADDR_ALIGN_BYTES);
+	WARN_ON(!dest_local ||
+			((uintptr_t)dest_local & SMD_FIFO_ADDR_ALIGN_BYTES));
+	WARN_ON(!src_local ||
+			((uintptr_t)src_local & SMD_FIFO_ADDR_ALIGN_BYTES));
+	num_bytes /= sizeof(uint32_t);
+
+	while (num_bytes--)
+		*dest_local++ = __raw_readl_no_log(src_local++);
+
+	return dest;
+}
+
+static inline void log_notify(uint32_t subsystem, smd_channel_t *ch)
+{
+	const char *subsys = smd_edge_to_subsystem(subsystem);
+
+	(void) subsys;
+
+	if (!ch)
+		SMD_POWER_INFO("Apps->%s\n", subsys);
+	else
+		SMD_POWER_INFO(
+			"Apps->%s ch%d '%s': tx%d/rx%d %dr/%dw : %dr/%dw\n",
+			subsys, ch->n, ch->name,
+			ch->fifo_size -
+				(smd_stream_write_avail(ch) + 1),
+			smd_stream_read_avail(ch),
+			ch->half_ch->get_tail(ch->send),
+			ch->half_ch->get_head(ch->send),
+			ch->half_ch->get_tail(ch->recv),
+			ch->half_ch->get_head(ch->recv)
+			);
+}
+
+static inline void notify_modem_smd(smd_channel_t *ch)
+{
+	static const struct interrupt_config_item *intr
+	   = &private_intr_config[SMD_MODEM].smd;
+
+	log_notify(SMD_APPS_MODEM, ch);
+	if (intr->out_base) {
+		++interrupt_stats[SMD_MODEM].smd_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_dsp_smd(smd_channel_t *ch)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_Q6].smd;
+
+	log_notify(SMD_APPS_QDSP, ch);
+	if (intr->out_base) {
+		++interrupt_stats[SMD_Q6].smd_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_dsps_smd(smd_channel_t *ch)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_DSPS].smd;
+
+	log_notify(SMD_APPS_DSPS, ch);
+	if (intr->out_base) {
+		++interrupt_stats[SMD_DSPS].smd_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_wcnss_smd(struct smd_channel *ch)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_WCNSS].smd;
+
+	log_notify(SMD_APPS_WCNSS, ch);
+	if (intr->out_base) {
+		++interrupt_stats[SMD_WCNSS].smd_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_modemfw_smd(smd_channel_t *ch)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_MODEM_Q6_FW].smd;
+
+	log_notify(SMD_APPS_Q6FW, ch);
+	if (intr->out_base) {
+		++interrupt_stats[SMD_MODEM_Q6_FW].smd_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_rpm_smd(smd_channel_t *ch)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_RPM].smd;
+
+	if (intr->out_base) {
+		log_notify(SMD_APPS_RPM, ch);
+		++interrupt_stats[SMD_RPM].smd_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_modem_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_MODEM].smsm;
+
+	SMSM_POWER_INFO("SMSM Apps->%s", "MODEM");
+
+	if (intr->out_base) {
+		++interrupt_stats[SMD_MODEM].smsm_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_dsp_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_Q6].smsm;
+
+	SMSM_POWER_INFO("SMSM Apps->%s", "ADSP");
+
+	if (intr->out_base) {
+		++interrupt_stats[SMD_Q6].smsm_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_dsps_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_DSPS].smsm;
+
+	SMSM_POWER_INFO("SMSM Apps->%s", "DSPS");
+
+	if (intr->out_base) {
+		++interrupt_stats[SMD_DSPS].smsm_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static inline void notify_wcnss_smsm(void)
+{
+	static const struct interrupt_config_item *intr
+		= &private_intr_config[SMD_WCNSS].smsm;
+
+	SMSM_POWER_INFO("SMSM Apps->%s", "WCNSS");
+
+	if (intr->out_base) {
+		++interrupt_stats[SMD_WCNSS].smsm_out_count;
+		smd_write_intr(intr->out_bit_pos,
+		intr->out_base + intr->out_offset);
+	}
+}
+
+static void notify_other_smsm(uint32_t smsm_entry, uint32_t notify_mask)
+{
+	if (smsm_info.intr_mask &&
+	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_MODEM))
+				& notify_mask))
+		notify_modem_smsm();
+
+	if (smsm_info.intr_mask &&
+	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_Q6))
+				& notify_mask))
+		notify_dsp_smsm();
+
+	if (smsm_info.intr_mask &&
+	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_WCNSS))
+				& notify_mask)) {
+		notify_wcnss_smsm();
+	}
+
+	if (smsm_info.intr_mask &&
+	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_DSPS))
+				& notify_mask)) {
+		notify_dsps_smsm();
+	}
+
+	if (smsm_info.intr_mask &&
+	    (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS))
+				& notify_mask)) {
+		smsm_cb_snapshot(1);
+	}
+}
+
+static int smsm_pm_notifier(struct notifier_block *nb,
+				unsigned long event, void *unused)
+{
+	switch (event) {
+	case PM_SUSPEND_PREPARE:
+		smsm_change_state(SMSM_APPS_STATE, SMSM_PROC_AWAKE, 0);
+		break;
+
+	case PM_POST_SUSPEND:
+		smsm_change_state(SMSM_APPS_STATE, 0, SMSM_PROC_AWAKE);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block smsm_pm_nb = {
+	.notifier_call = smsm_pm_notifier,
+	.priority = 0,
+};
+
+/* the spinlock is used to synchronize between the
+ * irq handler and code that mutates the channel
+ * list or fiddles with channel state
+ */
+static DEFINE_SPINLOCK(smd_lock);
+DEFINE_SPINLOCK(smem_lock);
+
+/* the mutex is used during open() and close()
+ * operations to avoid races while creating or
+ * destroying smd_channel structures
+ */
+static DEFINE_MUTEX(smd_creation_mutex);
+
+struct smd_shared {
+	struct smd_half_channel ch0;
+	struct smd_half_channel ch1;
+};
+
+struct smd_shared_word_access {
+	struct smd_half_channel_word_access ch0;
+	struct smd_half_channel_word_access ch1;
+};
+
+/**
+ * Maps edge type to local and remote processor ID's.
+ */
+static struct edge_to_pid edge_to_pids[] = {
+	[SMD_APPS_MODEM] = {SMD_APPS, SMD_MODEM, "modem"},
+	[SMD_APPS_QDSP] = {SMD_APPS, SMD_Q6, "adsp"},
+	[SMD_MODEM_QDSP] = {SMD_MODEM, SMD_Q6},
+	[SMD_APPS_DSPS] = {SMD_APPS, SMD_DSPS, "dsps"},
+	[SMD_MODEM_DSPS] = {SMD_MODEM, SMD_DSPS},
+	[SMD_QDSP_DSPS] = {SMD_Q6, SMD_DSPS},
+	[SMD_APPS_WCNSS] = {SMD_APPS, SMD_WCNSS, "wcnss"},
+	[SMD_MODEM_WCNSS] = {SMD_MODEM, SMD_WCNSS},
+	[SMD_QDSP_WCNSS] = {SMD_Q6, SMD_WCNSS},
+	[SMD_DSPS_WCNSS] = {SMD_DSPS, SMD_WCNSS},
+	[SMD_APPS_Q6FW] = {SMD_APPS, SMD_MODEM_Q6_FW},
+	[SMD_MODEM_Q6FW] = {SMD_MODEM, SMD_MODEM_Q6_FW},
+	[SMD_QDSP_Q6FW] = {SMD_Q6, SMD_MODEM_Q6_FW},
+	[SMD_DSPS_Q6FW] = {SMD_DSPS, SMD_MODEM_Q6_FW},
+	[SMD_WCNSS_Q6FW] = {SMD_WCNSS, SMD_MODEM_Q6_FW},
+	[SMD_APPS_RPM] = {SMD_APPS, SMD_RPM},
+	[SMD_MODEM_RPM] = {SMD_MODEM, SMD_RPM},
+	[SMD_QDSP_RPM] = {SMD_Q6, SMD_RPM},
+	[SMD_WCNSS_RPM] = {SMD_WCNSS, SMD_RPM},
+	[SMD_TZ_RPM] = {SMD_TZ, SMD_RPM},
+};
+
+struct restart_notifier_block {
+	unsigned int processor;
+	char *name;
+	struct notifier_block nb;
+};
+
+static struct platform_device loopback_tty_pdev = {.name = "LOOPBACK_TTY"};
+
+static LIST_HEAD(smd_ch_closed_list);
+static LIST_HEAD(smd_ch_closing_list);
+static LIST_HEAD(smd_ch_to_close_list);
+
+struct remote_proc_info {
+	unsigned int remote_pid;
+	unsigned int free_space;
+	struct work_struct probe_work;
+	struct list_head ch_list;
+	/* 2 total supported tables of channels */
+	unsigned char ch_allocated[SMEM_NUM_SMD_STREAM_CHANNELS * 2];
+	bool skip_pil;
+};
+
+static struct remote_proc_info remote_info[NUM_SMD_SUBSYSTEMS];
+
+static void finalize_channel_close_fn(struct work_struct *work);
+static DECLARE_WORK(finalize_channel_close_work, finalize_channel_close_fn);
+static struct workqueue_struct *channel_close_wq;
+
+#define PRI_ALLOC_TBL 1
+#define SEC_ALLOC_TBL 2
+static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm, int table_id,
+				struct remote_proc_info *r_info);
+
+static bool smd_edge_inited(int edge)
+{
+	return edge_to_pids[edge].initialized;
+}
+
+/* on smp systems, the probe might get called from multiple cores,
+ * hence use a lock
+ */
+static DEFINE_MUTEX(smd_probe_lock);
+
+/**
+ * scan_alloc_table - Scans a specified SMD channel allocation table in SMEM for
+ *			newly created channels that need to be made locally
+ *			visable
+ *
+ * @shared: pointer to the table array in SMEM
+ * @smd_ch_allocated: pointer to an array indicating already allocated channels
+ * @table_id: identifier for this channel allocation table
+ * @num_entries: number of entries in this allocation table
+ * @r_info: pointer to the info structure of the remote proc we care about
+ *
+ * The smd_probe_lock must be locked by the calling function.  Shared and
+ * smd_ch_allocated are assumed to be valid pointers.
+ */
+static void scan_alloc_table(struct smd_alloc_elm *shared,
+				char *smd_ch_allocated,
+				int table_id,
+				unsigned int num_entries,
+				struct remote_proc_info *r_info)
+{
+	unsigned int n;
+	uint32_t type;
+
+	for (n = 0; n < num_entries; n++) {
+		if (smd_ch_allocated[n])
+			continue;
+
+		/*
+		 * channel should be allocated only if APPS processor is
+		 * involved
+		 */
+		type = SMD_CHANNEL_TYPE(shared[n].type);
+		if (!pid_is_on_edge(type, SMD_APPS) ||
+				!pid_is_on_edge(type, r_info->remote_pid))
+			continue;
+		if (!shared[n].ref_count)
+			continue;
+		if (!shared[n].name[0])
+			continue;
+
+		if (!smd_edge_inited(type)) {
+			SMD_INFO(
+				"Probe skipping proc %d, tbl %d, ch %d, edge not inited\n",
+				r_info->remote_pid, table_id, n);
+			continue;
+		}
+
+		if (!smd_alloc_channel(&shared[n], table_id, r_info))
+			smd_ch_allocated[n] = 1;
+		else
+			SMD_INFO(
+				"Probe skipping proc %d, tbl %d, ch %d, not allocated\n",
+				r_info->remote_pid, table_id, n);
+	}
+}
+
+static void smd_channel_probe_now(struct remote_proc_info *r_info)
+{
+	struct smd_alloc_elm *shared;
+	unsigned int tbl_size;
+
+	shared = smem_get_entry(ID_CH_ALLOC_TBL, &tbl_size,
+							r_info->remote_pid, 0);
+
+	if (!shared) {
+		pr_err("%s: allocation table not initialized\n", __func__);
+		return;
+	}
+
+	mutex_lock(&smd_probe_lock);
+
+	scan_alloc_table(shared, r_info->ch_allocated, PRI_ALLOC_TBL,
+						tbl_size / sizeof(*shared),
+						r_info);
+
+	shared = smem_get_entry(SMEM_CHANNEL_ALLOC_TBL_2, &tbl_size,
+							r_info->remote_pid, 0);
+	if (shared)
+		scan_alloc_table(shared,
+			&(r_info->ch_allocated[SMEM_NUM_SMD_STREAM_CHANNELS]),
+			SEC_ALLOC_TBL,
+			tbl_size / sizeof(*shared),
+			r_info);
+
+	mutex_unlock(&smd_probe_lock);
+}
+
+/**
+ * smd_channel_probe_worker() - Scan for newly created SMD channels and init
+ *				local structures so the channels are visable to
+ *				local clients
+ *
+ * @work: work_struct corresponding to an instance of this function running on
+ *		a workqueue.
+ */
+static void smd_channel_probe_worker(struct work_struct *work)
+{
+	struct remote_proc_info *r_info;
+
+	r_info = container_of(work, struct remote_proc_info, probe_work);
+
+	smd_channel_probe_now(r_info);
+}
+
+/**
+ * get_remote_ch() - gathers remote channel info
+ *
+ * @shared2:   Pointer to v2 shared channel structure
+ * @type:      Edge type
+ * @pid:       Processor ID of processor on edge
+ * @remote_ch:  Channel that belongs to processor @pid
+ * @is_word_access_ch: Bool, is this a word aligned access channel
+ *
+ * @returns:		0 on success, error code on failure
+ */
+static int get_remote_ch(void *shared2,
+		uint32_t type, uint32_t pid,
+		void **remote_ch,
+		int is_word_access_ch
+		)
+{
+	if (!remote_ch || !shared2 || !pid_is_on_edge(type, pid) ||
+				!pid_is_on_edge(type, SMD_APPS))
+		return -EINVAL;
+
+	if (is_word_access_ch)
+		*remote_ch =
+			&((struct smd_shared_word_access *)(shared2))->ch1;
+	else
+		*remote_ch = &((struct smd_shared *)(shared2))->ch1;
+
+	return 0;
+}
+
+/**
+ * smd_remote_ss_to_edge() - return edge type from remote ss type
+ * @name:	remote subsystem name
+ *
+ * Returns the edge type connected between the local subsystem(APPS)
+ * and remote subsystem @name.
+ */
+int smd_remote_ss_to_edge(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(edge_to_pids); ++i) {
+		if (edge_to_pids[i].subsys_name[0] != 0x0) {
+			if (!strcmp(edge_to_pids[i].subsys_name, name))
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(smd_remote_ss_to_edge);
+
+/**
+ * smd_edge_to_pil_str - Returns the PIL string used to load the remote side of
+ *			 the indicated edge.
+ *
+ * @type -	Edge definition
+ * @returns -	The PIL string to load the remove side of @type or NULL if the
+ *		PIL string does not exist.
+ */
+const char *smd_edge_to_pil_str(uint32_t type)
+{
+	const char *pil_str = NULL;
+
+	if (type < ARRAY_SIZE(edge_to_pids)) {
+		if (!edge_to_pids[type].initialized)
+			return ERR_PTR(-EPROBE_DEFER);
+		if (!remote_info[smd_edge_to_remote_pid(type)].skip_pil) {
+			pil_str = edge_to_pids[type].subsys_name;
+			if (pil_str[0] == 0x0)
+				pil_str = NULL;
+		}
+	}
+	return pil_str;
+}
+EXPORT_SYMBOL(smd_edge_to_pil_str);
+
+/*
+ * Returns a pointer to the subsystem name or NULL if no
+ * subsystem name is available.
+ *
+ * @type - Edge definition
+ */
+const char *smd_edge_to_subsystem(uint32_t type)
+{
+	const char *subsys = NULL;
+
+	if (type < ARRAY_SIZE(edge_to_pids)) {
+		subsys = edge_to_pids[type].subsys_name;
+		if (subsys[0] == 0x0)
+			subsys = NULL;
+		if (!edge_to_pids[type].initialized)
+			subsys = ERR_PTR(-EPROBE_DEFER);
+	}
+	return subsys;
+}
+EXPORT_SYMBOL(smd_edge_to_subsystem);
+
+/*
+ * Returns a pointer to the subsystem name given the
+ * remote processor ID.
+ * subsystem is not necessarily PIL-loadable
+ *
+ * @pid     Remote processor ID
+ * @returns Pointer to subsystem name or NULL if not found
+ */
+const char *smd_pid_to_subsystem(uint32_t pid)
+{
+	const char *subsys = NULL;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(edge_to_pids); ++i) {
+		if (pid == edge_to_pids[i].remote_pid) {
+			if (!edge_to_pids[i].initialized) {
+				subsys = ERR_PTR(-EPROBE_DEFER);
+				break;
+			}
+			if (edge_to_pids[i].subsys_name[0] != 0x0) {
+				subsys = edge_to_pids[i].subsys_name;
+				break;
+			} else if (pid == SMD_RPM) {
+				subsys = "rpm";
+				break;
+			}
+		}
+	}
+
+	return subsys;
+}
+EXPORT_SYMBOL(smd_pid_to_subsystem);
+
+static void smd_reset_edge(void *void_ch, unsigned int new_state,
+				int is_word_access_ch)
+{
+	if (is_word_access_ch) {
+		struct smd_half_channel_word_access *ch =
+			(struct smd_half_channel_word_access *)(void_ch);
+		if (ch->state != SMD_SS_CLOSED) {
+			ch->state = new_state;
+			ch->fDSR = 0;
+			ch->fCTS = 0;
+			ch->fCD = 0;
+			ch->fSTATE = 1;
+		}
+	} else {
+		struct smd_half_channel *ch =
+			(struct smd_half_channel *)(void_ch);
+		if (ch->state != SMD_SS_CLOSED) {
+			ch->state = new_state;
+			ch->fDSR = 0;
+			ch->fCTS = 0;
+			ch->fCD = 0;
+			ch->fSTATE = 1;
+		}
+	}
+}
+
+/**
+ * smd_channel_reset_state() - find channels in an allocation table and set them
+ *				to the specified state
+ *
+ * @shared:	Pointer to the allocation table to scan
+ * @table_id:	ID of the table
+ * @new_state:	New state that channels should be set to
+ * @pid:	Processor ID of the remote processor for the channels
+ * @num_entries: Number of entries in the table
+ *
+ * Scan the indicated table for channels between Apps and @pid.  If a valid
+ * channel is found, set the remote side of the channel to @new_state.
+ */
+static void smd_channel_reset_state(struct smd_alloc_elm *shared, int table_id,
+		unsigned int new_state, unsigned int pid,
+		unsigned int num_entries)
+{
+	unsigned int n;
+	void *shared2;
+	uint32_t type;
+	void *remote_ch;
+	int is_word_access;
+	unsigned int base_id;
+
+	switch (table_id) {
+	case PRI_ALLOC_TBL:
+		base_id = SMEM_SMD_BASE_ID;
+		break;
+	case SEC_ALLOC_TBL:
+		base_id = SMEM_SMD_BASE_ID_2;
+		break;
+	default:
+		SMD_INFO("%s: invalid table_id:%d\n", __func__, table_id);
+		return;
+	}
+
+	for (n = 0; n < num_entries; n++) {
+		if (!shared[n].ref_count)
+			continue;
+		if (!shared[n].name[0])
+			continue;
+
+		type = SMD_CHANNEL_TYPE(shared[n].type);
+		is_word_access = is_word_access_ch(type);
+		if (is_word_access)
+			shared2 = smem_find(base_id + n,
+				sizeof(struct smd_shared_word_access), pid,
+				0);
+		else
+			shared2 = smem_find(base_id + n,
+				sizeof(struct smd_shared), pid, 0);
+		if (!shared2)
+			continue;
+
+		if (!get_remote_ch(shared2, type, pid,
+					&remote_ch, is_word_access))
+			smd_reset_edge(remote_ch, new_state, is_word_access);
+	}
+}
+
+/**
+ * pid_is_on_edge() - checks to see if the processor with id pid is on the
+ * edge specified by edge_num
+ *
+ * @edge_num:		the number of the edge which is being tested
+ * @pid:		the id of the processor being tested
+ *
+ * @returns:		true if on edge, false otherwise
+ */
+static bool pid_is_on_edge(uint32_t edge_num, unsigned int pid)
+{
+	struct edge_to_pid edge;
+
+	if (edge_num >= ARRAY_SIZE(edge_to_pids))
+		return 0;
+
+	edge = edge_to_pids[edge_num];
+	return (edge.local_pid == pid || edge.remote_pid == pid);
+}
+
+void smd_channel_reset(uint32_t restart_pid)
+{
+	struct smd_alloc_elm *shared_pri;
+	struct smd_alloc_elm *shared_sec;
+	unsigned long flags;
+	unsigned int pri_size;
+	unsigned int sec_size;
+
+	SMD_POWER_INFO("%s: starting reset\n", __func__);
+
+	shared_pri = smem_get_entry(ID_CH_ALLOC_TBL, &pri_size,	restart_pid, 0);
+	if (!shared_pri) {
+		pr_err("%s: allocation table not initialized\n", __func__);
+		return;
+	}
+	shared_sec = smem_get_entry(SMEM_CHANNEL_ALLOC_TBL_2, &sec_size,
+								restart_pid, 0);
+
+	/* reset SMSM entry */
+	if (smsm_info.state) {
+		writel_relaxed(0, SMSM_STATE_ADDR(restart_pid));
+
+		/* restart SMSM init handshake */
+		if (restart_pid == SMSM_MODEM) {
+			smsm_change_state(SMSM_APPS_STATE,
+				SMSM_INIT | SMSM_SMD_LOOPBACK | SMSM_RESET,
+				0);
+		}
+
+		/* notify SMSM processors */
+		smsm_irq_handler(0, 0);
+		notify_modem_smsm();
+		notify_dsp_smsm();
+		notify_dsps_smsm();
+		notify_wcnss_smsm();
+	}
+
+	/* change all remote states to CLOSING */
+	mutex_lock(&smd_probe_lock);
+	spin_lock_irqsave(&smd_lock, flags);
+	smd_channel_reset_state(shared_pri, PRI_ALLOC_TBL, SMD_SS_CLOSING,
+				restart_pid, pri_size / sizeof(*shared_pri));
+	if (shared_sec)
+		smd_channel_reset_state(shared_sec, SEC_ALLOC_TBL,
+						SMD_SS_CLOSING, restart_pid,
+						sec_size / sizeof(*shared_sec));
+	spin_unlock_irqrestore(&smd_lock, flags);
+	mutex_unlock(&smd_probe_lock);
+
+	mb(); /* Make sure memory is visible before proceeding */
+	smd_fake_irq_handler(0);
+
+	/* change all remote states to CLOSED */
+	mutex_lock(&smd_probe_lock);
+	spin_lock_irqsave(&smd_lock, flags);
+	smd_channel_reset_state(shared_pri, PRI_ALLOC_TBL, SMD_SS_CLOSED,
+				restart_pid, pri_size / sizeof(*shared_pri));
+	if (shared_sec)
+		smd_channel_reset_state(shared_sec, SEC_ALLOC_TBL,
+						SMD_SS_CLOSED, restart_pid,
+						sec_size / sizeof(*shared_sec));
+	spin_unlock_irqrestore(&smd_lock, flags);
+	mutex_unlock(&smd_probe_lock);
+
+	mb(); /* Make sure memory is visible before proceeding */
+	smd_fake_irq_handler(0);
+
+	SMD_POWER_INFO("%s: finished reset\n", __func__);
+}
+
+/* how many bytes are available for reading */
+static int smd_stream_read_avail(struct smd_channel *ch)
+{
+	unsigned int head = ch->half_ch->get_head(ch->recv);
+	unsigned int tail = ch->half_ch->get_tail(ch->recv);
+	unsigned int fifo_size = ch->fifo_size;
+	unsigned int bytes_avail = head - tail;
+
+	if (head < tail)
+		bytes_avail += fifo_size;
+
+	WARN_ON(bytes_avail >= fifo_size);
+	return bytes_avail;
+}
+
+/* how many bytes we are free to write */
+static int smd_stream_write_avail(struct smd_channel *ch)
+{
+	unsigned int head = ch->half_ch->get_head(ch->send);
+	unsigned int tail = ch->half_ch->get_tail(ch->send);
+	unsigned int fifo_size = ch->fifo_size;
+	unsigned int bytes_avail = tail - head;
+
+	if (tail <= head)
+		bytes_avail += fifo_size;
+	if (bytes_avail < SMD_FIFO_FULL_RESERVE)
+		bytes_avail = 0;
+	else
+		bytes_avail -= SMD_FIFO_FULL_RESERVE;
+
+	WARN_ON(bytes_avail >= fifo_size);
+	return bytes_avail;
+}
+
+static int smd_packet_read_avail(struct smd_channel *ch)
+{
+	if (ch->current_packet) {
+		int n = smd_stream_read_avail(ch);
+
+		if (n > ch->current_packet)
+			n = ch->current_packet;
+		return n;
+	} else {
+		return 0;
+	}
+}
+
+static int smd_packet_write_avail(struct smd_channel *ch)
+{
+	int n = smd_stream_write_avail(ch);
+
+	return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0;
+}
+
+static int ch_is_open(struct smd_channel *ch)
+{
+	return (ch->half_ch->get_state(ch->recv) == SMD_SS_OPENED ||
+		ch->half_ch->get_state(ch->recv) == SMD_SS_FLUSHING)
+		&& (ch->half_ch->get_state(ch->send) == SMD_SS_OPENED);
+}
+
+/* provide a pointer and length to readable data in the fifo */
+static unsigned int ch_read_buffer(struct smd_channel *ch, void **ptr)
+{
+	unsigned int head = ch->half_ch->get_head(ch->recv);
+	unsigned int tail = ch->half_ch->get_tail(ch->recv);
+	unsigned int fifo_size = ch->fifo_size;
+
+	WARN_ON(fifo_size >= SZ_1M);
+	WARN_ON(head >= fifo_size);
+	WARN_ON(tail >= fifo_size);
+	WARN_ON(OVERFLOW_ADD_UNSIGNED(uintptr_t, (uintptr_t)ch->recv_data,
+								tail));
+	*ptr = (void *) (ch->recv_data + tail);
+	if (tail <= head)
+		return head - tail;
+	else
+		return fifo_size - tail;
+}
+
+static int read_intr_blocked(struct smd_channel *ch)
+{
+	return ch->half_ch->get_fBLOCKREADINTR(ch->recv);
+}
+
+/* advance the fifo read pointer after data from ch_read_buffer is consumed */
+static void ch_read_done(struct smd_channel *ch, unsigned int count)
+{
+	unsigned int tail = ch->half_ch->get_tail(ch->recv);
+	unsigned int fifo_size = ch->fifo_size;
+
+	WARN_ON(count > smd_stream_read_avail(ch));
+
+	tail += count;
+	if (tail >= fifo_size)
+		tail -= fifo_size;
+	ch->half_ch->set_tail(ch->recv, tail);
+	wmb(); /* Make sure memory is visible before setting signal */
+	ch->half_ch->set_fTAIL(ch->send,  1);
+}
+
+/* basic read interface to ch_read_{buffer,done} used
+ * by smd_*_read() and update_packet_state()
+ * will read-and-discard if the _data pointer is null
+ */
+static int ch_read(struct smd_channel *ch, void *_data, int len)
+{
+	void *ptr;
+	unsigned int n;
+	unsigned char *data = _data;
+	int orig_len = len;
+
+	while (len > 0) {
+		n = ch_read_buffer(ch, &ptr);
+		if (n == 0)
+			break;
+
+		if (n > len)
+			n = len;
+		if (_data)
+			ch->read_from_fifo(data, ptr, n);
+
+		data += n;
+		len -= n;
+		ch_read_done(ch, n);
+	}
+
+	return orig_len - len;
+}
+
+static void update_stream_state(struct smd_channel *ch)
+{
+	/* streams have no special state requiring updating */
+}
+
+static void update_packet_state(struct smd_channel *ch)
+{
+	unsigned int hdr[5];
+	int r;
+	const char *peripheral = NULL;
+
+	/* can't do anything if we're in the middle of a packet */
+	while (ch->current_packet == 0) {
+		/* discard 0 length packets if any */
+
+		/* don't bother unless we can get the full header */
+		if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE)
+			return;
+
+		r = ch_read(ch, hdr, SMD_HEADER_SIZE);
+		WARN_ON(r != SMD_HEADER_SIZE);
+
+		ch->current_packet = hdr[0];
+		if (ch->current_packet > (uint32_t)INT_MAX) {
+			pr_err("%s: Invalid packet size of %d bytes detected. Edge: %d, Channel : %s, RPTR: %d, WPTR: %d",
+				__func__, ch->current_packet, ch->type,
+				ch->name, ch->half_ch->get_tail(ch->recv),
+				ch->half_ch->get_head(ch->recv));
+			peripheral = smd_edge_to_pil_str(ch->type);
+			if (peripheral) {
+				if (subsystem_restart(peripheral) < 0)
+					WARN_ON(1);
+			} else {
+				WARN_ON(1);
+			}
+		}
+	}
+}
+
+/**
+ * ch_write_buffer() - Provide a pointer and length for the next segment of
+ * free space in the FIFO.
+ * @ch: channel
+ * @ptr: Address to pointer for the next segment write
+ * @returns: Maximum size that can be written until the FIFO is either full
+ *           or the end of the FIFO has been reached.
+ *
+ * The returned pointer and length are passed to memcpy, so the next segment is
+ * defined as either the space available between the read index (tail) and the
+ * write index (head) or the space available to the end of the FIFO.
+ */
+static unsigned int ch_write_buffer(struct smd_channel *ch, void **ptr)
+{
+	unsigned int head = ch->half_ch->get_head(ch->send);
+	unsigned int tail = ch->half_ch->get_tail(ch->send);
+	unsigned int fifo_size = ch->fifo_size;
+
+	WARN_ON(fifo_size >= SZ_1M);
+	WARN_ON(head >= fifo_size);
+	WARN_ON(tail >= fifo_size);
+	WARN_ON(OVERFLOW_ADD_UNSIGNED(uintptr_t, (uintptr_t)ch->send_data,
+								head));
+
+	*ptr = (void *) (ch->send_data + head);
+	if (head < tail)
+		return tail - head - SMD_FIFO_FULL_RESERVE;
+
+	if (tail < SMD_FIFO_FULL_RESERVE)
+		return fifo_size + tail - head
+				- SMD_FIFO_FULL_RESERVE;
+
+	return fifo_size - head;
+
+}
+
+/* advace the fifo write pointer after freespace
+ * from ch_write_buffer is filled
+ */
+static void ch_write_done(struct smd_channel *ch, unsigned int count)
+{
+	unsigned int head = ch->half_ch->get_head(ch->send);
+	unsigned int fifo_size = ch->fifo_size;
+
+	WARN_ON(count > smd_stream_write_avail(ch));
+	head += count;
+	if (head >= fifo_size)
+		head -= fifo_size;
+	ch->half_ch->set_head(ch->send, head);
+	wmb();  /* Make sure memory is visible before setting signal */
+	ch->half_ch->set_fHEAD(ch->send, 1);
+}
+
+static void ch_set_state(struct smd_channel *ch, unsigned int n)
+{
+	if (n == SMD_SS_OPENED) {
+		ch->half_ch->set_fDSR(ch->send, 1);
+		ch->half_ch->set_fCTS(ch->send, 1);
+		ch->half_ch->set_fCD(ch->send, 1);
+	} else {
+		ch->half_ch->set_fDSR(ch->send, 0);
+		ch->half_ch->set_fCTS(ch->send, 0);
+		ch->half_ch->set_fCD(ch->send, 0);
+	}
+	ch->half_ch->set_state(ch->send, n);
+	ch->half_ch->set_fSTATE(ch->send, 1);
+	ch->notify_other_cpu(ch);
+}
+
+/**
+ * do_smd_probe() - Look for newly created SMD channels a specific processor
+ *
+ * @remote_pid: remote processor id of the proc that may have created channels
+ */
+static void do_smd_probe(unsigned int remote_pid)
+{
+	unsigned int free_space;
+
+	free_space = smem_get_free_space(remote_pid);
+	if (free_space != remote_info[remote_pid].free_space) {
+		remote_info[remote_pid].free_space = free_space;
+		schedule_work(&remote_info[remote_pid].probe_work);
+	}
+}
+
+static void remote_processed_close(struct smd_channel *ch)
+{
+	/* The remote side has observed our close, we can allow a reopen */
+	list_move(&ch->ch_list, &smd_ch_to_close_list);
+	queue_work(channel_close_wq, &finalize_channel_close_work);
+}
+
+static void smd_state_change(struct smd_channel *ch,
+			     unsigned int last, unsigned int next)
+{
+	ch->last_state = next;
+
+	SMD_INFO("SMD: ch %d %d -> %d\n", ch->n, last, next);
+
+	switch (next) {
+	case SMD_SS_OPENING:
+		if (last == SMD_SS_OPENED &&
+		    ch->half_ch->get_state(ch->send) == SMD_SS_CLOSED) {
+			/* We missed the CLOSING and CLOSED states */
+			remote_processed_close(ch);
+		} else if (ch->half_ch->get_state(ch->send) == SMD_SS_CLOSING ||
+		    ch->half_ch->get_state(ch->send) == SMD_SS_CLOSED) {
+			ch->half_ch->set_tail(ch->recv, 0);
+			ch->half_ch->set_head(ch->send, 0);
+			ch->half_ch->set_fBLOCKREADINTR(ch->send, 0);
+			ch->current_packet = 0;
+			ch_set_state(ch, SMD_SS_OPENING);
+		}
+		break;
+	case SMD_SS_OPENED:
+		if (ch->half_ch->get_state(ch->send) == SMD_SS_OPENING) {
+			ch_set_state(ch, SMD_SS_OPENED);
+			ch->notify(ch->priv, SMD_EVENT_OPEN);
+		}
+		break;
+	case SMD_SS_FLUSHING:
+	case SMD_SS_RESET:
+		/* we should force them to close? */
+		break;
+	case SMD_SS_CLOSED:
+		if (ch->half_ch->get_state(ch->send) == SMD_SS_OPENED) {
+			ch_set_state(ch, SMD_SS_CLOSING);
+			ch->pending_pkt_sz = 0;
+			ch->notify(ch->priv, SMD_EVENT_CLOSE);
+		}
+		/* We missed the CLOSING state */
+		if (ch->half_ch->get_state(ch->send) == SMD_SS_CLOSED)
+			remote_processed_close(ch);
+		break;
+	case SMD_SS_CLOSING:
+		if (ch->half_ch->get_state(ch->send) == SMD_SS_CLOSED)
+			remote_processed_close(ch);
+		break;
+	}
+}
+
+static void handle_smd_irq_closing_list(void)
+{
+	unsigned long flags;
+	struct smd_channel *ch;
+	struct smd_channel *index;
+	unsigned int tmp;
+
+	spin_lock_irqsave(&smd_lock, flags);
+	list_for_each_entry_safe(ch, index, &smd_ch_closing_list, ch_list) {
+		if (ch->half_ch->get_fSTATE(ch->recv))
+			ch->half_ch->set_fSTATE(ch->recv, 0);
+		tmp = ch->half_ch->get_state(ch->recv);
+		if (tmp != ch->last_state)
+			smd_state_change(ch, ch->last_state, tmp);
+	}
+	spin_unlock_irqrestore(&smd_lock, flags);
+}
+
+static void handle_smd_irq(struct remote_proc_info *r_info,
+		void (*notify)(smd_channel_t *ch))
+{
+	unsigned long flags;
+	struct smd_channel *ch;
+	unsigned int ch_flags;
+	unsigned int tmp;
+	unsigned char state_change;
+	struct list_head *list;
+
+	list = &r_info->ch_list;
+
+	spin_lock_irqsave(&smd_lock, flags);
+	list_for_each_entry(ch, list, ch_list) {
+		state_change = 0;
+		ch_flags = 0;
+		if (ch_is_open(ch)) {
+			if (ch->half_ch->get_fHEAD(ch->recv)) {
+				ch->half_ch->set_fHEAD(ch->recv, 0);
+				ch_flags |= 1;
+			}
+			if (ch->half_ch->get_fTAIL(ch->recv)) {
+				ch->half_ch->set_fTAIL(ch->recv, 0);
+				ch_flags |= 2;
+			}
+			if (ch->half_ch->get_fSTATE(ch->recv)) {
+				ch->half_ch->set_fSTATE(ch->recv, 0);
+				ch_flags |= 4;
+			}
+		}
+		tmp = ch->half_ch->get_state(ch->recv);
+		if (tmp != ch->last_state) {
+			SMD_POWER_INFO("SMD ch%d '%s' State change %d->%d\n",
+					ch->n, ch->name, ch->last_state, tmp);
+			smd_state_change(ch, ch->last_state, tmp);
+			state_change = 1;
+		}
+		if (ch_flags & 0x3) {
+			ch->update_state(ch);
+			SMD_POWER_INFO(
+				"SMD ch%d '%s' Data event 0x%x tx%d/rx%d %dr/%dw : %dr/%dw\n",
+				ch->n, ch->name,
+				ch_flags,
+				ch->fifo_size -
+					(smd_stream_write_avail(ch) + 1),
+				smd_stream_read_avail(ch),
+				ch->half_ch->get_tail(ch->send),
+				ch->half_ch->get_head(ch->send),
+				ch->half_ch->get_tail(ch->recv),
+				ch->half_ch->get_head(ch->recv)
+				);
+			ch->notify(ch->priv, SMD_EVENT_DATA);
+		}
+		if (ch_flags & 0x4 && !state_change) {
+			SMD_POWER_INFO("SMD ch%d '%s' State update\n",
+					ch->n, ch->name);
+			ch->notify(ch->priv, SMD_EVENT_STATUS);
+		}
+	}
+	spin_unlock_irqrestore(&smd_lock, flags);
+	do_smd_probe(r_info->remote_pid);
+}
+
+static inline void log_irq(uint32_t subsystem)
+{
+	const char *subsys = smd_edge_to_subsystem(subsystem);
+
+	(void) subsys;
+
+	SMD_POWER_INFO("SMD Int %s->Apps\n", subsys);
+}
+
+irqreturn_t smd_modem_irq_handler(int irq, void *data)
+{
+	if (unlikely(!edge_to_pids[SMD_APPS_MODEM].initialized))
+		return IRQ_HANDLED;
+	log_irq(SMD_APPS_MODEM);
+	++interrupt_stats[SMD_MODEM].smd_in_count;
+	handle_smd_irq(&remote_info[SMD_MODEM], notify_modem_smd);
+	handle_smd_irq_closing_list();
+	return IRQ_HANDLED;
+}
+
+irqreturn_t smd_dsp_irq_handler(int irq, void *data)
+{
+	if (unlikely(!edge_to_pids[SMD_APPS_QDSP].initialized))
+		return IRQ_HANDLED;
+	log_irq(SMD_APPS_QDSP);
+	++interrupt_stats[SMD_Q6].smd_in_count;
+	handle_smd_irq(&remote_info[SMD_Q6], notify_dsp_smd);
+	handle_smd_irq_closing_list();
+	return IRQ_HANDLED;
+}
+
+irqreturn_t smd_dsps_irq_handler(int irq, void *data)
+{
+	if (unlikely(!edge_to_pids[SMD_APPS_DSPS].initialized))
+		return IRQ_HANDLED;
+	log_irq(SMD_APPS_DSPS);
+	++interrupt_stats[SMD_DSPS].smd_in_count;
+	handle_smd_irq(&remote_info[SMD_DSPS], notify_dsps_smd);
+	handle_smd_irq_closing_list();
+	return IRQ_HANDLED;
+}
+
+irqreturn_t smd_wcnss_irq_handler(int irq, void *data)
+{
+	if (unlikely(!edge_to_pids[SMD_APPS_WCNSS].initialized))
+		return IRQ_HANDLED;
+	log_irq(SMD_APPS_WCNSS);
+	++interrupt_stats[SMD_WCNSS].smd_in_count;
+	handle_smd_irq(&remote_info[SMD_WCNSS], notify_wcnss_smd);
+	handle_smd_irq_closing_list();
+	return IRQ_HANDLED;
+}
+
+irqreturn_t smd_modemfw_irq_handler(int irq, void *data)
+{
+	if (unlikely(!edge_to_pids[SMD_APPS_Q6FW].initialized))
+		return IRQ_HANDLED;
+	log_irq(SMD_APPS_Q6FW);
+	++interrupt_stats[SMD_MODEM_Q6_FW].smd_in_count;
+	handle_smd_irq(&remote_info[SMD_MODEM_Q6_FW], notify_modemfw_smd);
+	handle_smd_irq_closing_list();
+	return IRQ_HANDLED;
+}
+
+irqreturn_t smd_rpm_irq_handler(int irq, void *data)
+{
+	if (unlikely(!edge_to_pids[SMD_APPS_RPM].initialized))
+		return IRQ_HANDLED;
+	log_irq(SMD_APPS_RPM);
+	++interrupt_stats[SMD_RPM].smd_in_count;
+	handle_smd_irq(&remote_info[SMD_RPM], notify_rpm_smd);
+	handle_smd_irq_closing_list();
+	return IRQ_HANDLED;
+}
+
+static void smd_fake_irq_handler(unsigned long arg)
+{
+	handle_smd_irq(&remote_info[SMD_MODEM], notify_modem_smd);
+	handle_smd_irq(&remote_info[SMD_Q6], notify_dsp_smd);
+	handle_smd_irq(&remote_info[SMD_DSPS], notify_dsps_smd);
+	handle_smd_irq(&remote_info[SMD_WCNSS], notify_wcnss_smd);
+	handle_smd_irq(&remote_info[SMD_MODEM_Q6_FW], notify_modemfw_smd);
+	handle_smd_irq(&remote_info[SMD_RPM], notify_rpm_smd);
+	handle_smd_irq_closing_list();
+}
+
+static int smd_is_packet(struct smd_alloc_elm *alloc_elm)
+{
+	if (SMD_XFER_TYPE(alloc_elm->type) == 1)
+		return 0;
+	else if (SMD_XFER_TYPE(alloc_elm->type) == 2)
+		return 1;
+
+	panic("Unsupported SMD xfer type: %d name:%s edge:%d\n",
+					SMD_XFER_TYPE(alloc_elm->type),
+					alloc_elm->name,
+					SMD_CHANNEL_TYPE(alloc_elm->type));
+}
+
+static int smd_stream_write(smd_channel_t *ch, const void *_data, int len,
+				bool intr_ntfy)
+{
+	void *ptr;
+	const unsigned char *buf = _data;
+	unsigned int xfer;
+	int orig_len = len;
+
+	SMD_DBG("smd_stream_write() %d -> ch%d\n", len, ch->n);
+	if (len < 0)
+		return -EINVAL;
+	else if (len == 0)
+		return 0;
+
+	while ((xfer = ch_write_buffer(ch, &ptr)) != 0) {
+		if (!ch_is_open(ch)) {
+			len = orig_len;
+			break;
+		}
+		if (xfer > len)
+			xfer = len;
+
+		ch->write_to_fifo(ptr, buf, xfer);
+		ch_write_done(ch, xfer);
+		len -= xfer;
+		buf += xfer;
+		if (len == 0)
+			break;
+	}
+
+	if (orig_len - len && intr_ntfy)
+		ch->notify_other_cpu(ch);
+
+	return orig_len - len;
+}
+
+static int smd_packet_write(smd_channel_t *ch, const void *_data, int len,
+				bool intr_ntfy)
+{
+	int ret;
+	unsigned int hdr[5];
+
+	SMD_DBG("smd_packet_write() %d -> ch%d\n", len, ch->n);
+	if (len < 0)
+		return -EINVAL;
+	else if (len == 0)
+		return 0;
+
+	if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE))
+		return -ENOMEM;
+
+	hdr[0] = len;
+	hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
+
+
+	ret = smd_stream_write(ch, hdr, sizeof(hdr), false);
+	if (ret < 0 || ret != sizeof(hdr)) {
+		SMD_DBG("%s failed to write pkt header: %d returned\n",
+								__func__, ret);
+		return -EFAULT;
+	}
+
+
+	ret = smd_stream_write(ch, _data, len, true);
+	if (ret < 0 || ret != len) {
+		SMD_DBG("%s failed to write pkt data: %d returned\n",
+								__func__, ret);
+		return ret;
+	}
+
+	return len;
+}
+
+static int smd_stream_read(smd_channel_t *ch, void *data, int len)
+{
+	int r;
+
+	if (len < 0)
+		return -EINVAL;
+
+	r = ch_read(ch, data, len);
+	if (r > 0)
+		if (!read_intr_blocked(ch))
+			ch->notify_other_cpu(ch);
+
+	return r;
+}
+
+static int smd_packet_read(smd_channel_t *ch, void *data, int len)
+{
+	unsigned long flags;
+	int r;
+
+	if (len < 0)
+		return -EINVAL;
+
+	if (ch->current_packet > (uint32_t)INT_MAX) {
+		pr_err("%s: Invalid packet size for Edge %d and Channel %s",
+			__func__, ch->type, ch->name);
+		return -EFAULT;
+	}
+
+	if (len > ch->current_packet)
+		len = ch->current_packet;
+
+	r = ch_read(ch, data, len);
+	if (r > 0)
+		if (!read_intr_blocked(ch))
+			ch->notify_other_cpu(ch);
+
+	spin_lock_irqsave(&smd_lock, flags);
+	ch->current_packet -= r;
+	update_packet_state(ch);
+	spin_unlock_irqrestore(&smd_lock, flags);
+
+	return r;
+}
+
+static int smd_packet_read_from_cb(smd_channel_t *ch, void *data, int len)
+{
+	int r;
+
+	if (len < 0)
+		return -EINVAL;
+
+	if (ch->current_packet > (uint32_t)INT_MAX) {
+		pr_err("%s: Invalid packet size for Edge %d and Channel %s",
+			__func__, ch->type, ch->name);
+		return -EFAULT;
+	}
+
+	if (len > ch->current_packet)
+		len = ch->current_packet;
+
+	r = ch_read(ch, data, len);
+	if (r > 0)
+		if (!read_intr_blocked(ch))
+			ch->notify_other_cpu(ch);
+
+	ch->current_packet -= r;
+	update_packet_state(ch);
+
+	return r;
+}
+
+/**
+ * smd_alloc_v2() - Init local channel structure with information stored in SMEM
+ *
+ * @ch: pointer to the local structure for this channel
+ * @table_id: the id of the table this channel resides in. 1 = first table, 2 =
+ *		second table, etc
+ * @r_info: pointer to the info structure of the remote proc for this channel
+ * @returns: -EINVAL for failure; 0 for success
+ *
+ * ch must point to an allocated instance of struct smd_channel that is zeroed
+ * out, and has the n and type members already initialized to the correct values
+ */
+static int smd_alloc(struct smd_channel *ch, int table_id,
+						struct remote_proc_info *r_info)
+{
+	void *buffer;
+	unsigned int buffer_sz;
+	unsigned int base_id;
+	unsigned int fifo_id;
+
+	switch (table_id) {
+	case PRI_ALLOC_TBL:
+		base_id = SMEM_SMD_BASE_ID;
+		fifo_id = SMEM_SMD_FIFO_BASE_ID;
+		break;
+	case SEC_ALLOC_TBL:
+		base_id = SMEM_SMD_BASE_ID_2;
+		fifo_id = SMEM_SMD_FIFO_BASE_ID_2;
+		break;
+	default:
+		SMD_INFO("Invalid table_id:%d passed to smd_alloc\n", table_id);
+		return -EINVAL;
+	}
+
+	if (is_word_access_ch(ch->type)) {
+		struct smd_shared_word_access *shared2;
+
+		shared2 = smem_find(base_id + ch->n, sizeof(*shared2),
+							r_info->remote_pid, 0);
+		if (!shared2) {
+			SMD_INFO("smem_find failed ch=%d\n", ch->n);
+			return -EINVAL;
+		}
+		ch->send = &shared2->ch0;
+		ch->recv = &shared2->ch1;
+	} else {
+		struct smd_shared *shared2;
+
+		shared2 = smem_find(base_id + ch->n, sizeof(*shared2),
+							r_info->remote_pid, 0);
+		if (!shared2) {
+			SMD_INFO("smem_find failed ch=%d\n", ch->n);
+			return -EINVAL;
+		}
+		ch->send = &shared2->ch0;
+		ch->recv = &shared2->ch1;
+	}
+	ch->half_ch = get_half_ch_funcs(ch->type);
+
+	buffer = smem_get_entry(fifo_id + ch->n, &buffer_sz,
+							r_info->remote_pid, 0);
+	if (!buffer) {
+		SMD_INFO("smem_get_entry failed\n");
+		return -EINVAL;
+	}
+
+	/* buffer must be a multiple of 32 size */
+	if ((buffer_sz & (SZ_32 - 1)) != 0) {
+		SMD_INFO("Buffer size: %u not multiple of 32\n", buffer_sz);
+		return -EINVAL;
+	}
+	buffer_sz /= 2;
+	ch->send_data = buffer;
+	ch->recv_data = buffer + buffer_sz;
+	ch->fifo_size = buffer_sz;
+
+	return 0;
+}
+
+/**
+ * smd_alloc_channel() - Create and init local structures for a newly allocated
+ *			SMD channel
+ *
+ * @alloc_elm: the allocation element stored in SMEM for this channel
+ * @table_id: the id of the table this channel resides in. 1 = first table, 2 =
+ *		seconds table, etc
+ * @r_info: pointer to the info structure of the remote proc for this channel
+ * @returns: error code for failure; 0 for success
+ */
+static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm, int table_id,
+				struct remote_proc_info *r_info)
+{
+	struct smd_channel *ch;
+
+	ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
+	if (ch == 0) {
+		pr_err("smd_alloc_channel() out of memory\n");
+		return -ENOMEM;
+	}
+	ch->n = alloc_elm->cid;
+	ch->type = SMD_CHANNEL_TYPE(alloc_elm->type);
+
+	if (smd_alloc(ch, table_id, r_info)) {
+		kfree(ch);
+		return -ENODEV;
+	}
+
+	/* probe_worker guarentees ch->type will be a valid type */
+	if (ch->type == SMD_APPS_MODEM)
+		ch->notify_other_cpu = notify_modem_smd;
+	else if (ch->type == SMD_APPS_QDSP)
+		ch->notify_other_cpu = notify_dsp_smd;
+	else if (ch->type == SMD_APPS_DSPS)
+		ch->notify_other_cpu = notify_dsps_smd;
+	else if (ch->type == SMD_APPS_WCNSS)
+		ch->notify_other_cpu = notify_wcnss_smd;
+	else if (ch->type == SMD_APPS_Q6FW)
+		ch->notify_other_cpu = notify_modemfw_smd;
+	else if (ch->type == SMD_APPS_RPM)
+		ch->notify_other_cpu = notify_rpm_smd;
+
+	if (smd_is_packet(alloc_elm)) {
+		ch->read = smd_packet_read;
+		ch->write = smd_packet_write;
+		ch->read_avail = smd_packet_read_avail;
+		ch->write_avail = smd_packet_write_avail;
+		ch->update_state = update_packet_state;
+		ch->read_from_cb = smd_packet_read_from_cb;
+		ch->is_pkt_ch = 1;
+	} else {
+		ch->read = smd_stream_read;
+		ch->write = smd_stream_write;
+		ch->read_avail = smd_stream_read_avail;
+		ch->write_avail = smd_stream_write_avail;
+		ch->update_state = update_stream_state;
+		ch->read_from_cb = smd_stream_read;
+	}
+
+	if (is_word_access_ch(ch->type)) {
+		ch->read_from_fifo = smd_memcpy32_from_fifo;
+		ch->write_to_fifo = smd_memcpy32_to_fifo;
+	} else {
+		ch->read_from_fifo = smd_memcpy_from_fifo;
+		ch->write_to_fifo = smd_memcpy_to_fifo;
+	}
+
+	smd_memcpy_from_fifo(ch->name, alloc_elm->name, SMD_MAX_CH_NAME_LEN);
+	ch->name[SMD_MAX_CH_NAME_LEN-1] = 0;
+
+	ch->pdev.name = ch->name;
+	ch->pdev.id = ch->type;
+
+	SMD_INFO("smd_alloc_channel() '%s' cid=%d\n",
+		 ch->name, ch->n);
+
+	mutex_lock(&smd_creation_mutex);
+	list_add(&ch->ch_list, &smd_ch_closed_list);
+	mutex_unlock(&smd_creation_mutex);
+
+	platform_device_register(&ch->pdev);
+	if (!strcmp(ch->name, "LOOPBACK") && ch->type == SMD_APPS_MODEM) {
+		/* create a platform driver to be used by smd_tty driver
+		 * so that it can access the loopback port
+		 */
+		loopback_tty_pdev.id = ch->type;
+		platform_device_register(&loopback_tty_pdev);
+	}
+	return 0;
+}
+
+static void do_nothing_notify(void *priv, unsigned int flags)
+{
+}
+
+static void finalize_channel_close_fn(struct work_struct *work)
+{
+	unsigned long flags;
+	struct smd_channel *ch;
+	struct smd_channel *index;
+
+	mutex_lock(&smd_creation_mutex);
+	spin_lock_irqsave(&smd_lock, flags);
+	list_for_each_entry_safe(ch, index,  &smd_ch_to_close_list, ch_list) {
+		list_del(&ch->ch_list);
+		list_add(&ch->ch_list, &smd_ch_closed_list);
+		ch->notify(ch->priv, SMD_EVENT_REOPEN_READY);
+		ch->notify = do_nothing_notify;
+	}
+	spin_unlock_irqrestore(&smd_lock, flags);
+	mutex_unlock(&smd_creation_mutex);
+}
+
+struct smd_channel *smd_get_channel(const char *name, uint32_t type)
+{
+	struct smd_channel *ch;
+
+	mutex_lock(&smd_creation_mutex);
+	list_for_each_entry(ch, &smd_ch_closed_list, ch_list) {
+		if (!strcmp(name, ch->name) &&
+			(type == ch->type)) {
+			list_del(&ch->ch_list);
+			mutex_unlock(&smd_creation_mutex);
+			return ch;
+		}
+	}
+	mutex_unlock(&smd_creation_mutex);
+
+	return NULL;
+}
+
+int smd_named_open_on_edge(const char *name, uint32_t edge,
+			   smd_channel_t **_ch,
+			   void *priv, void (*notify)(void *, unsigned int))
+{
+	struct smd_channel *ch;
+	unsigned long flags;
+
+	if (edge >= SMD_NUM_TYPE) {
+		pr_err("%s: edge:%d is invalid\n", __func__, edge);
+		return -EINVAL;
+	}
+
+	if (!smd_edge_inited(edge)) {
+		SMD_INFO("smd_open() before smd_init()\n");
+		return -EPROBE_DEFER;
+	}
+
+	SMD_DBG("smd_open('%s', %p, %p)\n", name, priv, notify);
+
+	ch = smd_get_channel(name, edge);
+	if (!ch) {
+		spin_lock_irqsave(&smd_lock, flags);
+		/* check opened list for port */
+		list_for_each_entry(ch,
+			&remote_info[edge_to_pids[edge].remote_pid].ch_list,
+			ch_list) {
+			if (!strcmp(name, ch->name)) {
+				/* channel is already open */
+				spin_unlock_irqrestore(&smd_lock, flags);
+				SMD_DBG("smd_open: channel '%s' already open\n",
+					ch->name);
+				return -EBUSY;
+			}
+		}
+
+		/* check closing list for port */
+		list_for_each_entry(ch, &smd_ch_closing_list, ch_list) {
+			if (!strcmp(name, ch->name) && (edge == ch->type)) {
+				/* channel exists, but is being closed */
+				spin_unlock_irqrestore(&smd_lock, flags);
+				return -EAGAIN;
+			}
+		}
+
+		/* check closing workqueue list for port */
+		list_for_each_entry(ch, &smd_ch_to_close_list, ch_list) {
+			if (!strcmp(name, ch->name) && (edge == ch->type)) {
+				/* channel exists, but is being closed */
+				spin_unlock_irqrestore(&smd_lock, flags);
+				return -EAGAIN;
+			}
+		}
+		spin_unlock_irqrestore(&smd_lock, flags);
+
+		/* one final check to handle closing->closed race condition */
+		ch = smd_get_channel(name, edge);
+		if (!ch)
+			return -ENODEV;
+	}
+
+	if (ch->half_ch->get_fSTATE(ch->send)) {
+		/* remote side hasn't acknowledged our last state transition */
+		SMD_INFO("%s: ch %d valid, waiting for remote to ack state\n",
+				__func__, ch->n);
+		msleep(250);
+		if (ch->half_ch->get_fSTATE(ch->send))
+			SMD_INFO("%s: ch %d - no remote ack, continuing\n",
+					__func__, ch->n);
+	}
+
+	if (notify == 0)
+		notify = do_nothing_notify;
+
+	ch->notify = notify;
+	ch->current_packet = 0;
+	ch->last_state = SMD_SS_CLOSED;
+	ch->priv = priv;
+
+	*_ch = ch;
+
+	SMD_DBG("smd_open: opening '%s'\n", ch->name);
+
+	spin_lock_irqsave(&smd_lock, flags);
+	list_add(&ch->ch_list,
+		       &remote_info[edge_to_pids[ch->type].remote_pid].ch_list);
+
+	SMD_DBG("%s: opening ch %d\n", __func__, ch->n);
+
+	smd_state_change(ch, ch->last_state, SMD_SS_OPENING);
+
+	spin_unlock_irqrestore(&smd_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(smd_named_open_on_edge);
+
+int smd_close(smd_channel_t *ch)
+{
+	unsigned long flags;
+	bool was_opened;
+
+	if (ch == 0)
+		return -EINVAL;
+
+	SMD_INFO("smd_close(%s)\n", ch->name);
+
+	spin_lock_irqsave(&smd_lock, flags);
+	list_del(&ch->ch_list);
+
+	was_opened = ch->half_ch->get_state(ch->recv) == SMD_SS_OPENED;
+	ch_set_state(ch, SMD_SS_CLOSED);
+
+	if (was_opened) {
+		list_add(&ch->ch_list, &smd_ch_closing_list);
+		spin_unlock_irqrestore(&smd_lock, flags);
+	} else {
+		spin_unlock_irqrestore(&smd_lock, flags);
+		ch->notify = do_nothing_notify;
+		mutex_lock(&smd_creation_mutex);
+		list_add(&ch->ch_list, &smd_ch_closed_list);
+		mutex_unlock(&smd_creation_mutex);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(smd_close);
+
+int smd_write_start(smd_channel_t *ch, int len)
+{
+	int ret;
+	unsigned int hdr[5];
+
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+	if (!ch->is_pkt_ch) {
+		pr_err("%s: non-packet channel specified\n", __func__);
+		return -EACCES;
+	}
+	if (len < 1) {
+		pr_err("%s: invalid length: %d\n", __func__, len);
+		return -EINVAL;
+	}
+
+	if (ch->pending_pkt_sz) {
+		pr_err("%s: packet of size: %d in progress\n", __func__,
+			ch->pending_pkt_sz);
+		return -EBUSY;
+	}
+	ch->pending_pkt_sz = len;
+
+	if (smd_stream_write_avail(ch) < (SMD_HEADER_SIZE)) {
+		ch->pending_pkt_sz = 0;
+		SMD_DBG("%s: no space to write packet header\n", __func__);
+		return -EAGAIN;
+	}
+
+	hdr[0] = len;
+	hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
+
+
+	ret = smd_stream_write(ch, hdr, sizeof(hdr), true);
+	if (ret < 0 || ret != sizeof(hdr)) {
+		ch->pending_pkt_sz = 0;
+		pr_err("%s: packet header failed to write\n", __func__);
+		return -EPERM;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(smd_write_start);
+
+int smd_write_segment(smd_channel_t *ch, const void *data, int len)
+{
+	int bytes_written;
+
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+	if (len < 1) {
+		pr_err("%s: invalid length: %d\n", __func__, len);
+		return -EINVAL;
+	}
+
+	if (!ch->pending_pkt_sz) {
+		pr_err("%s: no transaction in progress\n", __func__);
+		return -ENOEXEC;
+	}
+	if (ch->pending_pkt_sz - len < 0) {
+		pr_err("%s: segment of size: %d will make packet go over length\n",
+								__func__, len);
+		return -EINVAL;
+	}
+
+	bytes_written = smd_stream_write(ch, data, len, true);
+
+	ch->pending_pkt_sz -= bytes_written;
+
+	return bytes_written;
+}
+EXPORT_SYMBOL(smd_write_segment);
+
+int smd_write_end(smd_channel_t *ch)
+{
+
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+	if (ch->pending_pkt_sz) {
+		pr_err("%s: current packet not completely written\n", __func__);
+		return -E2BIG;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(smd_write_end);
+
+int smd_write_segment_avail(smd_channel_t *ch)
+{
+	int n;
+
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+	if (!ch->is_pkt_ch) {
+		pr_err("%s: non-packet channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	n = smd_stream_write_avail(ch);
+
+	/* pkt hdr already written, no need to reserve space for it */
+	if (ch->pending_pkt_sz)
+		return n;
+
+	return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0;
+}
+EXPORT_SYMBOL(smd_write_segment_avail);
+
+int smd_read(smd_channel_t *ch, void *data, int len)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	return ch->read(ch, data, len);
+}
+EXPORT_SYMBOL(smd_read);
+
+int smd_read_from_cb(smd_channel_t *ch, void *data, int len)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	return ch->read_from_cb(ch, data, len);
+}
+EXPORT_SYMBOL(smd_read_from_cb);
+
+int smd_write(smd_channel_t *ch, const void *data, int len)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, true);
+}
+EXPORT_SYMBOL(smd_write);
+
+int smd_read_avail(smd_channel_t *ch)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	if (ch->current_packet > (uint32_t)INT_MAX) {
+		pr_err("%s: Invalid packet size for Edge %d and Channel %s",
+			__func__, ch->type, ch->name);
+		return -EFAULT;
+	}
+	return ch->read_avail(ch);
+}
+EXPORT_SYMBOL(smd_read_avail);
+
+int smd_write_avail(smd_channel_t *ch)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	return ch->write_avail(ch);
+}
+EXPORT_SYMBOL(smd_write_avail);
+
+void smd_enable_read_intr(smd_channel_t *ch)
+{
+	if (ch)
+		ch->half_ch->set_fBLOCKREADINTR(ch->send, 0);
+}
+EXPORT_SYMBOL(smd_enable_read_intr);
+
+void smd_disable_read_intr(smd_channel_t *ch)
+{
+	if (ch)
+		ch->half_ch->set_fBLOCKREADINTR(ch->send, 1);
+}
+EXPORT_SYMBOL(smd_disable_read_intr);
+
+/**
+ * Enable/disable receive interrupts for the remote processor used by a
+ * particular channel.
+ * @ch:      open channel handle to use for the edge
+ * @mask:    1 = mask interrupts; 0 = unmask interrupts
+ * @cpumask  cpumask for the next cpu scheduled to be woken up
+ * @returns: 0 for success; < 0 for failure
+ *
+ * Note that this enables/disables all interrupts from the remote subsystem for
+ * all channels.  As such, it should be used with care and only for specific
+ * use cases such as power-collapse sequencing.
+ */
+int smd_mask_receive_interrupt(smd_channel_t *ch, bool mask,
+		const struct cpumask *cpumask)
+{
+	struct irq_chip *irq_chip;
+	struct irq_data *irq_data;
+	struct interrupt_config_item *int_cfg;
+
+	if (!ch)
+		return -EINVAL;
+
+	if (ch->type >= ARRAY_SIZE(edge_to_pids))
+		return -ENODEV;
+
+	int_cfg = &private_intr_config[edge_to_pids[ch->type].remote_pid].smd;
+
+	if (int_cfg->irq_id < 0)
+		return -ENODEV;
+
+	irq_chip = irq_get_chip(int_cfg->irq_id);
+	if (!irq_chip)
+		return -ENODEV;
+
+	irq_data = irq_get_irq_data(int_cfg->irq_id);
+	if (!irq_data)
+		return -ENODEV;
+
+	if (mask) {
+		SMD_POWER_INFO("SMD Masking interrupts from %s\n",
+				edge_to_pids[ch->type].subsys_name);
+		irq_chip->irq_mask(irq_data);
+		if (cpumask)
+			irq_set_affinity(int_cfg->irq_id, cpumask);
+	} else {
+		SMD_POWER_INFO("SMD Unmasking interrupts from %s\n",
+				edge_to_pids[ch->type].subsys_name);
+		irq_chip->irq_unmask(irq_data);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(smd_mask_receive_interrupt);
+
+int smd_cur_packet_size(smd_channel_t *ch)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	if (ch->current_packet > (uint32_t)INT_MAX) {
+		pr_err("%s: Invalid packet size for Edge %d and Channel %s",
+			__func__, ch->type, ch->name);
+		return -EFAULT;
+	}
+	return ch->current_packet;
+}
+EXPORT_SYMBOL(smd_cur_packet_size);
+
+int smd_tiocmget(smd_channel_t *ch)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	return  (ch->half_ch->get_fDSR(ch->recv) ? TIOCM_DSR : 0) |
+		(ch->half_ch->get_fCTS(ch->recv) ? TIOCM_CTS : 0) |
+		(ch->half_ch->get_fCD(ch->recv) ? TIOCM_CD : 0) |
+		(ch->half_ch->get_fRI(ch->recv) ? TIOCM_RI : 0) |
+		(ch->half_ch->get_fCTS(ch->send) ? TIOCM_RTS : 0) |
+		(ch->half_ch->get_fDSR(ch->send) ? TIOCM_DTR : 0);
+}
+EXPORT_SYMBOL(smd_tiocmget);
+
+/* this api will be called while holding smd_lock */
+int
+smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear)
+{
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	if (set & TIOCM_DTR)
+		ch->half_ch->set_fDSR(ch->send, 1);
+
+	if (set & TIOCM_RTS)
+		ch->half_ch->set_fCTS(ch->send, 1);
+
+	if (clear & TIOCM_DTR)
+		ch->half_ch->set_fDSR(ch->send, 0);
+
+	if (clear & TIOCM_RTS)
+		ch->half_ch->set_fCTS(ch->send, 0);
+
+	ch->half_ch->set_fSTATE(ch->send, 1);
+	barrier();
+	ch->notify_other_cpu(ch);
+
+	return 0;
+}
+EXPORT_SYMBOL(smd_tiocmset_from_cb);
+
+int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear)
+{
+	unsigned long flags;
+
+	if (!ch) {
+		pr_err("%s: Invalid channel specified\n", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&smd_lock, flags);
+	smd_tiocmset_from_cb(ch, set, clear);
+	spin_unlock_irqrestore(&smd_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(smd_tiocmset);
+
+int smd_is_pkt_avail(smd_channel_t *ch)
+{
+	unsigned long flags;
+
+	if (!ch || !ch->is_pkt_ch)
+		return -EINVAL;
+
+	if (ch->current_packet)
+		return 1;
+
+	spin_lock_irqsave(&smd_lock, flags);
+	update_packet_state(ch);
+	spin_unlock_irqrestore(&smd_lock, flags);
+
+	return ch->current_packet ? 1 : 0;
+}
+EXPORT_SYMBOL(smd_is_pkt_avail);
+
+static int smsm_cb_init(void)
+{
+	struct smsm_state_info *state_info;
+	int n;
+	int ret = 0;
+
+	smsm_states = kmalloc(sizeof(struct smsm_state_info)*SMSM_NUM_ENTRIES,
+		   GFP_KERNEL);
+
+	if (!smsm_states) {
+		pr_err("%s: SMSM init failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	smsm_cb_wq = create_singlethread_workqueue("smsm_cb_wq");
+	if (!smsm_cb_wq) {
+		pr_err("%s: smsm_cb_wq creation failed\n", __func__);
+		kfree(smsm_states);
+		return -EFAULT;
+	}
+
+	mutex_lock(&smsm_lock);
+	for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
+		state_info = &smsm_states[n];
+		state_info->last_value = __raw_readl(SMSM_STATE_ADDR(n));
+		state_info->intr_mask_set = 0x0;
+		state_info->intr_mask_clear = 0x0;
+		INIT_LIST_HEAD(&state_info->callbacks);
+	}
+	mutex_unlock(&smsm_lock);
+
+	return ret;
+}
+
+static int smsm_init(void)
+{
+	int i;
+	struct smsm_size_info_type *smsm_size_info;
+	unsigned long flags;
+	unsigned long j_start;
+	static int first = 1;
+	remote_spinlock_t *remote_spinlock;
+
+	if (!first)
+		return 0;
+	first = 0;
+
+	/* Verify that remote spinlock is not deadlocked */
+	remote_spinlock = smem_get_remote_spinlock();
+	j_start = jiffies;
+	while (!remote_spin_trylock_irqsave(remote_spinlock, flags)) {
+		if (jiffies_to_msecs(jiffies - j_start) > RSPIN_INIT_WAIT_MS) {
+			panic("%s: Remote processor %d will not release spinlock\n",
+				__func__, remote_spin_owner(remote_spinlock));
+		}
+	}
+	remote_spin_unlock_irqrestore(remote_spinlock, flags);
+
+	smsm_size_info = smem_find(SMEM_SMSM_SIZE_INFO,
+				sizeof(struct smsm_size_info_type), 0,
+				SMEM_ANY_HOST_FLAG);
+	if (smsm_size_info) {
+		SMSM_NUM_ENTRIES = smsm_size_info->num_entries;
+		SMSM_NUM_HOSTS = smsm_size_info->num_hosts;
+	}
+
+	i = kfifo_alloc(&smsm_snapshot_fifo,
+			sizeof(uint32_t) * SMSM_NUM_ENTRIES * SMSM_SNAPSHOT_CNT,
+			GFP_KERNEL);
+	if (i) {
+		pr_err("%s: SMSM state fifo alloc failed %d\n", __func__, i);
+		return i;
+	}
+	wakeup_source_init(&smsm_snapshot_ws, "smsm_snapshot");
+
+	if (!smsm_info.state) {
+		smsm_info.state = smem_alloc(ID_SHARED_STATE,
+						SMSM_NUM_ENTRIES *
+						sizeof(uint32_t), 0,
+						SMEM_ANY_HOST_FLAG);
+
+		if (smsm_info.state)
+			__raw_writel(0, SMSM_STATE_ADDR(SMSM_APPS_STATE));
+	}
+
+	if (!smsm_info.intr_mask) {
+		smsm_info.intr_mask = smem_alloc(SMEM_SMSM_CPU_INTR_MASK,
+						SMSM_NUM_ENTRIES *
+						SMSM_NUM_HOSTS *
+						sizeof(uint32_t), 0,
+						SMEM_ANY_HOST_FLAG);
+
+		if (smsm_info.intr_mask) {
+			for (i = 0; i < SMSM_NUM_ENTRIES; i++)
+				__raw_writel(0x0,
+					SMSM_INTR_MASK_ADDR(i, SMSM_APPS));
+
+			/* Configure legacy modem bits */
+			__raw_writel(LEGACY_MODEM_SMSM_MASK,
+				SMSM_INTR_MASK_ADDR(SMSM_MODEM_STATE,
+					SMSM_APPS));
+		}
+	}
+
+	i = smsm_cb_init();
+	if (i)
+		return i;
+
+	wmb(); /* Make sure memory is visible before proceeding */
+
+	smsm_pm_notifier(&smsm_pm_nb, PM_POST_SUSPEND, NULL);
+	i = register_pm_notifier(&smsm_pm_nb);
+	if (i)
+		pr_err("%s: power state notif error %d\n", __func__, i);
+
+	return 0;
+}
+
+static void smsm_cb_snapshot(uint32_t use_wakeup_source)
+{
+	int n;
+	uint32_t new_state;
+	unsigned long flags;
+	int ret;
+	uint64_t timestamp;
+
+	timestamp = sched_clock();
+	ret = kfifo_avail(&smsm_snapshot_fifo);
+	if (ret < SMSM_SNAPSHOT_SIZE) {
+		pr_err("%s: SMSM snapshot full %d\n", __func__, ret);
+		return;
+	}
+
+	/*
+	 * To avoid a race condition with notify_smsm_cb_clients_worker, the
+	 * following sequence must be followed:
+	 *   1) increment snapshot count
+	 *   2) insert data into FIFO
+	 *
+	 *   Potentially in parallel, the worker:
+	 *   a) verifies >= 1 snapshots are in FIFO
+	 *   b) processes snapshot
+	 *   c) decrements reference count
+	 *
+	 *   This order ensures that 1 will always occur before abc.
+	 */
+	if (use_wakeup_source) {
+		spin_lock_irqsave(&smsm_snapshot_count_lock, flags);
+		if (smsm_snapshot_count == 0) {
+			SMSM_POWER_INFO("SMSM snapshot wake lock\n");
+			__pm_stay_awake(&smsm_snapshot_ws);
+		}
+		++smsm_snapshot_count;
+		spin_unlock_irqrestore(&smsm_snapshot_count_lock, flags);
+	}
+
+	/* queue state entries */
+	for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
+		new_state = __raw_readl(SMSM_STATE_ADDR(n));
+
+		ret = kfifo_in(&smsm_snapshot_fifo,
+				&new_state, sizeof(new_state));
+		if (ret != sizeof(new_state)) {
+			pr_err("%s: SMSM snapshot failure %d\n", __func__, ret);
+			goto restore_snapshot_count;
+		}
+	}
+
+	ret = kfifo_in(&smsm_snapshot_fifo, &timestamp, sizeof(timestamp));
+	if (ret != sizeof(timestamp)) {
+		pr_err("%s: SMSM snapshot failure %d\n", __func__, ret);
+		goto restore_snapshot_count;
+	}
+
+	/* queue wakelock usage flag */
+	ret = kfifo_in(&smsm_snapshot_fifo,
+			&use_wakeup_source, sizeof(use_wakeup_source));
+	if (ret != sizeof(use_wakeup_source)) {
+		pr_err("%s: SMSM snapshot failure %d\n", __func__, ret);
+		goto restore_snapshot_count;
+	}
+
+	queue_work(smsm_cb_wq, &smsm_cb_work);
+	return;
+
+restore_snapshot_count:
+	if (use_wakeup_source) {
+		spin_lock_irqsave(&smsm_snapshot_count_lock, flags);
+		if (smsm_snapshot_count) {
+			--smsm_snapshot_count;
+			if (smsm_snapshot_count == 0) {
+				SMSM_POWER_INFO("SMSM snapshot wake unlock\n");
+				__pm_relax(&smsm_snapshot_ws);
+			}
+		} else {
+			pr_err("%s: invalid snapshot count\n", __func__);
+		}
+		spin_unlock_irqrestore(&smsm_snapshot_count_lock, flags);
+	}
+}
+
+static irqreturn_t smsm_irq_handler(int irq, void *data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&smem_lock, flags);
+	if (!smsm_info.state) {
+		SMSM_INFO("<SM NO STATE>\n");
+	} else {
+		unsigned int old_apps, apps;
+		unsigned int modm;
+
+		modm = __raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE));
+		old_apps = apps = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE));
+
+		SMSM_DBG("<SM %08x %08x>\n", apps, modm);
+		if (modm & SMSM_RESET) {
+			pr_err("SMSM: Modem SMSM state changed to SMSM_RESET.\n");
+		} else if (modm & SMSM_INIT) {
+			if (!(apps & SMSM_INIT))
+				apps |= SMSM_INIT;
+			if (modm & SMSM_SMDINIT)
+				apps |= SMSM_SMDINIT;
+		}
+
+		if (old_apps != apps) {
+			SMSM_DBG("<SM %08x NOTIFY>\n", apps);
+			__raw_writel(apps, SMSM_STATE_ADDR(SMSM_APPS_STATE));
+			notify_other_smsm(SMSM_APPS_STATE, (old_apps ^ apps));
+		}
+
+		smsm_cb_snapshot(1);
+	}
+	spin_unlock_irqrestore(&smem_lock, flags);
+	return IRQ_HANDLED;
+}
+
+irqreturn_t smsm_modem_irq_handler(int irq, void *data)
+{
+	SMSM_POWER_INFO("SMSM Int Modem->Apps\n");
+	++interrupt_stats[SMD_MODEM].smsm_in_count;
+	return smsm_irq_handler(irq, data);
+}
+
+irqreturn_t smsm_dsp_irq_handler(int irq, void *data)
+{
+	SMSM_POWER_INFO("SMSM Int LPASS->Apps\n");
+	++interrupt_stats[SMD_Q6].smsm_in_count;
+	return smsm_irq_handler(irq, data);
+}
+
+irqreturn_t smsm_dsps_irq_handler(int irq, void *data)
+{
+	SMSM_POWER_INFO("SMSM Int DSPS->Apps\n");
+	++interrupt_stats[SMD_DSPS].smsm_in_count;
+	return smsm_irq_handler(irq, data);
+}
+
+irqreturn_t smsm_wcnss_irq_handler(int irq, void *data)
+{
+	SMSM_POWER_INFO("SMSM Int WCNSS->Apps\n");
+	++interrupt_stats[SMD_WCNSS].smsm_in_count;
+	return smsm_irq_handler(irq, data);
+}
+
+/*
+ * Changes the global interrupt mask.  The set and clear masks are re-applied
+ * every time the global interrupt mask is updated for callback registration
+ * and de-registration.
+ *
+ * The clear mask is applied first, so if a bit is set to 1 in both the clear
+ * mask and the set mask, the result will be that the interrupt is set.
+ *
+ * @smsm_entry  SMSM entry to change
+ * @clear_mask  1 = clear bit, 0 = no-op
+ * @set_mask    1 = set bit, 0 = no-op
+ *
+ * @returns 0 for success, < 0 for error
+ */
+int smsm_change_intr_mask(uint32_t smsm_entry,
+			  uint32_t clear_mask, uint32_t set_mask)
+{
+	uint32_t  old_mask, new_mask;
+	unsigned long flags;
+
+	if (smsm_entry >= SMSM_NUM_ENTRIES) {
+		pr_err("smsm_change_state: Invalid entry %d\n",
+		       smsm_entry);
+		return -EINVAL;
+	}
+
+	if (!smsm_info.intr_mask) {
+		pr_err("smsm_change_intr_mask <SM NO STATE>\n");
+		return -EIO;
+	}
+
+	spin_lock_irqsave(&smem_lock, flags);
+	smsm_states[smsm_entry].intr_mask_clear = clear_mask;
+	smsm_states[smsm_entry].intr_mask_set = set_mask;
+
+	old_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
+	new_mask = (old_mask & ~clear_mask) | set_mask;
+	__raw_writel(new_mask, SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
+
+	wmb(); /* Make sure memory is visible before proceeding */
+	spin_unlock_irqrestore(&smem_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(smsm_change_intr_mask);
+
+int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask)
+{
+	if (smsm_entry >= SMSM_NUM_ENTRIES) {
+		pr_err("smsm_change_state: Invalid entry %d\n",
+		       smsm_entry);
+		return -EINVAL;
+	}
+
+	if (!smsm_info.intr_mask) {
+		pr_err("smsm_change_intr_mask <SM NO STATE>\n");
+		return -EIO;
+	}
+
+	*intr_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
+	return 0;
+}
+EXPORT_SYMBOL(smsm_get_intr_mask);
+
+int smsm_change_state(uint32_t smsm_entry,
+		      uint32_t clear_mask, uint32_t set_mask)
+{
+	unsigned long flags;
+	uint32_t  old_state, new_state;
+
+	if (smsm_entry >= SMSM_NUM_ENTRIES) {
+		pr_err("smsm_change_state: Invalid entry %d",
+		       smsm_entry);
+		return -EINVAL;
+	}
+
+	if (!smsm_info.state) {
+		pr_err("smsm_change_state <SM NO STATE>\n");
+		return -EIO;
+	}
+	spin_lock_irqsave(&smem_lock, flags);
+
+	old_state = __raw_readl(SMSM_STATE_ADDR(smsm_entry));
+	new_state = (old_state & ~clear_mask) | set_mask;
+	__raw_writel(new_state, SMSM_STATE_ADDR(smsm_entry));
+	SMSM_POWER_INFO("%s %d:%08x->%08x", __func__, smsm_entry,
+			old_state, new_state);
+	notify_other_smsm(SMSM_APPS_STATE, (old_state ^ new_state));
+
+	spin_unlock_irqrestore(&smem_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(smsm_change_state);
+
+uint32_t smsm_get_state(uint32_t smsm_entry)
+{
+	uint32_t rv = 0;
+
+	/* needs interface change to return error code */
+	if (smsm_entry >= SMSM_NUM_ENTRIES) {
+		pr_err("smsm_change_state: Invalid entry %d",
+		       smsm_entry);
+		return 0;
+	}
+
+	if (!smsm_info.state)
+		pr_err("smsm_get_state <SM NO STATE>\n");
+	else
+		rv = __raw_readl(SMSM_STATE_ADDR(smsm_entry));
+
+	return rv;
+}
+EXPORT_SYMBOL(smsm_get_state);
+
+/**
+ * Performs SMSM callback client notifiction.
+ */
+void notify_smsm_cb_clients_worker(struct work_struct *work)
+{
+	struct smsm_state_cb_info *cb_info;
+	struct smsm_state_info *state_info;
+	int n;
+	uint32_t new_state;
+	uint32_t state_changes;
+	uint32_t use_wakeup_source;
+	int ret;
+	unsigned long flags;
+	uint64_t t_snapshot;
+	uint64_t t_start;
+	unsigned long nanosec_rem;
+
+	while (kfifo_len(&smsm_snapshot_fifo) >= SMSM_SNAPSHOT_SIZE) {
+		t_start = sched_clock();
+		mutex_lock(&smsm_lock);
+		for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
+			state_info = &smsm_states[n];
+
+			ret = kfifo_out(&smsm_snapshot_fifo, &new_state,
+					sizeof(new_state));
+			if (ret != sizeof(new_state)) {
+				pr_err("%s: snapshot underflow %d\n",
+					__func__, ret);
+				mutex_unlock(&smsm_lock);
+				return;
+			}
+
+			state_changes = state_info->last_value ^ new_state;
+			if (state_changes) {
+				SMSM_POWER_INFO("SMSM Change %d: %08x->%08x\n",
+						n, state_info->last_value,
+						new_state);
+				list_for_each_entry(cb_info,
+					&state_info->callbacks, cb_list) {
+
+					if (cb_info->mask & state_changes)
+						cb_info->notify(cb_info->data,
+							state_info->last_value,
+							new_state);
+				}
+				state_info->last_value = new_state;
+			}
+		}
+
+		ret = kfifo_out(&smsm_snapshot_fifo, &t_snapshot,
+				sizeof(t_snapshot));
+		if (ret != sizeof(t_snapshot)) {
+			pr_err("%s: snapshot underflow %d\n",
+				__func__, ret);
+			mutex_unlock(&smsm_lock);
+			return;
+		}
+
+		/* read wakelock flag */
+		ret = kfifo_out(&smsm_snapshot_fifo, &use_wakeup_source,
+				sizeof(use_wakeup_source));
+		if (ret != sizeof(use_wakeup_source)) {
+			pr_err("%s: snapshot underflow %d\n",
+				__func__, ret);
+			mutex_unlock(&smsm_lock);
+			return;
+		}
+		mutex_unlock(&smsm_lock);
+
+		if (use_wakeup_source) {
+			spin_lock_irqsave(&smsm_snapshot_count_lock, flags);
+			if (smsm_snapshot_count) {
+				--smsm_snapshot_count;
+				if (smsm_snapshot_count == 0) {
+					SMSM_POWER_INFO(
+						"SMSM snapshot wake unlock\n");
+					__pm_relax(&smsm_snapshot_ws);
+				}
+			} else {
+				pr_err("%s: invalid snapshot count\n",
+						__func__);
+			}
+			spin_unlock_irqrestore(&smsm_snapshot_count_lock,
+					flags);
+		}
+
+		t_start = t_start - t_snapshot;
+		nanosec_rem = do_div(t_start, 1000000000U);
+		SMSM_POWER_INFO(
+			"SMSM snapshot queue response time %6u.%09lu s\n",
+			(unsigned int)t_start, nanosec_rem);
+	}
+}
+
+
+/**
+ * Registers callback for SMSM state notifications when the specified
+ * bits change.
+ *
+ * @smsm_entry  Processor entry to deregister
+ * @mask        Bits to deregister (if result is 0, callback is removed)
+ * @notify      Notification function to deregister
+ * @data        Opaque data passed in to callback
+ *
+ * @returns Status code
+ *  <0 error code
+ *  0  inserted new entry
+ *  1  updated mask of existing entry
+ */
+int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask,
+		void (*notify)(void *, uint32_t, uint32_t), void *data)
+{
+	struct smsm_state_info *state;
+	struct smsm_state_cb_info *cb_info;
+	struct smsm_state_cb_info *cb_found = 0;
+	uint32_t new_mask = 0;
+	int ret = 0;
+
+	if (smsm_entry >= SMSM_NUM_ENTRIES)
+		return -EINVAL;
+
+	mutex_lock(&smsm_lock);
+
+	if (!smsm_states) {
+		/* smsm not yet initialized */
+		ret = -ENODEV;
+		goto cleanup;
+	}
+
+	state = &smsm_states[smsm_entry];
+	list_for_each_entry(cb_info,
+			&state->callbacks, cb_list) {
+		if (!ret && (cb_info->notify == notify) &&
+				(cb_info->data == data)) {
+			cb_info->mask |= mask;
+			cb_found = cb_info;
+			ret = 1;
+		}
+		new_mask |= cb_info->mask;
+	}
+
+	if (!cb_found) {
+		cb_info = kmalloc(sizeof(struct smsm_state_cb_info),
+			GFP_ATOMIC);
+		if (!cb_info) {
+			ret = -ENOMEM;
+			goto cleanup;
+		}
+
+		cb_info->mask = mask;
+		cb_info->notify = notify;
+		cb_info->data = data;
+		INIT_LIST_HEAD(&cb_info->cb_list);
+		list_add_tail(&cb_info->cb_list,
+			&state->callbacks);
+		new_mask |= mask;
+	}
+
+	/* update interrupt notification mask */
+	if (smsm_entry == SMSM_MODEM_STATE)
+		new_mask |= LEGACY_MODEM_SMSM_MASK;
+
+	if (smsm_info.intr_mask) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&smem_lock, flags);
+		new_mask = (new_mask & ~state->intr_mask_clear)
+				| state->intr_mask_set;
+		__raw_writel(new_mask,
+				SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
+		wmb(); /* Make sure memory is visible before proceeding */
+		spin_unlock_irqrestore(&smem_lock, flags);
+	}
+
+cleanup:
+	mutex_unlock(&smsm_lock);
+	return ret;
+}
+EXPORT_SYMBOL(smsm_state_cb_register);
+
+
+/**
+ * Deregisters for SMSM state notifications for the specified bits.
+ *
+ * @smsm_entry  Processor entry to deregister
+ * @mask        Bits to deregister (if result is 0, callback is removed)
+ * @notify      Notification function to deregister
+ * @data        Opaque data passed in to callback
+ *
+ * @returns Status code
+ *  <0 error code
+ *  0  not found
+ *  1  updated mask
+ *  2  removed callback
+ */
+int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask,
+		void (*notify)(void *, uint32_t, uint32_t), void *data)
+{
+	struct smsm_state_cb_info *cb_info;
+	struct smsm_state_cb_info *cb_tmp;
+	struct smsm_state_info *state;
+	uint32_t new_mask = 0;
+	int ret = 0;
+
+	if (smsm_entry >= SMSM_NUM_ENTRIES)
+		return -EINVAL;
+
+	mutex_lock(&smsm_lock);
+
+	if (!smsm_states) {
+		/* smsm not yet initialized */
+		mutex_unlock(&smsm_lock);
+		return -ENODEV;
+	}
+
+	state = &smsm_states[smsm_entry];
+	list_for_each_entry_safe(cb_info, cb_tmp,
+		&state->callbacks, cb_list) {
+		if (!ret && (cb_info->notify == notify) &&
+			(cb_info->data == data)) {
+			cb_info->mask &= ~mask;
+			ret = 1;
+			if (!cb_info->mask) {
+				/* no mask bits set, remove callback */
+				list_del(&cb_info->cb_list);
+				kfree(cb_info);
+				ret = 2;
+				continue;
+			}
+		}
+		new_mask |= cb_info->mask;
+	}
+
+	/* update interrupt notification mask */
+	if (smsm_entry == SMSM_MODEM_STATE)
+		new_mask |= LEGACY_MODEM_SMSM_MASK;
+
+	if (smsm_info.intr_mask) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&smem_lock, flags);
+		new_mask = (new_mask & ~state->intr_mask_clear)
+				| state->intr_mask_set;
+		__raw_writel(new_mask,
+				SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
+		wmb(); /* Make sure memory is visible before proceeding */
+		spin_unlock_irqrestore(&smem_lock, flags);
+	}
+
+	mutex_unlock(&smsm_lock);
+	return ret;
+}
+EXPORT_SYMBOL(smsm_state_cb_deregister);
+
+static int restart_notifier_cb(struct notifier_block *this,
+				  unsigned long code,
+				  void *data);
+
+static struct restart_notifier_block restart_notifiers[] = {
+	{SMD_MODEM, "modem", .nb.notifier_call = restart_notifier_cb},
+	{SMD_Q6, "lpass", .nb.notifier_call = restart_notifier_cb},
+	{SMD_WCNSS, "wcnss", .nb.notifier_call = restart_notifier_cb},
+	{SMD_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb},
+	{SMD_MODEM, "gss", .nb.notifier_call = restart_notifier_cb},
+	{SMD_Q6, "adsp", .nb.notifier_call = restart_notifier_cb},
+	{SMD_DSPS, "slpi", .nb.notifier_call = restart_notifier_cb},
+};
+
+static int restart_notifier_cb(struct notifier_block *this,
+				  unsigned long code,
+				  void *data)
+{
+	remote_spinlock_t *remote_spinlock;
+
+	/*
+	 * Some SMD or SMSM clients assume SMD/SMSM SSR handling will be
+	 * done in the AFTER_SHUTDOWN level.  If this ever changes, extra
+	 * care should be taken to verify no clients are broken.
+	 */
+	if (code == SUBSYS_AFTER_SHUTDOWN) {
+		struct restart_notifier_block *notifier;
+
+		notifier = container_of(this,
+				struct restart_notifier_block, nb);
+		SMD_INFO("%s: ssrestart for processor %d ('%s')\n",
+				__func__, notifier->processor,
+				notifier->name);
+
+		remote_spinlock = smem_get_remote_spinlock();
+		remote_spin_release(remote_spinlock, notifier->processor);
+		remote_spin_release_all(notifier->processor);
+
+		smd_channel_reset(notifier->processor);
+	}
+
+	return NOTIFY_DONE;
+}
+
+/**
+ * smd_post_init() - SMD post initialization
+ * @remote_pid: remote pid that has been initialized.  Ignored when is_legacy=1
+ *
+ * This function is used by the device tree initialization to complete the SMD
+ * init sequence.
+ */
+void smd_post_init(unsigned int remote_pid)
+{
+	smd_channel_probe_now(&remote_info[remote_pid]);
+}
+
+/**
+ * smsm_post_init() - SMSM post initialization
+ * @returns:	0 for success, standard Linux error code otherwise
+ *
+ * This function is used by the legacy and device tree initialization
+ * to complete the SMSM init sequence.
+ */
+int smsm_post_init(void)
+{
+	int ret;
+
+	ret = smsm_init();
+	if (ret) {
+		pr_err("smsm_init() failed ret = %d\n", ret);
+		return ret;
+	}
+	smsm_irq_handler(0, 0);
+
+	return ret;
+}
+
+/**
+ * smd_get_intr_config() - Get interrupt configuration structure
+ * @edge:	edge type identifes local and remote processor
+ * @returns:	pointer to interrupt configuration
+ *
+ * This function returns the interrupt configuration of remote processor
+ * based on the edge type.
+ */
+struct interrupt_config *smd_get_intr_config(uint32_t edge)
+{
+	if (edge >= ARRAY_SIZE(edge_to_pids))
+		return NULL;
+	return &private_intr_config[edge_to_pids[edge].remote_pid];
+}
+
+/**
+ * smd_get_edge_remote_pid() - Get the remote processor ID
+ * @edge:	edge type identifes local and remote processor
+ * @returns:	remote processor ID
+ *
+ * This function returns remote processor ID based on edge type.
+ */
+int smd_edge_to_remote_pid(uint32_t edge)
+{
+	if (edge >= ARRAY_SIZE(edge_to_pids))
+		return -EINVAL;
+	return edge_to_pids[edge].remote_pid;
+}
+
+/**
+ * smd_get_edge_local_pid() - Get the local processor ID
+ * @edge:	edge type identifies local and remote processor
+ * @returns:	local processor ID
+ *
+ * This function returns local processor ID based on edge type.
+ */
+int smd_edge_to_local_pid(uint32_t edge)
+{
+	if (edge >= ARRAY_SIZE(edge_to_pids))
+		return -EINVAL;
+	return edge_to_pids[edge].local_pid;
+}
+
+/**
+ * smd_proc_set_skip_pil() - Mark if the indicated processor is be loaded by PIL
+ * @pid:		the processor id to mark
+ * @skip_pil:		true if @pid cannot by loaded by PIL
+ */
+void smd_proc_set_skip_pil(unsigned int pid, bool skip_pil)
+{
+	if (pid >= NUM_SMD_SUBSYSTEMS) {
+		pr_err("%s: invalid pid:%d\n", __func__, pid);
+		return;
+	}
+	remote_info[pid].skip_pil = skip_pil;
+}
+
+/**
+ * smd_set_edge_subsys_name() - Set the subsystem name
+ * @edge:		edge type identifies local and remote processor
+ * @subsys_name:	pointer to subsystem name
+ *
+ * This function is used to set the subsystem name for given edge type.
+ */
+void smd_set_edge_subsys_name(uint32_t edge, const char *subsys_name)
+{
+	if (edge < ARRAY_SIZE(edge_to_pids))
+		if (subsys_name)
+			strlcpy(edge_to_pids[edge].subsys_name,
+				subsys_name, SMD_MAX_CH_NAME_LEN);
+		else
+			strlcpy(edge_to_pids[edge].subsys_name,
+				"", SMD_MAX_CH_NAME_LEN);
+	else
+		pr_err("%s: Invalid edge type[%d]\n", __func__, edge);
+}
+
+/**
+ * smd_reset_all_edge_subsys_name() - Reset the subsystem name
+ *
+ * This function is used to reset the subsystem name of all edges in
+ * targets where configuration information is available through
+ * device tree.
+ */
+void smd_reset_all_edge_subsys_name(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(edge_to_pids); i++)
+		strlcpy(edge_to_pids[i].subsys_name,
+			"", sizeof(""));
+}
+
+/**
+ * smd_set_edge_initialized() - Set the edge initialized status
+ * @edge:	edge type identifies local and remote processor
+ *
+ * This function set the initialized varibale based on edge type.
+ */
+void smd_set_edge_initialized(uint32_t edge)
+{
+	if (edge < ARRAY_SIZE(edge_to_pids))
+		edge_to_pids[edge].initialized = true;
+	else
+		pr_err("%s: Invalid edge type[%d]\n", __func__, edge);
+}
+
+/**
+ * smd_cfg_smd_intr() - Set the SMD interrupt configuration
+ * @proc:	remote processor ID
+ * @mask:	bit position in IRQ register
+ * @ptr:	IRQ register
+ *
+ * This function is called in Legacy init sequence and used to set
+ * the SMD interrupt configurations for particular processor.
+ */
+void smd_cfg_smd_intr(uint32_t proc, uint32_t mask, void *ptr)
+{
+	private_intr_config[proc].smd.out_bit_pos = mask;
+	private_intr_config[proc].smd.out_base = ptr;
+	private_intr_config[proc].smd.out_offset = 0;
+}
+
+/*
+ * smd_cfg_smsm_intr() -  Set the SMSM interrupt configuration
+ * @proc:	remote processor ID
+ * @mask:	bit position in IRQ register
+ * @ptr:	IRQ register
+ *
+ * This function is called in Legacy init sequence and used to set
+ * the SMSM interrupt configurations for particular processor.
+ */
+void smd_cfg_smsm_intr(uint32_t proc, uint32_t mask, void *ptr)
+{
+	private_intr_config[proc].smsm.out_bit_pos = mask;
+	private_intr_config[proc].smsm.out_base = ptr;
+	private_intr_config[proc].smsm.out_offset = 0;
+}
+
+static __init int modem_restart_late_init(void)
+{
+	int i;
+	void *handle;
+	struct restart_notifier_block *nb;
+
+	for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
+		nb = &restart_notifiers[i];
+		handle = subsys_notif_register_notifier(nb->name, &nb->nb);
+		SMD_DBG("%s: registering notif for '%s', handle=%p\n",
+				__func__, nb->name, handle);
+	}
+
+	return 0;
+}
+late_initcall(modem_restart_late_init);
+
+int __init msm_smd_init(void)
+{
+	static bool registered;
+	int rc;
+	int i;
+
+	if (registered)
+		return 0;
+
+	smd_log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smd", 0);
+	if (!smd_log_ctx) {
+		pr_err("%s: unable to create SMD logging context\n", __func__);
+		msm_smd_debug_mask = 0;
+	}
+
+	smsm_log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smsm", 0);
+	if (!smsm_log_ctx) {
+		pr_err("%s: unable to create SMSM logging context\n", __func__);
+		msm_smd_debug_mask = 0;
+	}
+
+	registered = true;
+
+	for (i = 0; i < NUM_SMD_SUBSYSTEMS; ++i) {
+		remote_info[i].remote_pid = i;
+		remote_info[i].free_space = UINT_MAX;
+		INIT_WORK(&remote_info[i].probe_work, smd_channel_probe_worker);
+		INIT_LIST_HEAD(&remote_info[i].ch_list);
+	}
+
+	channel_close_wq = create_singlethread_workqueue("smd_channel_close");
+	if (IS_ERR(channel_close_wq)) {
+		pr_err("%s: create_singlethread_workqueue ENOMEM\n", __func__);
+		return -ENOMEM;
+	}
+
+	rc = msm_smd_driver_register();
+	if (rc) {
+		pr_err("%s: msm_smd_driver register failed %d\n",
+			__func__, rc);
+		return rc;
+	}
+	return 0;
+}
+
+arch_initcall(msm_smd_init);
+
+MODULE_DESCRIPTION("MSM Shared Memory Core");
+MODULE_AUTHOR("Brian Swetland <swetland@google.com>");
+MODULE_LICENSE("GPL");
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/smd_debug.c b/drivers/soc/qcom/smd_debug.c
new file mode 100644
index 0000000..07c5aeb
--- /dev/null
+++ b/drivers/soc/qcom/smd_debug.c
@@ -0,0 +1,429 @@
+/* drivers/soc/qcom/smd_debug.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2014, 2017, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/err.h>
+
+#include <soc/qcom/smem.h>
+
+#include "smd_private.h"
+
+#if defined(CONFIG_DEBUG_FS)
+
+static char *chstate(unsigned int n)
+{
+	switch (n) {
+	case SMD_SS_CLOSED:
+		return "CLOSED";
+	case SMD_SS_OPENING:
+		return "OPENING";
+	case SMD_SS_OPENED:
+		return "OPENED";
+	case SMD_SS_FLUSHING:
+		return "FLUSHING";
+	case SMD_SS_CLOSING:
+		return "CLOSING";
+	case SMD_SS_RESET:
+		return "RESET";
+	case SMD_SS_RESET_OPENING:
+		return "ROPENING";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static void debug_int_stats(struct seq_file *s)
+{
+	int subsys;
+	struct interrupt_stat *stats = interrupt_stats;
+	const char *subsys_name;
+
+	seq_puts(s,
+		"   Subsystem    | Interrupt ID |    In     |    Out    |\n");
+
+	for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
+		subsys_name = smd_pid_to_subsystem(subsys);
+		if (!IS_ERR_OR_NULL(subsys_name)) {
+			seq_printf(s, "%-10s %4s |    %9d | %9u | %9u |\n",
+				smd_pid_to_subsystem(subsys), "smd",
+				stats->smd_interrupt_id,
+				stats->smd_in_count,
+				stats->smd_out_count);
+
+			seq_printf(s, "%-10s %4s |    %9d | %9u | %9u |\n",
+				smd_pid_to_subsystem(subsys), "smsm",
+				stats->smsm_interrupt_id,
+				stats->smsm_in_count,
+				stats->smsm_out_count);
+		}
+		++stats;
+	}
+}
+
+static void debug_int_stats_reset(struct seq_file *s)
+{
+	int subsys;
+	struct interrupt_stat *stats = interrupt_stats;
+
+	seq_puts(s, "Resetting interrupt stats.\n");
+
+	for (subsys = 0; subsys < NUM_SMD_SUBSYSTEMS; ++subsys) {
+		stats->smd_in_count = 0;
+		stats->smd_out_count = 0;
+		stats->smsm_in_count = 0;
+		stats->smsm_out_count = 0;
+		++stats;
+	}
+}
+
+/* NNV: revist, it may not be smd version */
+static void debug_read_smd_version(struct seq_file *s)
+{
+	uint32_t *smd_ver;
+	uint32_t n, version;
+
+	smd_ver = smem_find(SMEM_VERSION_SMD, 32 * sizeof(uint32_t),
+							0, SMEM_ANY_HOST_FLAG);
+
+	if (smd_ver)
+		for (n = 0; n < 32; n++) {
+			version = smd_ver[n];
+			seq_printf(s, "entry %d: %d.%d\n", n,
+				       version >> 16,
+				       version & 0xffff);
+		}
+}
+
+/**
+ * pid_to_str - Convert a numeric processor id value into a human readable
+ *		string value.
+ *
+ * @pid: the processor id to convert
+ * @returns: a string representation of @pid
+ */
+static char *pid_to_str(int pid)
+{
+	switch (pid) {
+	case SMD_APPS:
+		return "APPS";
+	case SMD_MODEM:
+		return "MDMSW";
+	case SMD_Q6:
+		return "ADSP";
+	case SMD_TZ:
+		return "TZ";
+	case SMD_WCNSS:
+		return "WCNSS";
+	case SMD_MODEM_Q6_FW:
+		return "MDMFW";
+	case SMD_RPM:
+		return "RPM";
+	default:
+		return "???";
+	}
+}
+
+/**
+ * print_half_ch_state - Print the state of half of a SMD channel in a human
+ *			readable format.
+ *
+ * @s: the sequential file to print to
+ * @half_ch: half of a SMD channel that should have its state printed
+ * @half_ch_funcs: the relevant channel access functions for @half_ch
+ * @size: size of the fifo in bytes associated with @half_ch
+ * @proc: the processor id that owns the part of the SMD channel associated with
+ *		@half_ch
+ * @is_restricted: true if memory access is restricted
+ */
+static void print_half_ch_state(struct seq_file *s,
+				void *half_ch,
+				struct smd_half_channel_access *half_ch_funcs,
+				unsigned int size,
+				int proc,
+				bool is_restricted)
+{
+	seq_printf(s, "%-5s|", pid_to_str(proc));
+
+	if (!is_restricted) {
+		seq_printf(s, "%-7s|0x%05X|0x%05X|0x%05X",
+			chstate(half_ch_funcs->get_state(half_ch)),
+			size,
+			half_ch_funcs->get_tail(half_ch),
+			half_ch_funcs->get_head(half_ch));
+		seq_printf(s, "|%c%c%c%c%c%c%c%c|0x%05X",
+			half_ch_funcs->get_fDSR(half_ch) ? 'D' : 'd',
+			half_ch_funcs->get_fCTS(half_ch) ? 'C' : 'c',
+			half_ch_funcs->get_fCD(half_ch) ? 'C' : 'c',
+			half_ch_funcs->get_fRI(half_ch) ? 'I' : 'i',
+			half_ch_funcs->get_fHEAD(half_ch) ? 'W' : 'w',
+			half_ch_funcs->get_fTAIL(half_ch) ? 'R' : 'r',
+			half_ch_funcs->get_fSTATE(half_ch) ? 'S' : 's',
+			half_ch_funcs->get_fBLOCKREADINTR(half_ch) ? 'B' : 'b',
+			(half_ch_funcs->get_head(half_ch) -
+				half_ch_funcs->get_tail(half_ch)) & (size - 1));
+	} else {
+		seq_puts(s, " Access Restricted");
+	}
+}
+
+/**
+ * smd_xfer_type_to_str - Convert a numeric transfer type value into a human
+ *		readable string value.
+ *
+ * @xfer_type: the processor id to convert
+ * @returns: a string representation of @xfer_type
+ */
+static char *smd_xfer_type_to_str(uint32_t xfer_type)
+{
+	if (xfer_type == 1)
+		return "S"; /* streaming type */
+	else if (xfer_type == 2)
+		return "P"; /* packet type */
+	else
+		return "L"; /* legacy type */
+}
+
+/**
+ * print_smd_ch_table - Print the current state of every valid SMD channel in a
+ *			specific SMD channel allocation table to a human
+ *			readable formatted output.
+ *
+ * @s: the sequential file to print to
+ * @tbl: a valid pointer to the channel allocation table to print from
+ * @num_tbl_entries: total number of entries in the table referenced by @tbl
+ * @ch_base_id: the SMEM item id corresponding to the array of channel
+ *		structures for the channels found in @tbl
+ * @fifo_base_id: the SMEM item id corresponding to the array of channel fifos
+ *		for the channels found in @tbl
+ * @pid: processor id to use for any SMEM operations
+ * @flags: flags to use for any SMEM operations
+ */
+static void print_smd_ch_table(struct seq_file *s,
+				struct smd_alloc_elm *tbl,
+				unsigned int num_tbl_entries,
+				unsigned int ch_base_id,
+				unsigned int fifo_base_id,
+				unsigned int pid,
+				unsigned int flags)
+{
+	void *half_ch;
+	unsigned int half_ch_size;
+	uint32_t ch_type;
+	void *buffer;
+	unsigned int buffer_size;
+	int n;
+	bool is_restricted;
+
+/*
+ * formatted, human readable channel state output, ie:
+ID|CHANNEL NAME       |T|PROC |STATE  |FIFO SZ|RDPTR  |WRPTR  |FLAGS   |DATAPEN
+-------------------------------------------------------------------------------
+00|DS                 |S|APPS |CLOSED |0x02000|0x00000|0x00000|dcCiwrsb|0x00000
+  |                   | |MDMSW|OPENING|0x02000|0x00000|0x00000|dcCiwrsb|0x00000
+-------------------------------------------------------------------------------
+ */
+
+	seq_printf(s, "%2s|%-19s|%1s|%-5s|%-7s|%-7s|%-7s|%-7s|%-8s|%-7s\n",
+								"ID",
+								"CHANNEL NAME",
+								"T",
+								"PROC",
+								"STATE",
+								"FIFO SZ",
+								"RDPTR",
+								"WRPTR",
+								"FLAGS",
+								"DATAPEN");
+	seq_puts(s,
+		"-------------------------------------------------------------------------------\n");
+	for (n = 0; n < num_tbl_entries; ++n) {
+		if (strlen(tbl[n].name) == 0)
+			continue;
+
+		seq_printf(s, "%2u|%-19s|%s|", tbl[n].cid, tbl[n].name,
+			smd_xfer_type_to_str(SMD_XFER_TYPE(tbl[n].type)));
+		ch_type = SMD_CHANNEL_TYPE(tbl[n].type);
+
+
+		if (smd_edge_to_remote_pid(ch_type) == SMD_RPM &&
+		   smd_edge_to_local_pid(ch_type) != SMD_APPS)
+			is_restricted = true;
+		else
+			is_restricted = false;
+
+		if (is_word_access_ch(ch_type))
+			half_ch_size =
+				sizeof(struct smd_half_channel_word_access);
+		else
+			half_ch_size = sizeof(struct smd_half_channel);
+
+		half_ch = smem_find(ch_base_id + n, 2 * half_ch_size,
+								pid, flags);
+		buffer = smem_get_entry(fifo_base_id + n, &buffer_size,
+								pid, flags);
+		if (half_ch && buffer)
+			print_half_ch_state(s,
+					half_ch,
+					get_half_ch_funcs(ch_type),
+					buffer_size / 2,
+					smd_edge_to_local_pid(ch_type),
+					is_restricted);
+
+		seq_puts(s, "\n");
+		seq_printf(s, "%2s|%-19s|%1s|", "", "", "");
+
+		if (half_ch && buffer)
+			print_half_ch_state(s,
+					half_ch + half_ch_size,
+					get_half_ch_funcs(ch_type),
+					buffer_size / 2,
+					smd_edge_to_remote_pid(ch_type),
+					is_restricted);
+
+		seq_puts(s, "\n");
+		seq_puts(s,
+			"-------------------------------------------------------------------------------\n");
+	}
+}
+
+/**
+ * debug_ch - Print the current state of every valid SMD channel in a human
+ *		readable formatted table.
+ *
+ * @s: the sequential file to print to
+ */
+static void debug_ch(struct seq_file *s)
+{
+	struct smd_alloc_elm *tbl;
+	struct smd_alloc_elm *default_pri_tbl;
+	struct smd_alloc_elm *default_sec_tbl;
+	unsigned int tbl_size;
+	int i;
+
+	tbl = smem_get_entry(ID_CH_ALLOC_TBL, &tbl_size, 0, SMEM_ANY_HOST_FLAG);
+	default_pri_tbl = tbl;
+
+	if (!tbl) {
+		seq_puts(s, "Channel allocation table not found\n");
+		return;
+	}
+
+	if (IS_ERR(tbl) && PTR_ERR(tbl) == -EPROBE_DEFER) {
+		seq_puts(s, "SMEM is not initialized\n");
+		return;
+	}
+
+	seq_puts(s, "Primary allocation table:\n");
+	print_smd_ch_table(s, tbl, tbl_size / sizeof(*tbl), ID_SMD_CHANNELS,
+							SMEM_SMD_FIFO_BASE_ID,
+							0,
+							SMEM_ANY_HOST_FLAG);
+
+	tbl = smem_get_entry(SMEM_CHANNEL_ALLOC_TBL_2, &tbl_size, 0,
+							SMEM_ANY_HOST_FLAG);
+	default_sec_tbl = tbl;
+	if (tbl) {
+		seq_puts(s, "\n\nSecondary allocation table:\n");
+		print_smd_ch_table(s, tbl, tbl_size / sizeof(*tbl),
+						SMEM_SMD_BASE_ID_2,
+						SMEM_SMD_FIFO_BASE_ID_2,
+						0,
+						SMEM_ANY_HOST_FLAG);
+	}
+
+	for (i = 1; i < NUM_SMD_SUBSYSTEMS; ++i) {
+		tbl = smem_get_entry(ID_CH_ALLOC_TBL, &tbl_size, i, 0);
+		if (tbl && tbl != default_pri_tbl) {
+			seq_puts(s, "\n\n");
+			seq_printf(s, "%s <-> %s Primary allocation table:\n",
+							pid_to_str(SMD_APPS),
+							pid_to_str(i));
+			print_smd_ch_table(s, tbl, tbl_size / sizeof(*tbl),
+							ID_SMD_CHANNELS,
+							SMEM_SMD_FIFO_BASE_ID,
+							i,
+							0);
+		}
+
+		tbl = smem_get_entry(SMEM_CHANNEL_ALLOC_TBL_2, &tbl_size, i, 0);
+		if (tbl && tbl != default_sec_tbl) {
+			seq_puts(s, "\n\n");
+			seq_printf(s, "%s <-> %s Secondary allocation table:\n",
+							pid_to_str(SMD_APPS),
+							pid_to_str(i));
+			print_smd_ch_table(s, tbl, tbl_size / sizeof(*tbl),
+						SMEM_SMD_BASE_ID_2,
+						SMEM_SMD_FIFO_BASE_ID_2,
+						i,
+						0);
+		}
+	}
+}
+
+static int debugfs_show(struct seq_file *s, void *data)
+{
+	void (*show)(struct seq_file *) = s->private;
+
+	show(s);
+
+	return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debugfs_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+	.open = debug_open,
+	.release = single_release,
+	.read = seq_read,
+	.llseek = seq_lseek,
+};
+
+static void debug_create(const char *name, umode_t mode,
+			 struct dentry *dent,
+			 void (*show)(struct seq_file *))
+{
+	struct dentry *file;
+
+	file = debugfs_create_file(name, mode, dent, show, &debug_ops);
+	if (!file)
+		pr_err("%s: unable to create file '%s'\n", __func__, name);
+}
+
+static int __init smd_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("smd", 0);
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	debug_create("ch", 0444, dent, debug_ch);
+	debug_create("version", 0444, dent, debug_read_smd_version);
+	debug_create("int_stats", 0444, dent, debug_int_stats);
+	debug_create("int_stats_reset", 0444, dent, debug_int_stats_reset);
+
+	return 0;
+}
+
+late_initcall(smd_debugfs_init);
+#endif
diff --git a/drivers/soc/qcom/smd_init_dt.c b/drivers/soc/qcom/smd_init_dt.c
new file mode 100644
index 0000000..f14461f
--- /dev/null
+++ b/drivers/soc/qcom/smd_init_dt.c
@@ -0,0 +1,343 @@
+/* drivers/soc/qcom/smd_init_dt.c
+ *
+ * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ipc_logging.h>
+
+#include "smd_private.h"
+
+#define MODULE_NAME "msm_smd"
+#define IPC_LOG(level, x...) do { \
+	if (smd_log_ctx) \
+		ipc_log_string(smd_log_ctx, x); \
+	else \
+		printk(level x); \
+	} while (0)
+
+#if defined(CONFIG_MSM_SMD_DEBUG)
+#define SMD_DBG(x...) do {				\
+		if (msm_smd_debug_mask & MSM_SMD_DEBUG) \
+			IPC_LOG(KERN_DEBUG, x);		\
+	} while (0)
+
+#define SMSM_DBG(x...) do {					\
+		if (msm_smd_debug_mask & MSM_SMSM_DEBUG)	\
+			IPC_LOG(KERN_DEBUG, x);		\
+	} while (0)
+#else
+#define SMD_DBG(x...) do { } while (0)
+#define SMSM_DBG(x...) do { } while (0)
+#endif
+
+static DEFINE_MUTEX(smd_probe_lock);
+static int first_probe_done;
+
+static int msm_smsm_probe(struct platform_device *pdev)
+{
+	uint32_t edge;
+	char *key;
+	int ret;
+	uint32_t irq_offset;
+	uint32_t irq_bitmask;
+	uint32_t irq_line;
+	struct interrupt_config_item *private_irq;
+	struct device_node *node;
+	void *irq_out_base;
+	resource_size_t irq_out_size;
+	struct platform_device *parent_pdev;
+	struct resource *r;
+	struct interrupt_config *private_intr_config;
+	uint32_t remote_pid;
+
+	node = pdev->dev.of_node;
+
+	if (!pdev->dev.parent) {
+		pr_err("%s: missing link to parent device\n", __func__);
+		return -ENODEV;
+	}
+
+	parent_pdev = to_platform_device(pdev->dev.parent);
+
+	key = "irq-reg-base";
+	r = platform_get_resource_byname(parent_pdev, IORESOURCE_MEM, key);
+	if (!r)
+		goto missing_key;
+	irq_out_size = resource_size(r);
+	irq_out_base = ioremap_nocache(r->start, irq_out_size);
+	if (!irq_out_base) {
+		pr_err("%s: ioremap_nocache() of irq_out_base addr:%pr size:%pr\n",
+				__func__, &r->start, &irq_out_size);
+		return -ENOMEM;
+	}
+	SMSM_DBG("%s: %s = %p", __func__, key, irq_out_base);
+
+	key = "qcom,smsm-edge";
+	ret = of_property_read_u32(node, key, &edge);
+	if (ret)
+		goto missing_key;
+	SMSM_DBG("%s: %s = %d", __func__, key, edge);
+
+	key = "qcom,smsm-irq-offset";
+	ret = of_property_read_u32(node, key, &irq_offset);
+	if (ret)
+		goto missing_key;
+	SMSM_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+	key = "qcom,smsm-irq-bitmask";
+	ret = of_property_read_u32(node, key, &irq_bitmask);
+	if (ret)
+		goto missing_key;
+	SMSM_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+	key = "interrupts";
+	irq_line = irq_of_parse_and_map(node, 0);
+	if (!irq_line)
+		goto missing_key;
+	SMSM_DBG("%s: %s = %d", __func__, key, irq_line);
+
+	private_intr_config = smd_get_intr_config(edge);
+	if (!private_intr_config) {
+		pr_err("%s: invalid edge\n", __func__);
+		iounmap(irq_out_base);
+		return -ENODEV;
+	}
+	private_irq = &private_intr_config->smsm;
+	private_irq->out_bit_pos = irq_bitmask;
+	private_irq->out_offset = irq_offset;
+	private_irq->out_base = irq_out_base;
+	private_irq->irq_id = irq_line;
+	remote_pid = smd_edge_to_remote_pid(edge);
+	interrupt_stats[remote_pid].smsm_interrupt_id = irq_line;
+
+	ret = request_irq(irq_line,
+				private_irq->irq_handler,
+				IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
+				node->name,
+				NULL);
+	if (ret < 0) {
+		pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+		iounmap(irq_out_base);
+		return ret;
+	}
+	ret = enable_irq_wake(irq_line);
+	if (ret < 0)
+		pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+			irq_line);
+
+	ret = smsm_post_init();
+	if (ret) {
+		pr_err("smd_post_init() failed ret=%d\n", ret);
+		iounmap(irq_out_base);
+		free_irq(irq_line, NULL);
+		return ret;
+	}
+
+	return 0;
+
+missing_key:
+	pr_err("%s: missing key: %s", __func__, key);
+	return -ENODEV;
+}
+
+static int msm_smd_probe(struct platform_device *pdev)
+{
+	uint32_t edge;
+	char *key;
+	int ret;
+	uint32_t irq_offset;
+	uint32_t irq_bitmask;
+	uint32_t irq_line;
+	const char *subsys_name;
+	struct interrupt_config_item *private_irq;
+	struct device_node *node;
+	void *irq_out_base;
+	resource_size_t irq_out_size;
+	struct platform_device *parent_pdev;
+	struct resource *r;
+	struct interrupt_config *private_intr_config;
+	uint32_t remote_pid;
+	bool skip_pil;
+
+	node = pdev->dev.of_node;
+
+	if (!pdev->dev.parent) {
+		pr_err("%s: missing link to parent device\n", __func__);
+		return -ENODEV;
+	}
+
+	mutex_lock(&smd_probe_lock);
+	if (!first_probe_done) {
+		smd_reset_all_edge_subsys_name();
+		first_probe_done = 1;
+	}
+	mutex_unlock(&smd_probe_lock);
+
+	parent_pdev = to_platform_device(pdev->dev.parent);
+
+	key = "irq-reg-base";
+	r = platform_get_resource_byname(parent_pdev, IORESOURCE_MEM, key);
+	if (!r)
+		goto missing_key;
+	irq_out_size = resource_size(r);
+	irq_out_base = ioremap_nocache(r->start, irq_out_size);
+	if (!irq_out_base) {
+		pr_err("%s: ioremap_nocache() of irq_out_base addr:%pr size:%pr\n",
+				__func__, &r->start, &irq_out_size);
+		return -ENOMEM;
+	}
+	SMD_DBG("%s: %s = %p", __func__, key, irq_out_base);
+
+	key = "qcom,smd-edge";
+	ret = of_property_read_u32(node, key, &edge);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %d", __func__, key, edge);
+
+	key = "qcom,smd-irq-offset";
+	ret = of_property_read_u32(node, key, &irq_offset);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %x", __func__, key, irq_offset);
+
+	key = "qcom,smd-irq-bitmask";
+	ret = of_property_read_u32(node, key, &irq_bitmask);
+	if (ret)
+		goto missing_key;
+	SMD_DBG("%s: %s = %x", __func__, key, irq_bitmask);
+
+	key = "interrupts";
+	irq_line = irq_of_parse_and_map(node, 0);
+	if (!irq_line)
+		goto missing_key;
+	SMD_DBG("%s: %s = %d", __func__, key, irq_line);
+
+	key = "label";
+	subsys_name = of_get_property(node, key, NULL);
+	SMD_DBG("%s: %s = %s", __func__, key, subsys_name);
+	/*
+	 * Backwards compatibility.  Although label is required, some DTs may
+	 * still list the legacy pil-string.  Sanely handle pil-string.
+	 */
+	if (!subsys_name) {
+		pr_warn("msm_smd: Missing required property - label. Using legacy parsing\n");
+		key = "qcom,pil-string";
+		subsys_name = of_get_property(node, key, NULL);
+		SMD_DBG("%s: %s = %s", __func__, key, subsys_name);
+		if (subsys_name)
+			skip_pil = false;
+		else
+			skip_pil = true;
+	} else {
+		key = "qcom,not-loadable";
+		skip_pil = of_property_read_bool(node, key);
+		SMD_DBG("%s: %s = %d\n", __func__, key, skip_pil);
+	}
+
+	private_intr_config = smd_get_intr_config(edge);
+	if (!private_intr_config) {
+		pr_err("%s: invalid edge\n", __func__);
+		return -ENODEV;
+	}
+	private_irq = &private_intr_config->smd;
+	private_irq->out_bit_pos = irq_bitmask;
+	private_irq->out_offset = irq_offset;
+	private_irq->out_base = irq_out_base;
+	private_irq->irq_id = irq_line;
+	remote_pid = smd_edge_to_remote_pid(edge);
+	interrupt_stats[remote_pid].smd_interrupt_id = irq_line;
+
+	ret = request_irq(irq_line,
+			private_irq->irq_handler,
+			IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND | IRQF_SHARED,
+			node->name,
+			&pdev->dev);
+	if (ret < 0) {
+		pr_err("%s: request_irq() failed on %d\n", __func__, irq_line);
+		return ret;
+	}
+
+	ret = enable_irq_wake(irq_line);
+	if (ret < 0)
+		pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
+				irq_line);
+
+	smd_set_edge_subsys_name(edge, subsys_name);
+	smd_proc_set_skip_pil(smd_edge_to_remote_pid(edge), skip_pil);
+
+	smd_set_edge_initialized(edge);
+	smd_post_init(remote_pid);
+	return 0;
+
+missing_key:
+	pr_err("%s: missing key: %s", __func__, key);
+	return -ENODEV;
+}
+
+static const struct of_device_id msm_smd_match_table[] = {
+	{ .compatible = "qcom,smd" },
+	{},
+};
+
+static struct platform_driver msm_smd_driver = {
+	.probe = msm_smd_probe,
+	.driver = {
+		.name = MODULE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smd_match_table,
+	},
+};
+
+static const struct of_device_id msm_smsm_match_table[] = {
+	{ .compatible = "qcom,smsm" },
+	{},
+};
+
+static struct platform_driver msm_smsm_driver = {
+	.probe = msm_smsm_probe,
+	.driver = {
+		.name = "msm_smsm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smsm_match_table,
+	},
+};
+
+int msm_smd_driver_register(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&msm_smd_driver);
+	if (rc) {
+		pr_err("%s: smd_driver register failed %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = platform_driver_register(&msm_smsm_driver);
+	if (rc) {
+		pr_err("%s: msm_smsm_driver register failed %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_smd_driver_register);
+
+MODULE_DESCRIPTION("MSM SMD Device Tree Init");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/smd_private.c b/drivers/soc/qcom/smd_private.c
new file mode 100644
index 0000000..a554696
--- /dev/null
+++ b/drivers/soc/qcom/smd_private.c
@@ -0,0 +1,336 @@
+/* Copyright (c) 2012, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 "smd_private.h"
+
+void set_state(volatile void __iomem *half_channel, unsigned int data)
+{
+	((struct smd_half_channel __force *)(half_channel))->state = data;
+}
+
+unsigned int get_state(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->state;
+}
+
+void set_fDSR(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fDSR = data;
+}
+
+unsigned int get_fDSR(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fDSR;
+}
+
+void set_fCTS(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fCTS = data;
+}
+
+unsigned int get_fCTS(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fCTS;
+}
+
+void set_fCD(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fCD = data;
+}
+
+unsigned int get_fCD(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fCD;
+}
+
+void set_fRI(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fRI = data;
+}
+
+unsigned int get_fRI(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fRI;
+}
+
+void set_fHEAD(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fHEAD = data;
+}
+
+unsigned int get_fHEAD(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fHEAD;
+}
+
+void set_fTAIL(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fTAIL = data;
+}
+
+unsigned int get_fTAIL(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fTAIL;
+}
+
+void set_fSTATE(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)(half_channel))->fSTATE = data;
+}
+
+unsigned int get_fSTATE(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->fSTATE;
+}
+
+void set_fBLOCKREADINTR(volatile void __iomem *half_channel, unsigned char data)
+{
+	((struct smd_half_channel __force *)
+				(half_channel))->fBLOCKREADINTR = data;
+}
+
+unsigned int get_fBLOCKREADINTR(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)
+				(half_channel))->fBLOCKREADINTR;
+}
+
+void set_tail(volatile void __iomem *half_channel, unsigned int data)
+{
+	((struct smd_half_channel __force *)(half_channel))->tail = data;
+}
+
+unsigned int get_tail(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->tail;
+}
+
+void set_head(volatile void __iomem *half_channel, unsigned int data)
+{
+	((struct smd_half_channel __force *)(half_channel))->head = data;
+}
+
+unsigned int get_head(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel __force *)(half_channel))->head;
+}
+
+void set_state_word_access(volatile void __iomem *half_channel,
+				unsigned int data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->state = data;
+}
+
+unsigned int get_state_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->state;
+}
+
+void set_fDSR_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fDSR = data;
+}
+
+unsigned int get_fDSR_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fDSR;
+}
+
+void set_fCTS_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fCTS = data;
+}
+
+unsigned int get_fCTS_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fCTS;
+}
+
+void set_fCD_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fCD = data;
+}
+
+unsigned int get_fCD_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fCD;
+}
+
+void set_fRI_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fRI = data;
+}
+
+unsigned int get_fRI_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fRI;
+}
+
+void set_fHEAD_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fHEAD = data;
+}
+
+unsigned int get_fHEAD_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fHEAD;
+}
+
+void set_fTAIL_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fTAIL = data;
+}
+
+unsigned int get_fTAIL_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fTAIL;
+}
+
+void set_fSTATE_word_access(volatile void __iomem *half_channel,
+						unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fSTATE = data;
+}
+
+unsigned int get_fSTATE_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fSTATE;
+}
+
+void set_fBLOCKREADINTR_word_access(volatile void __iomem *half_channel,
+							unsigned char data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->fBLOCKREADINTR = data;
+}
+
+unsigned int get_fBLOCKREADINTR_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->fBLOCKREADINTR;
+}
+
+void set_tail_word_access(volatile void __iomem *half_channel,
+				unsigned int data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->tail = data;
+}
+
+unsigned int get_tail_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->tail;
+}
+
+void set_head_word_access(volatile void __iomem *half_channel,
+				unsigned int data)
+{
+	((struct smd_half_channel_word_access __force *)
+					(half_channel))->head = data;
+}
+
+unsigned int get_head_word_access(volatile void __iomem *half_channel)
+{
+	return ((struct smd_half_channel_word_access __force *)
+					(half_channel))->head;
+}
+
+int is_word_access_ch(unsigned int ch_type)
+{
+	if (ch_type == SMD_APPS_RPM || ch_type == SMD_MODEM_RPM ||
+		ch_type == SMD_QDSP_RPM || ch_type == SMD_WCNSS_RPM ||
+		ch_type == SMD_TZ_RPM)
+		return 1;
+	else
+		return 0;
+}
+
+struct smd_half_channel_access *get_half_ch_funcs(unsigned int ch_type)
+{
+	static struct smd_half_channel_access byte_access = {
+		.set_state = set_state,
+		.get_state = get_state,
+		.set_fDSR = set_fDSR,
+		.get_fDSR = get_fDSR,
+		.set_fCTS = set_fCTS,
+		.get_fCTS = get_fCTS,
+		.set_fCD = set_fCD,
+		.get_fCD = get_fCD,
+		.set_fRI = set_fRI,
+		.get_fRI = get_fRI,
+		.set_fHEAD = set_fHEAD,
+		.get_fHEAD = get_fHEAD,
+		.set_fTAIL = set_fTAIL,
+		.get_fTAIL = get_fTAIL,
+		.set_fSTATE = set_fSTATE,
+		.get_fSTATE = get_fSTATE,
+		.set_fBLOCKREADINTR = set_fBLOCKREADINTR,
+		.get_fBLOCKREADINTR = get_fBLOCKREADINTR,
+		.set_tail = set_tail,
+		.get_tail = get_tail,
+		.set_head = set_head,
+		.get_head = get_head,
+	};
+	static struct smd_half_channel_access word_access = {
+		.set_state = set_state_word_access,
+		.get_state = get_state_word_access,
+		.set_fDSR = set_fDSR_word_access,
+		.get_fDSR = get_fDSR_word_access,
+		.set_fCTS = set_fCTS_word_access,
+		.get_fCTS = get_fCTS_word_access,
+		.set_fCD = set_fCD_word_access,
+		.get_fCD = get_fCD_word_access,
+		.set_fRI = set_fRI_word_access,
+		.get_fRI = get_fRI_word_access,
+		.set_fHEAD = set_fHEAD_word_access,
+		.get_fHEAD = get_fHEAD_word_access,
+		.set_fTAIL = set_fTAIL_word_access,
+		.get_fTAIL = get_fTAIL_word_access,
+		.set_fSTATE = set_fSTATE_word_access,
+		.get_fSTATE = get_fSTATE_word_access,
+		.set_fBLOCKREADINTR = set_fBLOCKREADINTR_word_access,
+		.get_fBLOCKREADINTR = get_fBLOCKREADINTR_word_access,
+		.set_tail = set_tail_word_access,
+		.get_tail = get_tail_word_access,
+		.set_head = set_head_word_access,
+		.get_head = get_head_word_access,
+	};
+
+	if (is_word_access_ch(ch_type))
+		return &word_access;
+	else
+		return &byte_access;
+}
+
diff --git a/drivers/soc/qcom/smd_private.h b/drivers/soc/qcom/smd_private.h
new file mode 100644
index 0000000..98d0bde
--- /dev/null
+++ b/drivers/soc/qcom/smd_private.h
@@ -0,0 +1,246 @@
+/* drivers/soc/qcom/smd_private.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2014, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_
+#define _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/remote_spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <soc/qcom/smd.h>
+#include <soc/qcom/smsm.h>
+
+#define VERSION_QDSP6     4
+#define VERSION_APPS_SBL  6
+#define VERSION_MODEM_SBL 7
+#define VERSION_APPS      8
+#define VERSION_MODEM     9
+#define VERSION_DSPS      10
+
+#define ID_SMD_CHANNELS SMEM_SMD_BASE_ID
+#define ID_SHARED_STATE SMEM_SMSM_SHARED_STATE
+#define ID_CH_ALLOC_TBL SMEM_CHANNEL_ALLOC_TBL
+
+#define SMD_SS_CLOSED            0x00000000
+#define SMD_SS_OPENING           0x00000001
+#define SMD_SS_OPENED            0x00000002
+#define SMD_SS_FLUSHING          0x00000003
+#define SMD_SS_CLOSING           0x00000004
+#define SMD_SS_RESET             0x00000005
+#define SMD_SS_RESET_OPENING     0x00000006
+
+#define SMD_HEADER_SIZE          20
+
+/* 'type' field of smd_alloc_elm structure
+ * has the following breakup
+ * bits 0-7   -> channel type
+ * bits 8-11  -> xfer type
+ * bits 12-31 -> reserved
+ */
+struct smd_alloc_elm {
+	char name[20];
+	uint32_t cid;
+	uint32_t type;
+	uint32_t ref_count;
+};
+
+#define SMD_CHANNEL_TYPE(x) ((x) & 0x000000FF)
+#define SMD_XFER_TYPE(x)    (((x) & 0x00000F00) >> 8)
+
+struct smd_half_channel {
+	unsigned int state;
+	unsigned char fDSR;
+	unsigned char fCTS;
+	unsigned char fCD;
+	unsigned char fRI;
+	unsigned char fHEAD;
+	unsigned char fTAIL;
+	unsigned char fSTATE;
+	unsigned char fBLOCKREADINTR;
+	unsigned int tail;
+	unsigned int head;
+};
+
+struct smd_half_channel_word_access {
+	unsigned int state;
+	unsigned int fDSR;
+	unsigned int fCTS;
+	unsigned int fCD;
+	unsigned int fRI;
+	unsigned int fHEAD;
+	unsigned int fTAIL;
+	unsigned int fSTATE;
+	unsigned int fBLOCKREADINTR;
+	unsigned int tail;
+	unsigned int head;
+};
+
+struct smd_half_channel_access {
+	void (*set_state)(volatile void __iomem *half_channel,
+					unsigned int data);
+	unsigned int (*get_state)(volatile void __iomem *half_channel);
+	void (*set_fDSR)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fDSR)(volatile void __iomem *half_channel);
+	void (*set_fCTS)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fCTS)(volatile void __iomem *half_channel);
+	void (*set_fCD)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fCD)(volatile void __iomem *half_channel);
+	void (*set_fRI)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fRI)(volatile void __iomem *half_channel);
+	void (*set_fHEAD)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fHEAD)(volatile void __iomem *half_channel);
+	void (*set_fTAIL)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fTAIL)(volatile void __iomem *half_channel);
+	void (*set_fSTATE)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fSTATE)(volatile void __iomem *half_channel);
+	void (*set_fBLOCKREADINTR)(volatile void __iomem *half_channel,
+					unsigned char data);
+	unsigned int (*get_fBLOCKREADINTR)(volatile void __iomem *half_channel);
+	void (*set_tail)(volatile void __iomem *half_channel,
+					unsigned int data);
+	unsigned int (*get_tail)(volatile void __iomem *half_channel);
+	void (*set_head)(volatile void __iomem *half_channel,
+					unsigned int data);
+	unsigned int (*get_head)(volatile void __iomem *half_channel);
+};
+
+int is_word_access_ch(unsigned int ch_type);
+
+struct smd_half_channel_access *get_half_ch_funcs(unsigned int ch_type);
+
+struct smd_channel {
+	volatile void __iomem *send; /* some variant of smd_half_channel */
+	volatile void __iomem *recv; /* some variant of smd_half_channel */
+	unsigned char *send_data;
+	unsigned char *recv_data;
+	unsigned int fifo_size;
+	struct list_head ch_list;
+
+	unsigned int current_packet;
+	unsigned int n;
+	void *priv;
+	void (*notify)(void *priv, unsigned int flags);
+
+	int (*read)(smd_channel_t *ch, void *data, int len);
+	int (*write)(smd_channel_t *ch, const void *data, int len,
+			bool int_ntfy);
+	int (*read_avail)(smd_channel_t *ch);
+	int (*write_avail)(smd_channel_t *ch);
+	int (*read_from_cb)(smd_channel_t *ch, void *data, int len);
+
+	void (*update_state)(smd_channel_t *ch);
+	unsigned int last_state;
+	void (*notify_other_cpu)(smd_channel_t *ch);
+	void * (*read_from_fifo)(void *dest, const void *src, size_t num_bytes);
+	void * (*write_to_fifo)(void *dest, const void *src, size_t num_bytes);
+
+	char name[20];
+	struct platform_device pdev;
+	unsigned int type;
+
+	int pending_pkt_sz;
+
+	char is_pkt_ch;
+
+	/*
+	 * private internal functions to access *send and *recv.
+	 * never to be exported outside of smd
+	 */
+	struct smd_half_channel_access *half_ch;
+};
+
+extern spinlock_t smem_lock;
+
+struct interrupt_stat {
+	uint32_t smd_in_count;
+	uint32_t smd_out_count;
+	uint32_t smd_interrupt_id;
+
+	uint32_t smsm_in_count;
+	uint32_t smsm_out_count;
+	uint32_t smsm_interrupt_id;
+};
+extern struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
+
+struct interrupt_config_item {
+	/* must be initialized */
+	irqreturn_t (*irq_handler)(int req, void *data);
+	/* outgoing interrupt config (set from platform data) */
+	uint32_t out_bit_pos;
+	void __iomem *out_base;
+	uint32_t out_offset;
+	int irq_id;
+};
+
+enum {
+	MSM_SMD_DEBUG = 1U << 0,
+	MSM_SMSM_DEBUG = 1U << 1,
+	MSM_SMD_INFO = 1U << 2,
+	MSM_SMSM_INFO = 1U << 3,
+	MSM_SMD_POWER_INFO = 1U << 4,
+	MSM_SMSM_POWER_INFO = 1U << 5,
+};
+
+struct interrupt_config {
+	struct interrupt_config_item smd;
+	struct interrupt_config_item smsm;
+};
+
+struct edge_to_pid {
+	uint32_t	local_pid;
+	uint32_t	remote_pid;
+	char		subsys_name[SMD_MAX_CH_NAME_LEN];
+	bool		initialized;
+};
+
+extern void *smd_log_ctx;
+extern int msm_smd_debug_mask;
+
+extern irqreturn_t smd_modem_irq_handler(int irq, void *data);
+extern irqreturn_t smsm_modem_irq_handler(int irq, void *data);
+extern irqreturn_t smd_dsp_irq_handler(int irq, void *data);
+extern irqreturn_t smsm_dsp_irq_handler(int irq, void *data);
+extern irqreturn_t smd_dsps_irq_handler(int irq, void *data);
+extern irqreturn_t smsm_dsps_irq_handler(int irq, void *data);
+extern irqreturn_t smd_wcnss_irq_handler(int irq, void *data);
+extern irqreturn_t smsm_wcnss_irq_handler(int irq, void *data);
+extern irqreturn_t smd_rpm_irq_handler(int irq, void *data);
+extern irqreturn_t smd_modemfw_irq_handler(int irq, void *data);
+
+extern int msm_smd_driver_register(void);
+extern void smd_post_init(unsigned int remote_pid);
+extern int smsm_post_init(void);
+
+extern struct interrupt_config *smd_get_intr_config(uint32_t edge);
+extern int smd_edge_to_remote_pid(uint32_t edge);
+extern int smd_edge_to_local_pid(uint32_t edge);
+extern void smd_set_edge_subsys_name(uint32_t edge, const char *subsys_name);
+extern void smd_reset_all_edge_subsys_name(void);
+extern void smd_proc_set_skip_pil(unsigned int pid, bool skip_pil);
+extern void smd_set_edge_initialized(uint32_t edge);
+extern void smd_cfg_smd_intr(uint32_t proc, uint32_t mask, void *ptr);
+extern void smd_cfg_smsm_intr(uint32_t proc, uint32_t mask, void *ptr);
+#endif
diff --git a/drivers/soc/qcom/smsm_debug.c b/drivers/soc/qcom/smsm_debug.c
new file mode 100644
index 0000000..b9b97ef
--- /dev/null
+++ b/drivers/soc/qcom/smsm_debug.c
@@ -0,0 +1,330 @@
+/* drivers/soc/qcom/smsm_debug.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2014, 2017, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+
+#include <soc/qcom/smem.h>
+#include <soc/qcom/smsm.h>
+
+#if defined(CONFIG_DEBUG_FS)
+
+
+static void debug_read_smsm_state(struct seq_file *s)
+{
+	uint32_t *smsm;
+	int n;
+
+	smsm = smem_find(SMEM_SMSM_SHARED_STATE,
+			 SMSM_NUM_ENTRIES * sizeof(uint32_t),
+			 0,
+			 SMEM_ANY_HOST_FLAG);
+
+	if (smsm)
+		for (n = 0; n < SMSM_NUM_ENTRIES; n++)
+			seq_printf(s, "entry %d: 0x%08x\n", n, smsm[n]);
+}
+
+struct SMSM_CB_DATA {
+	int cb_count;
+	void *data;
+	uint32_t old_state;
+	uint32_t new_state;
+};
+static struct SMSM_CB_DATA smsm_cb_data;
+static struct completion smsm_cb_completion;
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+	smsm_cb_data.cb_count++;
+	smsm_cb_data.old_state = old_state;
+	smsm_cb_data.new_state = new_state;
+	smsm_cb_data.data = data;
+	complete_all(&smsm_cb_completion);
+}
+
+#define UT_EQ_INT(a, b) \
+	{ \
+		if ((a) != (b)) { \
+			seq_printf(s, "%s:%d " #a "(%d) != " #b "(%d)\n", \
+					__func__, __LINE__, \
+					a, b); \
+			break; \
+		} \
+	}
+
+#define UT_GT_INT(a, b) \
+	{ \
+		if ((a) <= (b)) { \
+			seq_printf(s, "%s:%d " #a "(%d) > " #b "(%d)\n", \
+					__func__, __LINE__, \
+					a, b); \
+			break; \
+		} \
+	}
+
+#define SMSM_CB_TEST_INIT() \
+	do { \
+		smsm_cb_data.cb_count = 0; \
+		smsm_cb_data.old_state = 0; \
+		smsm_cb_data.new_state = 0; \
+		smsm_cb_data.data = 0; \
+	} while (0)
+
+
+static void debug_test_smsm(struct seq_file *s)
+{
+	int test_num = 0;
+	int ret;
+
+	/* Test case 1 - Register new callback for notification */
+	do {
+		test_num++;
+		SMSM_CB_TEST_INIT();
+		ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 0);
+
+		/* de-assert SMSM_SMD_INIT to trigger state update */
+		UT_EQ_INT(smsm_cb_data.cb_count, 0);
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+
+		UT_EQ_INT(smsm_cb_data.cb_count, 1);
+		UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, SMSM_SMDINIT);
+		UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, 0x0);
+		UT_EQ_INT((int)(uintptr_t)smsm_cb_data.data, 0x1234);
+
+		/* re-assert SMSM_SMD_INIT to trigger state update */
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 2);
+		UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, 0x0);
+		UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, SMSM_SMDINIT);
+
+		/* deregister callback */
+		ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 2);
+
+		/* make sure state change doesn't cause any more callbacks */
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+		UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 2);
+
+		seq_printf(s, "Test %d - PASS\n", test_num);
+	} while (0);
+
+	/* Test case 2 - Update already registered callback */
+	do {
+		test_num++;
+		SMSM_CB_TEST_INIT();
+		ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 0);
+		ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_INIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 1);
+
+		/* verify both callback bits work */
+		reinit_completion(&smsm_cb_completion);
+		UT_EQ_INT(smsm_cb_data.cb_count, 0);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 1);
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 2);
+
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 3);
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 4);
+
+		/* deregister 1st callback */
+		ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 1);
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+		UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 4);
+
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 5);
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 6);
+
+		/* deregister 2nd callback */
+		ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_INIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 2);
+
+		/* make sure state change doesn't cause any more callbacks */
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+		UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 6);
+
+		seq_printf(s, "Test %d - PASS\n", test_num);
+	} while (0);
+
+	/* Test case 3 - Two callback registrations with different data */
+	do {
+		test_num++;
+		SMSM_CB_TEST_INIT();
+		ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 0);
+		ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_INIT,
+				smsm_state_cb, (void *)0x3456);
+		UT_EQ_INT(ret, 0);
+
+		/* verify both callbacks work */
+		reinit_completion(&smsm_cb_completion);
+		UT_EQ_INT(smsm_cb_data.cb_count, 0);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 1);
+		UT_EQ_INT((int)(uintptr_t)smsm_cb_data.data, 0x1234);
+
+		reinit_completion(&smsm_cb_completion);
+		smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0);
+		UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion,
+					msecs_to_jiffies(20)), 0);
+		UT_EQ_INT(smsm_cb_data.cb_count, 2);
+		UT_EQ_INT((int)(uintptr_t)smsm_cb_data.data, 0x3456);
+
+		/* cleanup and unregister
+		 * degregister in reverse to verify data field is
+		 * being used
+		 */
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT);
+		smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT);
+		ret = smsm_state_cb_deregister(SMSM_APPS_STATE,
+				SMSM_INIT,
+				smsm_state_cb, (void *)0x3456);
+		UT_EQ_INT(ret, 2);
+		ret = smsm_state_cb_deregister(SMSM_APPS_STATE,
+				SMSM_SMDINIT,
+				smsm_state_cb, (void *)0x1234);
+		UT_EQ_INT(ret, 2);
+
+		seq_printf(s, "Test %d - PASS\n", test_num);
+	} while (0);
+}
+
+static void debug_read_intr_mask(struct seq_file *s)
+{
+	uint32_t *smsm;
+	int m, n;
+
+	smsm = smem_find(SMEM_SMSM_CPU_INTR_MASK,
+			  SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t),
+			  0,
+			  SMEM_ANY_HOST_FLAG);
+
+	if (smsm)
+		for (m = 0; m < SMSM_NUM_ENTRIES; m++) {
+			seq_printf(s, "entry %d:", m);
+			for (n = 0; n < SMSM_NUM_HOSTS; n++)
+				seq_printf(s, "   host %d: 0x%08x",
+					       n, smsm[m * SMSM_NUM_HOSTS + n]);
+			seq_puts(s, "\n");
+		}
+}
+
+static int debugfs_show(struct seq_file *s, void *data)
+{
+	void (*show)(struct seq_file *) = s->private;
+
+	show(s);
+
+	return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debugfs_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+	.open = debug_open,
+	.release = single_release,
+	.read = seq_read,
+	.llseek = seq_lseek,
+};
+
+static void debug_create(const char *name, umode_t mode,
+			 struct dentry *dent,
+			 void (*show)(struct seq_file *))
+{
+	struct dentry *file;
+
+	file = debugfs_create_file(name, mode, dent, show, &debug_ops);
+	if (!file)
+		pr_err("%s: unable to create file '%s'\n", __func__, name);
+}
+
+static int __init smsm_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("smsm", 0);
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	debug_create("state", 0444, dent, debug_read_smsm_state);
+	debug_create("intr_mask", 0444, dent, debug_read_intr_mask);
+	debug_create("smsm_test", 0444, dent, debug_test_smsm);
+
+	init_completion(&smsm_cb_completion);
+
+	return 0;
+}
+
+late_initcall(smsm_debugfs_init);
+#endif
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/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 626cfdc..0c5e9ca 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1690,6 +1690,16 @@
 	  and warnings and which allows logins in single user mode)
 	  Otherwise, say 'N'.
 
+config SERIAL_MSM_SMD
+	bool "Enable tty device interface for some SMD ports"
+	default n
+	depends on MSM_SMD
+	help
+	  This driver provides the interface for the userspace clients
+	  to communicate over smd via device nodes. This enable the
+	  usersapce clients to read and write to some streaming SMD ports
+	  via tty device interface for MSM chipset.
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 1bdc7f8..882fff9 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -98,3 +98,4 @@
 
 # GPIOLIB helpers for modem control lines
 obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
+obj-$(CONFIG_SERIAL_MSM_SMD)	+= msm_smd_tty.o
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index b142869..899524d 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -164,6 +164,7 @@
 	int ioctl_count;
 	int edge_count;
 	unsigned int tx_yield_count;
+	bool manual_flow;
 };
 
 static const struct uart_ops msm_geni_serial_pops;
@@ -266,16 +267,18 @@
 
 	/* Possible stop tx is called multiple times. */
 	m_cmd_active = geni_status & M_GENI_CMD_ACTIVE;
-	if (port->xfer_mode == SE_DMA)
+	if (port->xfer_mode == SE_DMA) {
 		tx_fifo_status = port->tx_dma ? 1 : 0;
-	else
+		rx_fifo_status =
+			geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);
+	} else {
 		tx_fifo_status = geni_read_reg_nolog(uport->membase,
 						SE_GENI_TX_FIFO_STATUS);
-	tx_active = m_cmd_active || tx_fifo_status;
-	rx_fifo_status = geni_read_reg_nolog(uport->membase,
+		rx_fifo_status = geni_read_reg_nolog(uport->membase,
 						SE_GENI_RX_FIFO_STATUS);
-	if (rx_fifo_status)
-		rx_active = true;
+	}
+	tx_active = m_cmd_active || tx_fifo_status;
+	rx_active =  rx_fifo_status ? true : false;
 
 	if (rx_active || tx_active || !uart_circ_empty(xmit))
 		xfer_on = true;
@@ -303,10 +306,12 @@
 		u32 geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
 		u32 rx_fifo_status = geni_read_reg_nolog(uport->membase,
 							SE_GENI_RX_FIFO_STATUS);
+		u32 rx_dma =
+			geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);
 
 		IPC_LOG_MSG(port->ipc_log_misc,
-			"%s IOS 0x%x geni status 0x%x rx fifo 0x%x\n",
-			__func__, geni_ios, geni_status, rx_fifo_status);
+			"%s IOS 0x%x geni status 0x%x rx: fifo 0x%x dma 0x%x\n",
+		__func__, geni_ios, geni_status, rx_fifo_status, rx_dma);
 	}
 }
 
@@ -405,13 +410,9 @@
 {
 	u32 geni_ios = 0;
 	unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
-	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	if (device_pending_suspend(uport)) {
-		IPC_LOG_MSG(port->ipc_log_misc,
-				"%s.Device is suspended.\n", __func__);
+	if (device_pending_suspend(uport))
 		return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
-	}
 
 	geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
 	if (!(geni_ios & IO2_DATA_IN))
@@ -436,8 +437,12 @@
 				"%s.Device is suspended.\n", __func__);
 		return;
 	}
-	if (!(mctrl & TIOCM_RTS))
+	if (!(mctrl & TIOCM_RTS)) {
 		uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_NOT_READY);
+		port->manual_flow = true;
+	} else {
+		port->manual_flow = false;
+	}
 	geni_write_reg_nolog(uart_manual_rfr, uport->membase,
 							SE_UART_MANUAL_RFR);
 	/* Write to flow control must complete before return to client*/
@@ -542,7 +547,7 @@
 		 * Total polling iterations based on FIFO worth of bytes to be
 		 * sent at current baud .Add a little fluff to the wait.
 		 */
-		total_iter = ((fifo_bits * USEC_PER_SEC) / baud);
+		total_iter = ((fifo_bits * USEC_PER_SEC) / baud) / 10;
 		total_iter += 50;
 	}
 
@@ -920,19 +925,12 @@
 	geni_write_reg_nolog(tx_irq_en, uport->membase, SE_DMA_TX_IRQ_EN_SET);
 }
 
-static void msm_geni_serial_stop_tx(struct uart_port *uport)
+static void stop_tx_sequencer(struct uart_port *uport)
 {
 	unsigned int geni_m_irq_en;
 	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	if (!uart_console(uport) && device_pending_suspend(uport)) {
-		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
-		IPC_LOG_MSG(port->ipc_log_misc,
-				"%s.Device is suspended.\n", __func__);
-		return;
-	}
-
 	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
 	geni_m_irq_en &= ~M_CMD_DONE_EN;
 	if (port->xfer_mode == FIFO_MODE) {
@@ -948,9 +946,7 @@
 		}
 	}
 	port->xmit_size = 0;
-
 	geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN);
-
 	geni_status = geni_read_reg_nolog(uport->membase,
 						SE_GENI_STATUS);
 	/* Possible stop tx is called multiple times. */
@@ -970,13 +966,9 @@
 	IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
 }
 
-static void msm_geni_serial_start_rx(struct uart_port *uport)
+static void msm_geni_serial_stop_tx(struct uart_port *uport)
 {
-	unsigned int geni_s_irq_en;
-	unsigned int geni_m_irq_en;
-	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
-	int ret;
 
 	if (!uart_console(uport) && device_pending_suspend(uport)) {
 		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
@@ -984,6 +976,16 @@
 				"%s.Device is suspended.\n", __func__);
 		return;
 	}
+	stop_tx_sequencer(uport);
+}
+
+static void start_rx_sequencer(struct uart_port *uport)
+{
+	unsigned int geni_s_irq_en;
+	unsigned int geni_m_irq_en;
+	unsigned int geni_status;
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+	int ret;
 
 	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
 	if (geni_status & S_GENI_CMD_ACTIVE)
@@ -1011,7 +1013,7 @@
 			dev_err(uport->dev, "%s: RX Prep dma failed %d\n",
 				__func__, ret);
 			msm_geni_serial_stop_rx(uport);
-			goto exit_geni_serial_start_rx;
+			goto exit_start_rx_sequencer;
 		}
 	}
 	/*
@@ -1020,10 +1022,24 @@
 	 */
 	mb();
 	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
-exit_geni_serial_start_rx:
+exit_start_rx_sequencer:
 	IPC_LOG_MSG(port->ipc_log_misc, "%s 0x%x\n", __func__, geni_status);
 }
 
+static void msm_geni_serial_start_rx(struct uart_port *uport)
+{
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
+		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
+		IPC_LOG_MSG(port->ipc_log_misc,
+				"%s.Device is suspended.\n", __func__);
+		return;
+	}
+	start_rx_sequencer(&port->uport);
+}
+
+
 static void msm_geni_serial_rx_fsm_rst(struct uart_port *uport)
 {
 	unsigned int rx_irq_en;
@@ -1043,19 +1059,15 @@
 	geni_write_reg_nolog(rx_irq_en, uport->membase, SE_DMA_RX_IRQ_EN_SET);
 }
 
-static void msm_geni_serial_stop_rx(struct uart_port *uport)
+static void stop_rx_sequencer(struct uart_port *uport)
 {
 	unsigned int geni_s_irq_en;
 	unsigned int geni_m_irq_en;
 	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 	u32 irq_clear = S_CMD_DONE_EN;
+	bool done;
 
-	if (!uart_console(uport) && device_pending_suspend(uport)) {
-		IPC_LOG_MSG(port->ipc_log_misc,
-				"%s.Device is suspended.\n", __func__);
-		return;
-	}
 	IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
 	if (port->xfer_mode == FIFO_MODE) {
 		geni_s_irq_en = geni_read_reg_nolog(uport->membase,
@@ -1069,28 +1081,47 @@
 							SE_GENI_S_IRQ_EN);
 		geni_write_reg_nolog(geni_m_irq_en, uport->membase,
 							SE_GENI_M_IRQ_EN);
-	} else if (port->xfer_mode == SE_DMA && port->rx_dma) {
-		msm_geni_serial_rx_fsm_rst(uport);
-		geni_se_rx_dma_unprep(port->wrapper_dev, port->rx_dma,
-						      DMA_RX_BUF_SIZE);
-		port->rx_dma = (dma_addr_t)NULL;
 	}
 
 	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
 	/* Possible stop rx is called multiple times. */
 	if (!(geni_status & S_GENI_CMD_ACTIVE))
-		return;
+		goto exit_rx_seq;
 	geni_cancel_s_cmd(uport->membase);
 	/*
 	 * Ensure that the cancel goes through before polling for the
 	 * cancel control bit.
 	 */
 	mb();
-	msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+	done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
 					S_GENI_CMD_CANCEL, false);
+	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+	if (!done)
+		IPC_LOG_MSG(port->ipc_log_misc, "%s Cancel fail 0x%x\n",
+							__func__, geni_status);
+
 	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
 	if ((geni_status & S_GENI_CMD_ACTIVE))
 		msm_geni_serial_abort_rx(uport);
+exit_rx_seq:
+	if (port->xfer_mode == SE_DMA && port->rx_dma) {
+		msm_geni_serial_rx_fsm_rst(uport);
+		geni_se_rx_dma_unprep(port->wrapper_dev, port->rx_dma,
+						      DMA_RX_BUF_SIZE);
+		port->rx_dma = (dma_addr_t)NULL;
+	}
+}
+
+static void msm_geni_serial_stop_rx(struct uart_port *uport)
+{
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
+		IPC_LOG_MSG(port->ipc_log_misc,
+				"%s.Device is suspended.\n", __func__);
+		return;
+	}
+	stop_rx_sequencer(uport);
 }
 
 static int handle_rx_hs(struct uart_port *uport,
@@ -1890,7 +1921,7 @@
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
 	if (!uart_console(uport) && device_pending_suspend(uport))
-		return 0;
+		return 1;
 
 	if (port->xfer_mode == SE_DMA)
 		tx_fifo_status = port->tx_dma ? 1 : 0;
@@ -2377,6 +2408,11 @@
 		goto exit_geni_serial_probe;
 	}
 
+	/* Optional to use the Rx pin as wakeup irq */
+	dev_port->wakeup_irq = platform_get_irq(pdev, 1);
+	if ((dev_port->wakeup_irq < 0 && !is_console))
+		dev_info(&pdev->dev, "No wakeup IRQ configured\n");
+
 	dev_port->serial_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
 	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_pinctrl)) {
 		dev_err(&pdev->dev, "No pinctrl config specified!\n");
@@ -2391,13 +2427,24 @@
 		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_active);
 		goto exit_geni_serial_probe;
 	}
-	dev_port->serial_rsc.geni_gpio_sleep =
-		pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
+
+	/*
+	 * For clients who setup an Inband wakeup, leave the GPIO pins
+	 * always connected to the core, else move the pins to their
+	 * defined "sleep" state.
+	 */
+	if (dev_port->wakeup_irq > 0) {
+		dev_port->serial_rsc.geni_gpio_sleep =
+			dev_port->serial_rsc.geni_gpio_active;
+	} else {
+		dev_port->serial_rsc.geni_gpio_sleep =
+			pinctrl_lookup_state(dev_port->serial_rsc.geni_pinctrl,
 							PINCTRL_SLEEP);
-	if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
-		dev_err(&pdev->dev, "No sleep config specified!\n");
-		ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
-		goto exit_geni_serial_probe;
+		if (IS_ERR_OR_NULL(dev_port->serial_rsc.geni_gpio_sleep)) {
+			dev_err(&pdev->dev, "No sleep config specified!\n");
+			ret = PTR_ERR(dev_port->serial_rsc.geni_gpio_sleep);
+			goto exit_geni_serial_probe;
+		}
 	}
 
 	wakeup_source_init(&dev_port->geni_wake, dev_name(&pdev->dev));
@@ -2414,11 +2461,6 @@
 		goto exit_geni_serial_probe;
 	}
 
-	/* Optional to use the Rx pin as wakeup irq */
-	dev_port->wakeup_irq = platform_get_irq(pdev, 1);
-	if ((dev_port->wakeup_irq < 0 && !is_console))
-		dev_info(&pdev->dev, "No wakeup IRQ configured\n");
-
 	uport->private_data = (void *)drv;
 	platform_set_drvdata(pdev, dev_port);
 	if (is_console) {
@@ -2462,25 +2504,42 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	struct msm_geni_serial_port *port = platform_get_drvdata(pdev);
 	int ret = 0;
+	u32 uart_manual_rfr = 0;
+	u32 geni_status = geni_read_reg_nolog(port->uport.membase,
+							SE_GENI_STATUS);
 
 	wait_for_transfers_inflight(&port->uport);
+	/*
+	 * Disable Interrupt
+	 * Manual RFR On.
+	 * Stop Rx.
+	 * Resources off
+	 */
 	disable_irq(port->uport.irq);
+	/*
+	 * If the clients haven't done a manual flow on/off then go ahead and
+	 * set this to manual flow on.
+	 */
+	if (!port->manual_flow) {
+		uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_READY);
+		geni_write_reg_nolog(uart_manual_rfr, port->uport.membase,
+							SE_UART_MANUAL_RFR);
+		/*
+		 * Ensure that the manual flow on writes go through before
+		 * doing a stop_rx else we could end up flowing off the peer.
+		 */
+		mb();
+	}
+	stop_rx_sequencer(&port->uport);
+	if ((geni_status & M_GENI_CMD_ACTIVE))
+		stop_tx_sequencer(&port->uport);
 	ret = se_geni_resources_off(&port->serial_rsc);
 	if (ret) {
 		dev_err(dev, "%s: Error ret %d\n", __func__, ret);
 		goto exit_runtime_suspend;
 	}
 	if (port->wakeup_irq > 0) {
-		struct se_geni_rsc *rsc = &port->serial_rsc;
-
 		port->edge_count = 0;
-		ret = pinctrl_select_state(rsc->geni_pinctrl,
-						rsc->geni_gpio_active);
-		if (ret) {
-			dev_err(dev, "%s: Error %d pinctrl_select_state\n",
-							__func__, ret);
-			goto exit_runtime_suspend;
-		}
 		enable_irq(port->wakeup_irq);
 	}
 	IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__);
@@ -2503,12 +2562,24 @@
 	__pm_stay_awake(&port->geni_wake);
 	if (port->wakeup_irq > 0)
 		disable_irq(port->wakeup_irq);
+	/*
+	 * Resources On.
+	 * Start Rx.
+	 * Auto RFR.
+	 * Enable IRQ.
+	 */
 	ret = se_geni_resources_on(&port->serial_rsc);
 	if (ret) {
 		dev_err(dev, "%s: Error ret %d\n", __func__, ret);
 		__pm_relax(&port->geni_wake);
 		goto exit_runtime_resume;
 	}
+	start_rx_sequencer(&port->uport);
+	if (!port->manual_flow)
+		geni_write_reg_nolog(0, port->uport.membase,
+						SE_UART_MANUAL_RFR);
+	/* Ensure that the Rx is running before enabling interrupts */
+	mb();
 	enable_irq(port->uport.irq);
 	IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__);
 exit_runtime_resume:
diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c
new file mode 100644
index 0000000..84ee1dd
--- /dev/null
+++ b/drivers/tty/serial/msm_smd_tty.c
@@ -0,0 +1,1049 @@
+/* Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2015, 2017, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ipc_logging.h>
+#include <linux/of.h>
+#include <linux/suspend.h>
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#include <soc/qcom/smd.h>
+#include <soc/qcom/smsm.h>
+#include <soc/qcom/subsystem_restart.h>
+
+#define MODULE_NAME "msm_smdtty"
+#define MAX_SMD_TTYS 37
+#define MAX_TTY_BUF_SIZE 2048
+#define TTY_PUSH_WS_DELAY 500
+#define TTY_PUSH_WS_POST_SUSPEND_DELAY 100
+#define MAX_RA_WAKE_LOCK_NAME_LEN 32
+#define SMD_TTY_LOG_PAGES 2
+
+#define SMD_TTY_INFO(buf...) \
+do { \
+	if (smd_tty_log_ctx) { \
+		ipc_log_string(smd_tty_log_ctx, buf); \
+	} \
+} while (0)
+
+#define SMD_TTY_ERR(buf...) \
+do { \
+	if (smd_tty_log_ctx) \
+		ipc_log_string(smd_tty_log_ctx, buf); \
+	pr_err(buf); \
+} while (0)
+
+static void *smd_tty_log_ctx;
+static bool smd_tty_in_suspend;
+static bool smd_tty_read_in_suspend;
+static struct wakeup_source read_in_suspend_ws;
+
+/**
+ * struct smd_tty_info - context for an individual SMD TTY device
+ *
+ * @ch:  SMD channel handle
+ * @port:  TTY port context structure
+ * @device_ptr:  TTY device pointer
+ * @pending_ws:  pending-data wakeup source
+ * @tty_tsklt:  read tasklet
+ * @buf_req_timer:  RX buffer retry timer
+ * @ch_allocated:  completion set when SMD channel is allocated
+ * @pil:  Peripheral Image Loader handle
+ * @edge:  SMD edge associated with port
+ * @ch_name:  SMD channel name associated with port
+ * @dev_name:  SMD platform device name associated with port
+ *
+ * @open_lock_lha1: open/close lock - used to serialize open/close operations
+ * @open_wait:  Timeout in seconds to wait for SMD port to be created / opened
+ *
+ * @reset_lock_lha2: lock for reset and open state
+ * @in_reset:  True if SMD channel is closed / in SSR
+ * @in_reset_updated:  reset state changed
+ * @is_open:  True if SMD port is open
+ * @ch_opened_wait_queue:  SMD port open/close wait queue
+ *
+ * @ra_lock_lha3:  Read-available lock - used to synchronize reads from SMD
+ * @ra_wakeup_source_name: Name of the read-available wakeup source
+ * @ra_wakeup_source:  Read-available wakeup source
+ */
+struct smd_tty_info {
+	smd_channel_t *ch;
+	struct tty_port port;
+	struct device *device_ptr;
+	struct wakeup_source pending_ws;
+	struct tasklet_struct tty_tsklt;
+	struct timer_list buf_req_timer;
+	struct completion ch_allocated;
+	void *pil;
+	uint32_t edge;
+	char ch_name[SMD_MAX_CH_NAME_LEN];
+	char dev_name[SMD_MAX_CH_NAME_LEN];
+
+	struct mutex open_lock_lha1;
+	unsigned int open_wait;
+
+	spinlock_t reset_lock_lha2;
+	int in_reset;
+	int in_reset_updated;
+	int is_open;
+	wait_queue_head_t ch_opened_wait_queue;
+
+	spinlock_t ra_lock_lha3;
+	char ra_wakeup_source_name[MAX_RA_WAKE_LOCK_NAME_LEN];
+	struct wakeup_source ra_wakeup_source;
+};
+
+/**
+ * struct smd_tty_pfdriver - SMD tty channel platform driver structure
+ *
+ * @list:  Adds this structure into smd_tty_platform_driver_list::list.
+ * @ref_cnt:  reference count for this structure.
+ * @driver:  SMD channel platform driver context structure
+ */
+struct smd_tty_pfdriver {
+	struct list_head list;
+	int ref_cnt;
+	struct platform_driver driver;
+};
+
+#define LOOPBACK_IDX 36
+
+static struct delayed_work loopback_work;
+static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
+
+static DEFINE_MUTEX(smd_tty_pfdriver_lock_lha1);
+static LIST_HEAD(smd_tty_pfdriver_list);
+
+static int is_in_reset(struct smd_tty_info *info)
+{
+	return info->in_reset;
+}
+
+static void buf_req_retry(unsigned long param)
+{
+	struct smd_tty_info *info = (struct smd_tty_info *)param;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->reset_lock_lha2, flags);
+	if (info->is_open) {
+		spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+		tasklet_hi_schedule(&info->tty_tsklt);
+		return;
+	}
+	spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+}
+
+static ssize_t open_timeout_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t n)
+{
+	unsigned int num_dev;
+	unsigned long wait;
+
+	if (dev == NULL) {
+		SMD_TTY_INFO("%s: Invalid Device passed", __func__);
+		return -EINVAL;
+	}
+	for (num_dev = 0; num_dev < MAX_SMD_TTYS; num_dev++) {
+		if (dev == smd_tty[num_dev].device_ptr)
+			break;
+	}
+	if (num_dev >= MAX_SMD_TTYS) {
+		SMD_TTY_ERR("[%s]: Device Not found", __func__);
+		return -EINVAL;
+	}
+	if (!kstrtoul(buf, 10, &wait)) {
+		mutex_lock(&smd_tty[num_dev].open_lock_lha1);
+		smd_tty[num_dev].open_wait = wait;
+		mutex_unlock(&smd_tty[num_dev].open_lock_lha1);
+		return n;
+	}
+
+	SMD_TTY_INFO("[%s]: Unable to convert %s to an int",
+			__func__, buf);
+	return -EINVAL;
+
+}
+
+static ssize_t open_timeout_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	unsigned int num_dev;
+	unsigned int open_wait;
+
+	if (dev == NULL) {
+		SMD_TTY_INFO("%s: Invalid Device passed", __func__);
+		return -EINVAL;
+	}
+	for (num_dev = 0; num_dev < MAX_SMD_TTYS; num_dev++) {
+		if (dev == smd_tty[num_dev].device_ptr)
+			break;
+	}
+	if (num_dev >= MAX_SMD_TTYS) {
+		SMD_TTY_ERR("[%s]: Device Not Found", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&smd_tty[num_dev].open_lock_lha1);
+	open_wait = smd_tty[num_dev].open_wait;
+	mutex_unlock(&smd_tty[num_dev].open_lock_lha1);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", open_wait);
+}
+
+static DEVICE_ATTR
+	(open_timeout, 0664, open_timeout_show, open_timeout_store);
+
+static void smd_tty_read(unsigned long param)
+{
+	unsigned char *ptr;
+	int avail;
+	struct smd_tty_info *info = (struct smd_tty_info *)param;
+	struct tty_struct *tty = tty_port_tty_get(&info->port);
+	unsigned long flags;
+
+	if (!tty)
+		return;
+
+	for (;;) {
+		if (is_in_reset(info)) {
+			/* signal TTY clients using TTY_BREAK */
+			tty_insert_flip_char(tty->port, 0x00, TTY_BREAK);
+			tty_flip_buffer_push(tty->port);
+			break;
+		}
+
+		if (test_bit(TTY_THROTTLED, &tty->flags))
+			break;
+		spin_lock_irqsave(&info->ra_lock_lha3, flags);
+		avail = smd_read_avail(info->ch);
+		if (avail == 0) {
+			__pm_relax(&info->ra_wakeup_source);
+			spin_unlock_irqrestore(&info->ra_lock_lha3, flags);
+			break;
+		}
+		spin_unlock_irqrestore(&info->ra_lock_lha3, flags);
+
+		if (avail > MAX_TTY_BUF_SIZE)
+			avail = MAX_TTY_BUF_SIZE;
+
+		avail = tty_prepare_flip_string(tty->port, &ptr, avail);
+		if (avail <= 0) {
+			mod_timer(&info->buf_req_timer,
+					jiffies + msecs_to_jiffies(30));
+			tty_kref_put(tty);
+			return;
+		}
+
+		if (smd_read(info->ch, ptr, avail) != avail) {
+			/* shouldn't be possible since we're in interrupt
+			 * context here and nobody else could 'steal' our
+			 * characters.
+			 */
+			SMD_TTY_ERR(
+				"%s - Possible smd_tty_buffer mismatch for %s",
+				__func__, info->ch_name);
+		}
+
+		/*
+		 * Keep system awake long enough to allow the TTY
+		 * framework to pass the flip buffer to any waiting
+		 * userspace clients.
+		 */
+		__pm_wakeup_event(&info->pending_ws, TTY_PUSH_WS_DELAY);
+
+		if (smd_tty_in_suspend)
+			smd_tty_read_in_suspend = true;
+
+		tty_flip_buffer_push(tty->port);
+	}
+
+	/* XXX only when writable and necessary */
+	tty_wakeup(tty);
+	tty_kref_put(tty);
+}
+
+static void smd_tty_notify(void *priv, unsigned int event)
+{
+	struct smd_tty_info *info = priv;
+	struct tty_struct *tty;
+	unsigned long flags;
+
+	switch (event) {
+	case SMD_EVENT_DATA:
+		spin_lock_irqsave(&info->reset_lock_lha2, flags);
+		if (!info->is_open) {
+			spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+			break;
+		}
+		spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+		/* There may be clients (tty framework) that are blocked
+		 * waiting for space to write data, so if a possible read
+		 * interrupt came in wake anyone waiting and disable the
+		 * interrupts
+		 */
+		if (smd_write_avail(info->ch)) {
+			smd_disable_read_intr(info->ch);
+			tty = tty_port_tty_get(&info->port);
+			if (tty)
+				wake_up_interruptible(&tty->write_wait);
+			tty_kref_put(tty);
+		}
+		spin_lock_irqsave(&info->ra_lock_lha3, flags);
+		if (smd_read_avail(info->ch)) {
+			__pm_stay_awake(&info->ra_wakeup_source);
+			tasklet_hi_schedule(&info->tty_tsklt);
+		}
+		spin_unlock_irqrestore(&info->ra_lock_lha3, flags);
+		break;
+
+	case SMD_EVENT_OPEN:
+		tty = tty_port_tty_get(&info->port);
+		spin_lock_irqsave(&info->reset_lock_lha2, flags);
+		if (tty)
+			clear_bit(TTY_OTHER_CLOSED, &tty->flags);
+		info->in_reset = 0;
+		info->in_reset_updated = 1;
+		info->is_open = 1;
+		wake_up_interruptible(&info->ch_opened_wait_queue);
+		spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+		tty_kref_put(tty);
+		break;
+
+	case SMD_EVENT_CLOSE:
+		spin_lock_irqsave(&info->reset_lock_lha2, flags);
+		info->in_reset = 1;
+		info->in_reset_updated = 1;
+		info->is_open = 0;
+		wake_up_interruptible(&info->ch_opened_wait_queue);
+		spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+
+		tty = tty_port_tty_get(&info->port);
+		if (tty) {
+			/* send TTY_BREAK through read tasklet */
+			set_bit(TTY_OTHER_CLOSED, &tty->flags);
+			tasklet_hi_schedule(&info->tty_tsklt);
+
+			if (tty->index == LOOPBACK_IDX)
+				schedule_delayed_work(&loopback_work,
+						msecs_to_jiffies(1000));
+		}
+		tty_kref_put(tty);
+		break;
+	}
+}
+
+static uint32_t is_modem_smsm_inited(void)
+{
+	uint32_t modem_state;
+	uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
+
+	modem_state = smsm_get_state(SMSM_MODEM_STATE);
+	return (modem_state & ready_state) == ready_state;
+}
+
+static int smd_tty_dummy_probe(struct platform_device *pdev)
+{
+	int n;
+
+	for (n = 0; n < MAX_SMD_TTYS; ++n) {
+		if (!smd_tty[n].dev_name)
+			continue;
+
+		if (pdev->id == smd_tty[n].edge &&
+			!strcmp(pdev->name, smd_tty[n].dev_name)) {
+			complete_all(&smd_tty[n].ch_allocated);
+			return 0;
+		}
+	}
+	SMD_TTY_ERR("[ERR]%s: unknown device '%s'\n", __func__, pdev->name);
+
+	return -ENODEV;
+}
+
+/**
+ * smd_tty_add_driver() - Add platform drivers for smd tty device
+ *
+ * @info: context for an individual SMD TTY device
+ *
+ * @returns: 0 for success, standard Linux error code otherwise
+ *
+ * This function is used to register platform driver once for all
+ * smd tty devices which have same names and increment the reference
+ * count for 2nd to nth devices.
+ */
+static int smd_tty_add_driver(struct smd_tty_info *info)
+{
+	int r = 0;
+	struct smd_tty_pfdriver *smd_tty_pfdriverp;
+	struct smd_tty_pfdriver *item;
+
+	if (!info) {
+		pr_err("%s on a NULL device structure\n", __func__);
+		return -EINVAL;
+	}
+
+	SMD_TTY_INFO("Begin %s on smd_tty[%s]\n", __func__,
+					info->ch_name);
+
+	mutex_lock(&smd_tty_pfdriver_lock_lha1);
+	list_for_each_entry(item, &smd_tty_pfdriver_list, list) {
+		if (!strcmp(item->driver.driver.name, info->dev_name)) {
+			SMD_TTY_INFO("%s:%s Driver Already reg. cnt:%d\n",
+				__func__, info->ch_name, item->ref_cnt);
+			++item->ref_cnt;
+			goto exit;
+		}
+	}
+
+	smd_tty_pfdriverp = kzalloc(sizeof(*smd_tty_pfdriverp), GFP_KERNEL);
+	if (IS_ERR_OR_NULL(smd_tty_pfdriverp)) {
+		pr_err("%s: kzalloc() failed for smd_tty_pfdriver[%s]\n",
+			__func__, info->ch_name);
+		r = -ENOMEM;
+		goto exit;
+	}
+
+	smd_tty_pfdriverp->driver.probe = smd_tty_dummy_probe;
+	smd_tty_pfdriverp->driver.driver.name = info->dev_name;
+	smd_tty_pfdriverp->driver.driver.owner = THIS_MODULE;
+	r = platform_driver_register(&smd_tty_pfdriverp->driver);
+	if (r) {
+		pr_err("%s: %s Platform driver reg. failed\n",
+			__func__, info->ch_name);
+		kfree(smd_tty_pfdriverp);
+		goto exit;
+	}
+	++smd_tty_pfdriverp->ref_cnt;
+	list_add(&smd_tty_pfdriverp->list, &smd_tty_pfdriver_list);
+
+exit:
+	SMD_TTY_INFO("End %s on smd_tty_ch[%s]\n", __func__, info->ch_name);
+	mutex_unlock(&smd_tty_pfdriver_lock_lha1);
+	return r;
+}
+
+/**
+ * smd_tty_remove_driver() - Remove the platform drivers for smd tty device
+ *
+ * @info: context for an individual SMD TTY device
+ *
+ * This function is used to decrement the reference count on
+ * platform drivers for smd pkt devices and removes the drivers
+ * when the reference count becomes zero.
+ */
+static void smd_tty_remove_driver(struct smd_tty_info *info)
+{
+	struct smd_tty_pfdriver *smd_tty_pfdriverp;
+	bool found_item = false;
+
+	if (!info) {
+		pr_err("%s on a NULL device\n", __func__);
+		return;
+	}
+
+	SMD_TTY_INFO("Begin %s on smd_tty_ch[%s]\n", __func__,
+					info->ch_name);
+	mutex_lock(&smd_tty_pfdriver_lock_lha1);
+	list_for_each_entry(smd_tty_pfdriverp, &smd_tty_pfdriver_list, list) {
+		if (!strcmp(smd_tty_pfdriverp->driver.driver.name,
+					info->dev_name)) {
+			found_item = true;
+			SMD_TTY_INFO("%s:%s Platform driver cnt:%d\n",
+				__func__, info->ch_name,
+				smd_tty_pfdriverp->ref_cnt);
+			if (smd_tty_pfdriverp->ref_cnt > 0)
+				--smd_tty_pfdriverp->ref_cnt;
+			else
+				pr_warn("%s reference count <= 0\n", __func__);
+			break;
+		}
+	}
+	if (!found_item)
+		SMD_TTY_ERR("%s:%s No item found in list.\n",
+			__func__, info->ch_name);
+
+	if (found_item && smd_tty_pfdriverp->ref_cnt == 0) {
+		platform_driver_unregister(&smd_tty_pfdriverp->driver);
+		smd_tty_pfdriverp->driver.probe = NULL;
+		list_del(&smd_tty_pfdriverp->list);
+		kfree(smd_tty_pfdriverp);
+	}
+	mutex_unlock(&smd_tty_pfdriver_lock_lha1);
+	SMD_TTY_INFO("End %s on smd_tty_ch[%s]\n", __func__, info->ch_name);
+}
+
+static int smd_tty_port_activate(struct tty_port *tport,
+				 struct tty_struct *tty)
+{
+	int res = 0;
+	unsigned int n = tty->index;
+	struct smd_tty_info *info;
+	const char *peripheral = NULL;
+
+	if (n >= MAX_SMD_TTYS || !smd_tty[n].ch_name)
+		return -ENODEV;
+
+	info = smd_tty + n;
+
+	mutex_lock(&info->open_lock_lha1);
+	tty->driver_data = info;
+
+	res = smd_tty_add_driver(info);
+	if (res) {
+		SMD_TTY_ERR("%s:%d Idx smd_tty_driver register failed %d\n",
+							__func__, n, res);
+		goto out;
+	}
+
+	peripheral = smd_edge_to_pil_str(smd_tty[n].edge);
+	if (!IS_ERR_OR_NULL(peripheral)) {
+		info->pil = subsystem_get(peripheral);
+		if (IS_ERR(info->pil)) {
+			SMD_TTY_INFO(
+				"%s failed on smd_tty device :%s subsystem_get failed for %s",
+				__func__, info->ch_name,
+				peripheral);
+
+			/*
+			 * Sleep, inorder to reduce the frequency of
+			 * retry by user-space modules and to avoid
+			 * possible watchdog bite.
+			 */
+			msleep((smd_tty[n].open_wait * 1000));
+			res = PTR_ERR(info->pil);
+			goto platform_unregister;
+		}
+	}
+
+	/* Wait for the modem SMSM to be inited for the SMD
+	 * Loopback channel to be allocated at the modem. Since
+	 * the wait need to be done atmost once, using msleep
+	 * doesn't degrade the performance.
+	 */
+	if (n == LOOPBACK_IDX) {
+		if (!is_modem_smsm_inited())
+			msleep(5000);
+		smsm_change_state(SMSM_APPS_STATE,
+				  0, SMSM_SMD_LOOPBACK);
+		msleep(100);
+	}
+
+	/*
+	 * Wait for a channel to be allocated so we know
+	 * the modem is ready enough.
+	 */
+	if (smd_tty[n].open_wait) {
+		res = wait_for_completion_interruptible_timeout(
+				&info->ch_allocated,
+				msecs_to_jiffies(smd_tty[n].open_wait *
+								1000));
+
+		if (res == 0) {
+			SMD_TTY_INFO(
+				"Timed out waiting for SMD channel %s",
+				info->ch_name);
+			res = -ETIMEDOUT;
+			goto release_pil;
+		} else if (res < 0) {
+			SMD_TTY_INFO(
+				"Error waiting for SMD channel %s : %d\n",
+				info->ch_name, res);
+			goto release_pil;
+		}
+	}
+
+	tasklet_init(&info->tty_tsklt, smd_tty_read, (unsigned long)info);
+	wakeup_source_init(&info->pending_ws, info->ch_name);
+	scnprintf(info->ra_wakeup_source_name, MAX_RA_WAKE_LOCK_NAME_LEN,
+		  "SMD_TTY_%s_RA", info->ch_name);
+	wakeup_source_init(&info->ra_wakeup_source,
+			info->ra_wakeup_source_name);
+
+	res = smd_named_open_on_edge(info->ch_name,
+				     smd_tty[n].edge, &info->ch, info,
+				     smd_tty_notify);
+	if (res < 0) {
+		SMD_TTY_INFO("%s: %s open failed %d\n",
+			      __func__, info->ch_name, res);
+		goto release_wl_tl;
+	}
+
+	res = wait_event_interruptible_timeout(info->ch_opened_wait_queue,
+					       info->is_open, (2 * HZ));
+	if (res == 0)
+		res = -ETIMEDOUT;
+	if (res < 0) {
+		SMD_TTY_INFO("%s: wait for %s smd_open failed %d\n",
+			      __func__, info->ch_name, res);
+		goto close_ch;
+	}
+	SMD_TTY_INFO("%s with PID %u opened port %s",
+		      current->comm, current->pid, info->ch_name);
+	smd_disable_read_intr(info->ch);
+	mutex_unlock(&info->open_lock_lha1);
+	return 0;
+
+close_ch:
+	smd_close(info->ch);
+	info->ch = NULL;
+
+release_wl_tl:
+	tasklet_kill(&info->tty_tsklt);
+	wakeup_source_trash(&info->pending_ws);
+	wakeup_source_trash(&info->ra_wakeup_source);
+
+release_pil:
+	subsystem_put(info->pil);
+
+platform_unregister:
+	smd_tty_remove_driver(info);
+
+out:
+	mutex_unlock(&info->open_lock_lha1);
+
+	return res;
+}
+
+static void smd_tty_port_shutdown(struct tty_port *tport)
+{
+	struct smd_tty_info *info;
+	struct tty_struct *tty = tty_port_tty_get(tport);
+	unsigned long flags;
+
+	info = tty->driver_data;
+	if (info == 0) {
+		tty_kref_put(tty);
+		return;
+	}
+
+	mutex_lock(&info->open_lock_lha1);
+
+	spin_lock_irqsave(&info->reset_lock_lha2, flags);
+	info->is_open = 0;
+	spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+
+	tasklet_kill(&info->tty_tsklt);
+	wakeup_source_trash(&info->pending_ws);
+	wakeup_source_trash(&info->ra_wakeup_source);
+
+	SMD_TTY_INFO("%s with PID %u closed port %s",
+			current->comm, current->pid,
+			info->ch_name);
+	tty->driver_data = NULL;
+	del_timer(&info->buf_req_timer);
+
+	smd_close(info->ch);
+	info->ch = NULL;
+	subsystem_put(info->pil);
+	smd_tty_remove_driver(info);
+
+	mutex_unlock(&info->open_lock_lha1);
+	tty_kref_put(tty);
+}
+
+static int smd_tty_open(struct tty_struct *tty, struct file *f)
+{
+	struct smd_tty_info *info = smd_tty + tty->index;
+
+	return tty_port_open(&info->port, tty, f);
+}
+
+static void smd_tty_close(struct tty_struct *tty, struct file *f)
+{
+	struct smd_tty_info *info = smd_tty + tty->index;
+
+	tty_port_close(&info->port, tty, f);
+}
+
+static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf,
+									int len)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	int avail;
+
+	/* if we're writing to a packet channel we will
+	 * never be able to write more data than there
+	 * is currently space for
+	 */
+	if (is_in_reset(info))
+		return -ENETRESET;
+
+	avail = smd_write_avail(info->ch);
+	/* if no space, we'll have to setup a notification later to wake up the
+	 * tty framework when space becomes available
+	 */
+	if (!avail) {
+		smd_enable_read_intr(info->ch);
+		return 0;
+	}
+	if (len > avail)
+		len = avail;
+	SMD_TTY_INFO("[WRITE]: PID %u -> port %s %x bytes",
+			current->pid, info->ch_name, len);
+
+	return smd_write(info->ch, buf, len);
+}
+
+static int smd_tty_write_room(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+
+	return smd_write_avail(info->ch);
+}
+
+static int smd_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+
+	return smd_read_avail(info->ch);
+}
+
+static void smd_tty_unthrottle(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->reset_lock_lha2, flags);
+	if (info->is_open) {
+		spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+		tasklet_hi_schedule(&info->tty_tsklt);
+		return;
+	}
+	spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+}
+
+/*
+ * Returns the current TIOCM status bits including:
+ *      SMD Signals (DTR/DSR, CTS/RTS, CD, RI)
+ *      TIOCM_OUT1 - reset state (1=in reset)
+ *      TIOCM_OUT2 - reset state updated (1=updated)
+ */
+static int smd_tty_tiocmget(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	unsigned long flags;
+	int tiocm;
+
+	tiocm = smd_tiocmget(info->ch);
+
+	spin_lock_irqsave(&info->reset_lock_lha2, flags);
+	tiocm |= (info->in_reset ? TIOCM_OUT1 : 0);
+	if (info->in_reset_updated) {
+		tiocm |= TIOCM_OUT2;
+		info->in_reset_updated = 0;
+	}
+	SMD_TTY_INFO("PID %u --> %s TIOCM is %x ",
+			current->pid, __func__, tiocm);
+	spin_unlock_irqrestore(&info->reset_lock_lha2, flags);
+
+	return tiocm;
+}
+
+static int smd_tty_tiocmset(struct tty_struct *tty,
+				unsigned int set, unsigned int clear)
+{
+	struct smd_tty_info *info = tty->driver_data;
+
+	if (info->in_reset)
+		return -ENETRESET;
+
+	SMD_TTY_INFO("PID %u --> %s Set: %x Clear: %x",
+			current->pid, __func__, set, clear);
+	return smd_tiocmset(info->ch, set, clear);
+}
+
+static void loopback_probe_worker(struct work_struct *work)
+{
+	/* wait for modem to restart before requesting loopback server */
+	if (!is_modem_smsm_inited())
+		schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
+	else
+		smsm_change_state(SMSM_APPS_STATE,
+			  0, SMSM_SMD_LOOPBACK);
+}
+
+static const struct tty_port_operations smd_tty_port_ops = {
+	.shutdown = smd_tty_port_shutdown,
+	.activate = smd_tty_port_activate,
+};
+
+static const struct tty_operations smd_tty_ops = {
+	.open = smd_tty_open,
+	.close = smd_tty_close,
+	.write = smd_tty_write,
+	.write_room = smd_tty_write_room,
+	.chars_in_buffer = smd_tty_chars_in_buffer,
+	.unthrottle = smd_tty_unthrottle,
+	.tiocmget = smd_tty_tiocmget,
+	.tiocmset = smd_tty_tiocmset,
+};
+
+static int smd_tty_pm_notifier(struct notifier_block *nb,
+				unsigned long event, void *unused)
+{
+	switch (event) {
+	case PM_SUSPEND_PREPARE:
+		smd_tty_read_in_suspend = false;
+		smd_tty_in_suspend = true;
+		break;
+
+	case PM_POST_SUSPEND:
+		smd_tty_in_suspend = false;
+		if (smd_tty_read_in_suspend) {
+			smd_tty_read_in_suspend = false;
+			__pm_wakeup_event(&read_in_suspend_ws,
+					TTY_PUSH_WS_POST_SUSPEND_DELAY);
+		}
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block smd_tty_pm_nb = {
+	.notifier_call = smd_tty_pm_notifier,
+	.priority = 0,
+};
+
+/**
+ * smd_tty_log_init()- Init function for IPC logging
+ *
+ * Initialize the buffer that is used to provide the log information
+ * pertaining to the smd_tty module.
+ */
+static void smd_tty_log_init(void)
+{
+	smd_tty_log_ctx = ipc_log_context_create(SMD_TTY_LOG_PAGES,
+						"smd_tty", 0);
+	if (!smd_tty_log_ctx)
+		pr_err("%s: Unable to create IPC log", __func__);
+}
+
+static struct tty_driver *smd_tty_driver;
+
+static int smd_tty_register_driver(void)
+{
+	int ret;
+
+	smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
+	if (smd_tty_driver == 0) {
+		SMD_TTY_ERR("%s - Driver allocation failed", __func__);
+		return -ENOMEM;
+	}
+
+	smd_tty_driver->owner = THIS_MODULE;
+	smd_tty_driver->driver_name = "smd_tty_driver";
+	smd_tty_driver->name = "smd";
+	smd_tty_driver->major = 0;
+	smd_tty_driver->minor_start = 0;
+	smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+	smd_tty_driver->init_termios = tty_std_termios;
+	smd_tty_driver->init_termios.c_iflag = 0;
+	smd_tty_driver->init_termios.c_oflag = 0;
+	smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+	smd_tty_driver->init_termios.c_lflag = 0;
+	smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
+		TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	tty_set_operations(smd_tty_driver, &smd_tty_ops);
+
+	ret = tty_register_driver(smd_tty_driver);
+	if (ret) {
+		put_tty_driver(smd_tty_driver);
+		SMD_TTY_ERR("%s: driver registration failed %d", __func__, ret);
+	}
+
+	return ret;
+}
+
+static void smd_tty_device_init(int idx)
+{
+	struct tty_port *port;
+
+	port = &smd_tty[idx].port;
+	tty_port_init(port);
+	port->ops = &smd_tty_port_ops;
+	smd_tty[idx].device_ptr = tty_port_register_device(port, smd_tty_driver,
+							   idx, NULL);
+	if (IS_ERR_OR_NULL(smd_tty[idx].device_ptr)) {
+		SMD_TTY_ERR("%s: Unable to register tty port %s reason %d\n",
+				__func__,
+				smd_tty[idx].ch_name,
+				PTR_ERR_OR_ZERO(smd_tty[idx].device_ptr));
+		return;
+	}
+	init_completion(&smd_tty[idx].ch_allocated);
+	mutex_init(&smd_tty[idx].open_lock_lha1);
+	spin_lock_init(&smd_tty[idx].reset_lock_lha2);
+	spin_lock_init(&smd_tty[idx].ra_lock_lha3);
+	smd_tty[idx].is_open = 0;
+	setup_timer(&smd_tty[idx].buf_req_timer, buf_req_retry,
+			(unsigned long)&smd_tty[idx]);
+	init_waitqueue_head(&smd_tty[idx].ch_opened_wait_queue);
+
+	if (device_create_file(smd_tty[idx].device_ptr, &dev_attr_open_timeout))
+		SMD_TTY_ERR("%s: Unable to create device attributes for %s",
+			__func__, smd_tty[idx].ch_name);
+}
+
+static int smd_tty_devicetree_init(struct platform_device *pdev)
+{
+	int ret;
+	int idx;
+	int edge;
+	char *key = NULL;
+	const char *ch_name;
+	const char *dev_name;
+	const char *remote_ss;
+	struct device_node *node;
+
+	ret = smd_tty_register_driver();
+	if (ret) {
+		SMD_TTY_ERR("%s: driver registration failed %d\n",
+						__func__, ret);
+		return ret;
+	}
+
+	for_each_child_of_node(pdev->dev.of_node, node) {
+
+		ret = of_alias_get_id(node, "smd");
+		SMD_TTY_INFO("%s:adding smd%d\n", __func__, ret);
+
+		if (ret < 0 || ret >= MAX_SMD_TTYS)
+			goto error;
+		idx = ret;
+
+		key = "qcom,smdtty-remote";
+		remote_ss = of_get_property(node, key, NULL);
+		if (!remote_ss)
+			goto error;
+
+		edge = smd_remote_ss_to_edge(remote_ss);
+		if (edge < 0)
+			goto error;
+		smd_tty[idx].edge = edge;
+
+		key = "qcom,smdtty-port-name";
+		ch_name = of_get_property(node, key, NULL);
+		if (!ch_name)
+			goto error;
+		strlcpy(smd_tty[idx].ch_name, ch_name,
+					SMD_MAX_CH_NAME_LEN);
+
+		key = "qcom,smdtty-dev-name";
+		dev_name = of_get_property(node, key, NULL);
+		if (!dev_name) {
+			strlcpy(smd_tty[idx].dev_name, smd_tty[idx].ch_name,
+							SMD_MAX_CH_NAME_LEN);
+		} else {
+			strlcpy(smd_tty[idx].dev_name, dev_name,
+						SMD_MAX_CH_NAME_LEN);
+		}
+
+		smd_tty_device_init(idx);
+	}
+	INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
+
+	ret = register_pm_notifier(&smd_tty_pm_nb);
+	if (ret)
+		pr_err("%s: power state notif error %d\n", __func__, ret);
+
+	return 0;
+
+error:
+	SMD_TTY_ERR("%s:Initialization error, key[%s]\n", __func__, key);
+	/* Unregister tty platform devices */
+	for_each_child_of_node(pdev->dev.of_node, node) {
+
+		ret = of_alias_get_id(node, "smd");
+		SMD_TTY_INFO("%s:Removing smd%d\n", __func__, ret);
+
+		if (ret < 0 || ret >= MAX_SMD_TTYS)
+			goto out;
+		idx = ret;
+
+		if (smd_tty[idx].device_ptr) {
+			device_remove_file(smd_tty[idx].device_ptr,
+						&dev_attr_open_timeout);
+			tty_unregister_device(smd_tty_driver, idx);
+		}
+	}
+out:
+	tty_unregister_driver(smd_tty_driver);
+	put_tty_driver(smd_tty_driver);
+	return ret;
+}
+
+static int msm_smd_tty_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	if (pdev) {
+		if (pdev->dev.of_node) {
+			ret = smd_tty_devicetree_init(pdev);
+			if (ret) {
+				SMD_TTY_ERR("%s: device tree init failed\n",
+								__func__);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static const struct of_device_id msm_smd_tty_match_table[] = {
+	{ .compatible = "qcom,smdtty" },
+	{},
+};
+
+static struct platform_driver msm_smd_tty_driver = {
+	.probe = msm_smd_tty_probe,
+	.driver = {
+		.name = MODULE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = msm_smd_tty_match_table,
+	 },
+};
+
+
+static int __init smd_tty_init(void)
+{
+	int rc;
+
+	smd_tty_log_init();
+	rc = platform_driver_register(&msm_smd_tty_driver);
+	if (rc) {
+		SMD_TTY_ERR("%s: msm_smd_tty_driver register failed %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	wakeup_source_init(&read_in_suspend_ws, "SMDTTY_READ_IN_SUSPEND");
+	return 0;
+}
+
+module_init(smd_tty_init);
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/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/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/msm_smd_pkt.h b/include/linux/msm_smd_pkt.h
new file mode 100644
index 0000000..c79933d
--- /dev/null
+++ b/include/linux/msm_smd_pkt.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2010,2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __LINUX_MSM_SMD_PKT_H
+#define __LINUX_MSM_SMD_PKT_H
+
+#include <linux/ioctl.h>
+
+#define SMD_PKT_IOCTL_MAGIC (0xC2)
+
+#define SMD_PKT_IOCTL_BLOCKING_WRITE \
+	_IOR(SMD_PKT_IOCTL_MAGIC, 0, unsigned int)
+
+#endif /* __LINUX_MSM_SMD_PKT_H */
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index 42fd34c..bbc4625 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -214,6 +214,11 @@
 #define PM660L_V1P1_REV3	0x01
 #define PM660L_V1P1_REV4	0x01
 
+#define PM660L_V2P0_REV1	0x00
+#define PM660L_V2P0_REV2	0x00
+#define PM660L_V2P0_REV3	0x00
+#define PM660L_V2P0_REV4	0x02
+
 /* PMI8998 FAB_ID */
 #define PMI8998_FAB_ID_SMIC	0x11
 #define PMI8998_FAB_ID_GF	0x30
diff --git a/include/linux/regulator/qpnp-labibb-regulator.h b/include/linux/regulator/qpnp-labibb-regulator.h
index 2470695..33985af 100644
--- a/include/linux/regulator/qpnp-labibb-regulator.h
+++ b/include/linux/regulator/qpnp-labibb-regulator.h
@@ -15,6 +15,7 @@
 
 enum labibb_notify_event {
 	LAB_VREG_OK = 1,
+	LAB_VREG_NOT_OK,
 };
 
 int qpnp_labibb_notifier_register(struct notifier_block *nb);
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/soc/qcom/smd.h b/include/soc/qcom/smd.h
new file mode 100644
index 0000000..9853a93
--- /dev/null
+++ b/include/soc/qcom/smd.h
@@ -0,0 +1,381 @@
+/* include/soc/qcom/smd.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2014, 2017, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ASM_ARCH_MSM_SMD_H
+#define __ASM_ARCH_MSM_SMD_H
+
+#include <linux/io.h>
+
+#include <soc/qcom/smem.h>
+
+typedef struct smd_channel smd_channel_t;
+struct cpumask;
+
+#define SMD_MAX_CH_NAME_LEN 20 /* includes null char at end */
+
+#define SMD_EVENT_DATA 1
+#define SMD_EVENT_OPEN 2
+#define SMD_EVENT_CLOSE 3
+#define SMD_EVENT_STATUS 4
+#define SMD_EVENT_REOPEN_READY 5
+
+/*
+ * SMD Processor ID's.
+ *
+ * For all processors that have both SMSM and SMD clients,
+ * the SMSM Processor ID and the SMD Processor ID will
+ * be the same.  In cases where a processor only supports
+ * SMD, the entry will only exist in this enum.
+ */
+enum {
+	SMD_APPS = SMEM_APPS,
+	SMD_MODEM = SMEM_MODEM,
+	SMD_Q6 = SMEM_Q6,
+	SMD_DSPS = SMEM_DSPS,
+	SMD_TZ = SMEM_DSPS,
+	SMD_WCNSS = SMEM_WCNSS,
+	SMD_MODEM_Q6_FW = SMEM_MODEM_Q6_FW,
+	SMD_RPM = SMEM_RPM,
+	NUM_SMD_SUBSYSTEMS,
+};
+
+enum {
+	SMD_APPS_MODEM = 0,
+	SMD_APPS_QDSP,
+	SMD_MODEM_QDSP,
+	SMD_APPS_DSPS,
+	SMD_MODEM_DSPS,
+	SMD_QDSP_DSPS,
+	SMD_APPS_WCNSS,
+	SMD_MODEM_WCNSS,
+	SMD_QDSP_WCNSS,
+	SMD_DSPS_WCNSS,
+	SMD_APPS_Q6FW,
+	SMD_MODEM_Q6FW,
+	SMD_QDSP_Q6FW,
+	SMD_DSPS_Q6FW,
+	SMD_WCNSS_Q6FW,
+	SMD_APPS_RPM,
+	SMD_MODEM_RPM,
+	SMD_QDSP_RPM,
+	SMD_WCNSS_RPM,
+	SMD_TZ_RPM,
+	SMD_NUM_TYPE,
+
+};
+
+#ifdef CONFIG_MSM_SMD
+int smd_close(smd_channel_t *ch);
+
+/* passing a null pointer for data reads and discards */
+int smd_read(smd_channel_t *ch, void *data, int len);
+int smd_read_from_cb(smd_channel_t *ch, void *data, int len);
+
+/* Write to stream channels may do a partial write and return
+ * the length actually written.
+ * Write to packet channels will never do a partial write --
+ * it will return the requested length written or an error.
+ */
+int smd_write(smd_channel_t *ch, const void *data, int len);
+
+int smd_write_avail(smd_channel_t *ch);
+int smd_read_avail(smd_channel_t *ch);
+
+/* Returns the total size of the current packet being read.
+ * Returns 0 if no packets available or a stream channel.
+ */
+int smd_cur_packet_size(smd_channel_t *ch);
+
+/* these are used to get and set the IF sigs of a channel.
+ * DTR and RTS can be set; DSR, CTS, CD and RI can be read.
+ */
+int smd_tiocmget(smd_channel_t *ch);
+int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear);
+int
+smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear);
+int smd_named_open_on_edge(const char *name, uint32_t edge, smd_channel_t **_ch,
+			   void *priv, void (*notify)(void *, unsigned int));
+
+/* Tells the other end of the smd channel that this end wants to receive
+ * interrupts when the written data is read.  Read interrupts should only
+ * enabled when there is no space left in the buffer to write to, thus the
+ * interrupt acts as notification that space may be available.  If the
+ * other side does not support enabling/disabling interrupts on demand,
+ * then this function has no effect if called.
+ */
+void smd_enable_read_intr(smd_channel_t *ch);
+
+/* Tells the other end of the smd channel that this end does not want
+ * interrupts when written data is read.  The interrupts should be
+ * disabled by default.  If the other side does not support enabling/
+ * disabling interrupts on demand, then this function has no effect if
+ * called.
+ */
+void smd_disable_read_intr(smd_channel_t *ch);
+
+/**
+ * Enable/disable receive interrupts for the remote processor used by a
+ * particular channel.
+ * @ch:      open channel handle to use for the edge
+ * @mask:    1 = mask interrupts; 0 = unmask interrupts
+ * @cpumask  cpumask for the next cpu scheduled to be woken up
+ * @returns: 0 for success; < 0 for failure
+ *
+ * Note that this enables/disables all interrupts from the remote subsystem for
+ * all channels.  As such, it should be used with care and only for specific
+ * use cases such as power-collapse sequencing.
+ */
+int smd_mask_receive_interrupt(smd_channel_t *ch, bool mask,
+		const struct cpumask *cpumask);
+
+/* Starts a packet transaction.  The size of the packet may exceed the total
+ * size of the smd ring buffer.
+ *
+ * @ch: channel to write the packet to
+ * @len: total length of the packet
+ *
+ * Returns:
+ *      0 - success
+ *      -ENODEV - invalid smd channel
+ *      -EACCES - non-packet channel specified
+ *      -EINVAL - invalid length
+ *      -EBUSY - transaction already in progress
+ *      -EAGAIN - no enough memory in ring buffer to start transaction
+ *      -EPERM - unable to successfully start transaction due to write error
+ */
+int smd_write_start(smd_channel_t *ch, int len);
+
+/* Writes a segment of the packet for a packet transaction.
+ *
+ * @ch: channel to write packet to
+ * @data: buffer of data to write
+ * @len: length of data buffer
+ *
+ * Returns:
+ *      number of bytes written
+ *      -ENODEV - invalid smd channel
+ *      -EINVAL - invalid length
+ *      -ENOEXEC - transaction not started
+ */
+int smd_write_segment(smd_channel_t *ch, const void *data, int len);
+
+/* Completes a packet transaction.  Do not call from interrupt context.
+ *
+ * @ch: channel to complete transaction on
+ *
+ * Returns:
+ *      0 - success
+ *      -ENODEV - invalid smd channel
+ *      -E2BIG - some ammount of packet is not yet written
+ */
+int smd_write_end(smd_channel_t *ch);
+
+/**
+ * smd_write_segment_avail() - available write space for packet transactions
+ * @ch: channel to write packet to
+ * @returns: number of bytes available to write to, or -ENODEV for invalid ch
+ *
+ * This is a version of smd_write_avail() intended for use with packet
+ * transactions.  This version correctly accounts for any internal reserved
+ * space at all stages of the transaction.
+ */
+int smd_write_segment_avail(smd_channel_t *ch);
+
+/*
+ * Returns a pointer to the subsystem name or NULL if no
+ * subsystem name is available.
+ *
+ * @type - Edge definition
+ */
+const char *smd_edge_to_subsystem(uint32_t type);
+
+/*
+ * Returns a pointer to the subsystem name given the
+ * remote processor ID.
+ *
+ * @pid     Remote processor ID
+ * @returns Pointer to subsystem name or NULL if not found
+ */
+const char *smd_pid_to_subsystem(uint32_t pid);
+
+/*
+ * Checks to see if a new packet has arrived on the channel.  Only to be
+ * called with interrupts disabled.
+ *
+ * @ch: channel to check if a packet has arrived
+ *
+ * Returns:
+ *      0 - packet not available
+ *      1 - packet available
+ *      -EINVAL - NULL parameter or non-packet based channel provided
+ */
+int smd_is_pkt_avail(smd_channel_t *ch);
+
+/*
+ * SMD initialization function that registers for a SMD platform driver.
+ *
+ * returns success on successful driver registration.
+ */
+int __init msm_smd_init(void);
+
+/**
+ * smd_remote_ss_to_edge() - return edge type from remote ss type
+ * @name:	remote subsystem name
+ *
+ * Returns the edge type connected between the local subsystem(APPS)
+ * and remote subsystem @name.
+ */
+int smd_remote_ss_to_edge(const char *name);
+
+/**
+ * smd_edge_to_pil_str - Returns the PIL string used to load the remote side of
+ *			the indicated edge.
+ *
+ * @type - Edge definition
+ * @returns - The PIL string to load the remove side of @type or NULL if the
+ *		PIL string does not exist.
+ */
+const char *smd_edge_to_pil_str(uint32_t type);
+
+#else
+
+static inline int smd_close(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int smd_read(smd_channel_t *ch, void *data, int len)
+{
+	return -ENODEV;
+}
+
+static inline int smd_read_from_cb(smd_channel_t *ch, void *data, int len)
+{
+	return -ENODEV;
+}
+
+static inline int smd_write(smd_channel_t *ch, const void *data, int len)
+{
+	return -ENODEV;
+}
+
+static inline int smd_write_avail(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int smd_read_avail(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int smd_cur_packet_size(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int smd_tiocmget(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int
+smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear)
+{
+	return -ENODEV;
+}
+
+static inline int
+smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear)
+{
+	return -ENODEV;
+}
+
+static inline int
+smd_named_open_on_edge(const char *name, uint32_t edge, smd_channel_t **_ch,
+			   void *priv, void (*notify)(void *, unsigned int))
+{
+	return -ENODEV;
+}
+
+static inline void smd_enable_read_intr(smd_channel_t *ch)
+{
+}
+
+static inline void smd_disable_read_intr(smd_channel_t *ch)
+{
+}
+
+static inline int smd_mask_receive_interrupt(smd_channel_t *ch, bool mask,
+		const struct cpumask *cpumask)
+{
+	return -ENODEV;
+}
+
+static inline int smd_write_start(smd_channel_t *ch, int len)
+{
+	return -ENODEV;
+}
+
+static inline int
+smd_write_segment(smd_channel_t *ch, const void *data, int len)
+{
+	return -ENODEV;
+}
+
+static inline int smd_write_end(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int smd_write_segment_avail(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline const char *smd_edge_to_subsystem(uint32_t type)
+{
+	return NULL;
+}
+
+static inline const char *smd_pid_to_subsystem(uint32_t pid)
+{
+	return NULL;
+}
+
+static inline int smd_is_pkt_avail(smd_channel_t *ch)
+{
+	return -ENODEV;
+}
+
+static inline int __init msm_smd_init(void)
+{
+	return 0;
+}
+
+static inline int smd_remote_ss_to_edge(const char *name)
+{
+	return -EINVAL;
+}
+
+static inline const char *smd_edge_to_pil_str(uint32_t type)
+{
+	return NULL;
+}
+#endif
+
+#endif
diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
index bef98d6..6bb76f7 100644
--- a/include/soc/qcom/smem.h
+++ b/include/soc/qcom/smem.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-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
@@ -21,7 +21,8 @@
 	SMEM_Q6,
 	SMEM_DSPS,
 	SMEM_WCNSS,
-	SMEM_CDSP,
+	SMEM_MODEM_Q6_FW,
+	SMEM_CDSP = SMEM_MODEM_Q6_FW,
 	SMEM_RPM,
 	SMEM_TZ,
 	SMEM_SPSS,
diff --git a/include/soc/qcom/smsm.h b/include/soc/qcom/smsm.h
new file mode 100644
index 0000000..00d31e8
--- /dev/null
+++ b/include/soc/qcom/smsm.h
@@ -0,0 +1,147 @@
+/* Copyright (c) 2011-2013, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SMSM_H_
+#define _ARCH_ARM_MACH_MSM_SMSM_H_
+
+#include <soc/qcom/smem.h>
+
+enum {
+	SMSM_APPS_STATE,
+	SMSM_MODEM_STATE,
+	SMSM_Q6_STATE,
+	SMSM_APPS_DEM,
+	SMSM_WCNSS_STATE = SMSM_APPS_DEM,
+	SMSM_MODEM_DEM,
+	SMSM_DSPS_STATE = SMSM_MODEM_DEM,
+	SMSM_Q6_DEM,
+	SMSM_POWER_MASTER_DEM,
+	SMSM_TIME_MASTER_DEM,
+};
+extern uint32_t SMSM_NUM_ENTRIES;
+
+/*
+ * Ordered by when processors adopted the SMSM protocol.  May not be 1-to-1
+ * with SMEM PIDs, despite initial expectations.
+ */
+enum {
+	SMSM_APPS = SMEM_APPS,
+	SMSM_MODEM = SMEM_MODEM,
+	SMSM_Q6 = SMEM_Q6,
+	SMSM_WCNSS,
+	SMSM_DSPS,
+};
+extern uint32_t SMSM_NUM_HOSTS;
+
+#define SMSM_INIT              0x00000001
+#define SMSM_SMDINIT           0x00000008
+#define SMSM_RPCINIT           0x00000020
+#define SMSM_RESET             0x00000040
+#define SMSM_TIMEWAIT          0x00000400
+#define SMSM_TIMEINIT          0x00000800
+#define SMSM_PROC_AWAKE        0x00001000
+#define SMSM_SMD_LOOPBACK      0x00800000
+
+#define SMSM_USB_PLUG_UNPLUG    0x00002000
+
+#define SMSM_A2_POWER_CONTROL  0x00000002
+#define SMSM_A2_POWER_CONTROL_ACK  0x00000800
+
+#ifdef CONFIG_MSM_SMD
+int smsm_change_state(uint32_t smsm_entry,
+		      uint32_t clear_mask, uint32_t set_mask);
+
+/*
+ * Changes the global interrupt mask.  The set and clear masks are re-applied
+ * every time the global interrupt mask is updated for callback registration
+ * and de-registration.
+ *
+ * The clear mask is applied first, so if a bit is set to 1 in both the clear
+ * mask and the set mask, the result will be that the interrupt is set.
+ *
+ * @smsm_entry  SMSM entry to change
+ * @clear_mask  1 = clear bit, 0 = no-op
+ * @set_mask    1 = set bit, 0 = no-op
+ *
+ * @returns 0 for success, < 0 for error
+ */
+int smsm_change_intr_mask(uint32_t smsm_entry,
+			  uint32_t clear_mask, uint32_t set_mask);
+int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask);
+uint32_t smsm_get_state(uint32_t smsm_entry);
+int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask,
+	void (*notify)(void *, uint32_t old_state, uint32_t new_state),
+	void *data);
+int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask,
+	void (*notify)(void *, uint32_t, uint32_t), void *data);
+
+#else
+static inline int smsm_change_state(uint32_t smsm_entry,
+		      uint32_t clear_mask, uint32_t set_mask)
+{
+	return -ENODEV;
+}
+
+/*
+ * Changes the global interrupt mask.  The set and clear masks are re-applied
+ * every time the global interrupt mask is updated for callback registration
+ * and de-registration.
+ *
+ * The clear mask is applied first, so if a bit is set to 1 in both the clear
+ * mask and the set mask, the result will be that the interrupt is set.
+ *
+ * @smsm_entry  SMSM entry to change
+ * @clear_mask  1 = clear bit, 0 = no-op
+ * @set_mask    1 = set bit, 0 = no-op
+ *
+ * @returns 0 for success, < 0 for error
+ */
+static inline int smsm_change_intr_mask(uint32_t smsm_entry,
+			  uint32_t clear_mask, uint32_t set_mask)
+{
+	return -ENODEV;
+}
+
+static inline int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask)
+{
+	return -ENODEV;
+}
+static inline uint32_t smsm_get_state(uint32_t smsm_entry)
+{
+	return 0;
+}
+static inline int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask,
+	void (*notify)(void *, uint32_t old_state, uint32_t new_state),
+	void *data)
+{
+	return -ENODEV;
+}
+static inline int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask,
+	void (*notify)(void *, uint32_t, uint32_t), void *data)
+{
+	return -ENODEV;
+}
+static inline void smsm_reset_modem(unsigned int mode)
+{
+}
+static inline void smsm_reset_modem_cont(void)
+{
+}
+static inline void smd_sleep_exit(void)
+{
+}
+static inline int smsm_check_for_modem_crash(void)
+{
+	return -ENODEV;
+}
+#endif
+#endif
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/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c
index 50d9b51..bc1829e 100644
--- a/net/rmnet_data/rmnet_data_config.c
+++ b/net/rmnet_data/rmnet_data_config.c
@@ -25,6 +25,7 @@
 #include "rmnet_data_vnd.h"
 #include "rmnet_data_private.h"
 #include "rmnet_data_trace.h"
+#include "rmnet_map.h"
 
 RMNET_LOG_MODULE(RMNET_DATA_LOGMASK_CONFIG);
 
@@ -869,7 +870,8 @@
 	conf->dev = dev;
 	spin_lock_init(&conf->agg_lock);
 	config->recycle = kfree_skb;
-
+	hrtimer_init(&conf->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	conf->hrtimer.function = rmnet_map_flush_packet_queue;
 	rc = netdev_rx_handler_register(dev, rmnet_rx_handler, config);
 
 	if (rc) {
@@ -1232,6 +1234,22 @@
 	config = _rmnet_get_phys_ep_config(dev);
 
 	if (config) {
+		unsigned long flags;
+
+		hrtimer_cancel(&config->hrtimer);
+		spin_lock_irqsave(&config->agg_lock, flags);
+		if (config->agg_state == RMNET_MAP_TXFER_SCHEDULED) {
+			if (config->agg_skb) {
+				kfree_skb(config->agg_skb);
+				config->agg_skb = NULL;
+				config->agg_count = 0;
+				memset(&config->agg_time, 0,
+				       sizeof(struct timespec));
+			}
+			config->agg_state = RMNET_MAP_AGG_IDLE;
+		}
+		spin_unlock_irqrestore(&config->agg_lock, flags);
+
 		cfg = &config->local_ep;
 
 		if (cfg && cfg->refcount)
diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h
index aa8a0b5..4142656 100644
--- a/net/rmnet_data/rmnet_data_config.h
+++ b/net/rmnet_data/rmnet_data_config.h
@@ -16,6 +16,7 @@
 #include <linux/time.h>
 #include <linux/spinlock.h>
 #include <net/rmnet_config.h>
+#include <linux/hrtimer.h>
 
 #ifndef _RMNET_DATA_CONFIG_H_
 #define _RMNET_DATA_CONFIG_H_
@@ -85,6 +86,7 @@
 	u8 agg_count;
 	struct timespec agg_time;
 	struct timespec agg_last;
+	struct hrtimer hrtimer;
 };
 
 int rmnet_config_init(void);
diff --git a/net/rmnet_data/rmnet_map.h b/net/rmnet_data/rmnet_map.h
index 3bab6d9..718140c 100644
--- a/net/rmnet_data/rmnet_map.h
+++ b/net/rmnet_data/rmnet_map.h
@@ -147,4 +147,5 @@
 				     struct net_device *orig_dev,
 				     u32 egress_data_format);
 int rmnet_ul_aggregation_skip(struct sk_buff *skb, int offset);
+enum hrtimer_restart rmnet_map_flush_packet_queue(struct hrtimer *t);
 #endif /* _RMNET_MAP_H_ */
diff --git a/net/rmnet_data/rmnet_map_data.c b/net/rmnet_data/rmnet_map_data.c
index 1c0f1060..f24b157 100644
--- a/net/rmnet_data/rmnet_map_data.c
+++ b/net/rmnet_data/rmnet_map_data.c
@@ -18,7 +18,6 @@
 #include <linux/netdevice.h>
 #include <linux/rmnet_data.h>
 #include <linux/spinlock.h>
-#include <linux/workqueue.h>
 #include <linux/time.h>
 #include <linux/net_map.h>
 #include <linux/ip.h>
@@ -48,11 +47,6 @@
 module_param(agg_bypass_time, long, 0644);
 MODULE_PARM_DESC(agg_bypass_time, "Skip agg when apart spaced more than this");
 
-struct agg_work {
-	struct delayed_work work;
-	struct rmnet_phys_ep_config *config;
-};
-
 #define RMNET_MAP_DEAGGR_SPACING  64
 #define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
 
@@ -166,24 +160,21 @@
 }
 
 /* rmnet_map_flush_packet_queue() - Transmits aggregeted frame on timeout
- * @work:        struct agg_work containing delayed work and skb to flush
  *
- * This function is scheduled to run in a specified number of jiffies after
+ * This function is scheduled to run in a specified number of ns after
  * the last frame transmitted by the network stack. When run, the buffer
  * containing aggregated packets is finally transmitted on the underlying link.
  *
  */
-static void rmnet_map_flush_packet_queue(struct work_struct *work)
+enum hrtimer_restart rmnet_map_flush_packet_queue(struct hrtimer *t)
 {
-	struct agg_work *real_work;
 	struct rmnet_phys_ep_config *config;
 	unsigned long flags;
 	struct sk_buff *skb;
 	int rc, agg_count = 0;
 
+	config = container_of(t, struct rmnet_phys_ep_config, hrtimer);
 	skb = 0;
-	real_work = (struct agg_work *)work;
-	config = real_work->config;
 	LOGD("%s", "Entering flush thread");
 	spin_lock_irqsave(&config->agg_lock, flags);
 	if (likely(config->agg_state == RMNET_MAP_TXFER_SCHEDULED)) {
@@ -211,7 +202,8 @@
 		rc = dev_queue_xmit(skb);
 		rmnet_stats_queue_xmit(rc, RMNET_STATS_QUEUE_XMIT_AGG_TIMEOUT);
 	}
-	kfree(work);
+
+	return HRTIMER_NORESTART;
 }
 
 /* rmnet_map_aggregate() - Software aggregates multiple packets.
@@ -226,7 +218,6 @@
 void rmnet_map_aggregate(struct sk_buff *skb,
 			 struct rmnet_phys_ep_config *config) {
 	u8 *dest_buff;
-	struct agg_work *work;
 	unsigned long flags;
 	struct sk_buff *agg_skb;
 	struct timespec diff, last;
@@ -290,7 +281,9 @@
 		config->agg_skb = 0;
 		config->agg_count = 0;
 		memset(&config->agg_time, 0, sizeof(struct timespec));
+		config->agg_state = RMNET_MAP_AGG_IDLE;
 		spin_unlock_irqrestore(&config->agg_lock, flags);
+		hrtimer_cancel(&config->hrtimer);
 		LOGL("delta t: %ld.%09lu\tcount: %d", diff.tv_sec,
 		     diff.tv_nsec, agg_count);
 		trace_rmnet_map_aggregate(skb, agg_count);
@@ -307,19 +300,9 @@
 
 schedule:
 	if (config->agg_state != RMNET_MAP_TXFER_SCHEDULED) {
-		work = kmalloc(sizeof(*work), GFP_ATOMIC);
-		if (!work) {
-			LOGE("Failed to allocate work item for packet %s",
-			     "transfer. DATA PATH LIKELY BROKEN!");
-			config->agg_state = RMNET_MAP_AGG_IDLE;
-			spin_unlock_irqrestore(&config->agg_lock, flags);
-			return;
-		}
-		INIT_DELAYED_WORK((struct delayed_work *)work,
-				  rmnet_map_flush_packet_queue);
-		work->config = config;
 		config->agg_state = RMNET_MAP_TXFER_SCHEDULED;
-		schedule_delayed_work((struct delayed_work *)work, 1);
+		hrtimer_start(&config->hrtimer, ns_to_ktime(3000000),
+			      HRTIMER_MODE_REL);
 	}
 	spin_unlock_irqrestore(&config->agg_lock, flags);
 }
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;