Merge "drm/msm/sde: update max up/down scale" into msm-4.8
diff --git a/Documentation/devicetree/bindings/arm/cache.txt b/Documentation/devicetree/bindings/arm/cache.txt
new file mode 100644
index 0000000..a9594f0
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/cache.txt
@@ -0,0 +1,195 @@
+==========================================
+ARM processors cache binding description
+==========================================
+
+Device tree bindings for ARM processor caches adhere to the cache bindings
+described in [3], in section 3.8 for multi-level and shared caches.
+On ARM based systems most of the cache properties related to cache
+geometry are probeable in HW, hence, unless otherwise stated, the properties
+defined in ePAPR for multi-level and shared caches are to be considered
+optional by default.
+
+On ARM, caches are either architected (directly controlled by the processor
+through coprocessor instructions and tightly coupled with the processor
+implementation) or unarchitected (controlled through a memory mapped
+interface, implemented as a stand-alone IP external to the processor
+implementation).
+
+This document provides the device tree bindings for ARM architected caches.
+
+- ARM architected cache node
+
+	Description: must be a direct child of the cpu node. A system
+		     can contain multiple architected cache nodes per cpu node,
+		     linked through the next-level-cache phandle. The
+		     next-level-cache property in the cpu node points to
+		     the first level of architected cache for the CPU.
+		     The next-level-cache property in architected cache nodes
+		     points to the respective next level of caching in the
+		     hierarchy. An architected cache node with an empty or
+		     missing next-level-cache property represents the last
+		     architected cache level for the CPU.
+		     On ARM v7 and v8 architectures, the order in which cache
+		     nodes are linked through the next-level-cache phandle must
+		     follow the ordering specified in the processors CLIDR (v7)
+		     and CLIDR_EL1 (v8) registers, as described in [1][2],
+		     implying that a cache node pointed at by a
+		     next-level-cache phandle must correspond to a level
+		     defined in CLIDR (v7) and CLIDR_EL1 (v8) greater than the
+		     one the cache node containing the next-level-cache
+		     phandle corresponds to.
+
+	Since on ARM most of the cache properties are probeable in HW the
+	properties described in [3] - section 3.8 multi-level and shared
+	caches - shall be considered optional, with the following properties
+	updates, specific for the ARM architected cache node.
+
+	- compatible
+		Usage: Required
+		Value type: <string>
+		Definition: value shall be "arm,arch-cache".
+
+	- interrupts
+		Usage: Optional
+		Value type: See definition
+		Definition: standard device tree property [3] that defines
+			    the interrupt line associated with the cache.
+			    The property can be accompanied by an
+			    interrupt-names property, as described in [4].
+
+	- power-domain
+		Usage: Optional
+		Value type: phandle
+		Definition: A phandle and power domain specifier as defined by
+			    bindings of power controller specified by the
+			    phandle [5].
+
+	- qcom,dump-size
+		Usage: Optional
+		Value type: <integer>
+		Definition: The memory size needed to contain a copy of the
+			    cache data and associated tag ram.
+			    size = nways * nsets * (bytes per cache line +
+			                            bytes tag ram per line)
+
+Example(dual-cluster big.LITTLE system 32-bit)
+
+	cpus {
+		#size-cells = <0>;
+		#address-cells = <1>;
+
+		cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x0>;
+			next-level-cache = <&L1_0>;
+
+			L1_0: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_0>;
+			};
+
+			L2_0: l2-cache {
+				compatible = "arm,arch-cache";
+			};
+		};
+
+		cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x1>;
+			next-level-cache = <&L1_1>;
+
+			L1_1: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_0>;
+			};
+		};
+
+		cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x2>;
+			next-level-cache = <&L1_2>;
+
+			L1_2: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_0>;
+			};
+		};
+
+		cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x3>;
+			next-level-cache = <&L1_3>;
+
+			L1_3: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_0>;
+			};
+		};
+
+		cpu@100 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x100>;
+			next-level-cache = <&L1_4>;
+
+			L1_4: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_1>;
+			};
+
+			L2_1: l2-cache {
+				compatible = "arm,arch-cache";
+			};
+		};
+
+		cpu@101 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x101>;
+			next-level-cache = <&L1_5>;
+
+			L1_5: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_1>;
+			};
+		};
+
+		cpu@102 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x102>;
+			next-level-cache = <&L1_6>;
+
+			L1_6: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_1>;
+			};
+		};
+
+		cpu@103 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x103>;
+			next-level-cache = <&L1_7>;
+
+			L1_7: l1-cache {
+				compatible = "arm,arch-cache";
+				next-level-cache = <&L2_1>;
+			};
+		};
+	};
+
+[1] ARMv7-AR Reference Manual
+    http://infocenter.arm.com/help/index.jsp
+[2] ARMv8-A Reference Manual
+    http://infocenter.arm.com/help/index.jsp
+[3] ePAPR standard
+    https://www.power.org/documentation/epapr-version-1-1/
+[4] Kernel documentation - resource property bindings
+    Documentation/devicetree/bindings/resource-names.txt
+[5] Kernel documentation - power domain bindings
+    Documentation/devicetree/bindings/power/power_domain.txt
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_gladiator_hang_detect.txt b/Documentation/devicetree/bindings/arm/msm/msm_gladiator_hang_detect.txt
index d35bb84..3c399ea 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_gladiator_hang_detect.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_gladiator_hang_detect.txt
@@ -21,7 +21,7 @@
 detection on PCIO port
 
 Required properties:
-- compatible : "qcom,gladiator-hang-detect"
+- compatible : "qcom,gladiator-hang-detect" or "qcom,gladiator-hang-detect-v2"
 - qcom, threshold-arr:
 		Array of APCS_COMMON_GLADIATOR_HANG_THRESHOLD_n register
 		address
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index e3e6983..7c4cd86 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -15,6 +15,7 @@
  - phys: from the *Generic PHY* bindings
  - phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
 	or "usb3-phy".
+ - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
  - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
  - snps,disable_scramble_quirk: true when SW should disable data scrambling.
 	Only really useful for FPGA builds.
@@ -54,8 +55,6 @@
 	register for post-silicon frame length adjustment when the
 	fladj_30mhz_sdbnd signal is invalid or incorrect.
 
- - <DEPRECATED> tx-fifo-resize: determines if the FIFO *has* to be reallocated.
-
 This is usually a subnode to DWC3 glue to which it is connected.
 
 dwc3@4a030000 {
@@ -63,4 +62,5 @@
 	reg = <0x4a030000 0xcfff>;
 	interrupts = <0 92 4>
 	usb-phy = <&usb2_phy>, <&usb3,phy>;
+	tx-fifo-resize;
 };
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 1c870ac..e5113aa 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -60,6 +60,8 @@
 	  should point to external connector device, which provide "USB-HOST"
 	  cable events. A single phandle may be specified if a single connector
 	  device provides both "USB" and "USB-HOST" events.
+- qcom,num-gsi-evt-buffs: If present, specifies number of GSI based hardware accelerated
+  event buffers. 1 event buffer is needed per h/w accelerated endpoint.
 
 Sub nodes:
 - Sub node for "DWC3- USB3 controller".
@@ -84,6 +86,7 @@
 		qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
 		qcom,usb-dbm = <&dbm_1p4>;
 		qcom,lpm-to-suspend-delay-ms = <2>;
+		qcom,num-gsi-evt-buffs = <0x2>;
 
 		qcom,msm_bus,name = "usb3";
 		qcom,msm_bus,num_cases = <2>;
diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
index 39acb08..ca164e7 100644
--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.txt
@@ -59,6 +59,7 @@
 				interrupts = <0 205 0x4>;
 				phys = <&hs_phy>, <&ss_phy>;
 				phy-names = "usb2-phy", "usb3-phy";
+				tx-fifo-resize;
 				dr_mode = "host";
 			};
 		};
diff --git a/arch/arm64/boot/dts/qcom/msmskunk-rumi.dtsi b/arch/arm64/boot/dts/qcom/msmskunk-rumi.dtsi
index eb4c639..1f235d3 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk-rumi.dtsi
@@ -24,7 +24,7 @@
 	status = "ok";
 };
 
-&ufs_mem {
+&ufshc_mem {
 	limit-tx-hs-gear = <1>;
 	limit-rx-hs-gear = <1>;
 	scsi-cmd-timeout = <300000>;
@@ -77,7 +77,7 @@
 	status = "ok";
 };
 
-&ufs_card {
+&ufshc_card {
 	limit-tx-hs-gear = <1>;
 	limit-rx-hs-gear = <1>;
 
diff --git a/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi b/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi
index 317deae..74e6172 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi
@@ -28,6 +28,7 @@
 		USB3_GDSC-supply = <&usb30_prim_gdsc>;
 		qcom,usb-dbm = <&dbm_1p5>;
 		qcom,dwc-usb3-msm-tx-fifo-size = <21288>;
+		qcom,num-gsi-evt-buffs = <0x3>;
 
 		clocks = <&clock_gcc GCC_USB30_PRIM_MASTER_CLK>,
 			 <&clock_gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
@@ -52,7 +53,6 @@
 			tx-fifo-resize;
 			snps,nominal-elastic-buffer;
 			snps,hird_thresh = <0x10>;
-			snps,num-gsi-evt-buffs = <0x3>;
 		};
 	};
 
diff --git a/arch/arm64/boot/dts/qcom/msmskunk.dtsi b/arch/arm64/boot/dts/qcom/msmskunk.dtsi
index 018966a..c15a508 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk.dtsi
@@ -26,6 +26,11 @@
 	qcom,msm-id = <321 0x0>;
 	interrupt-parent = <&intc>;
 
+	aliases {
+		ufshc1 = &ufshc_mem; /* Embedded UFS slot */
+		ufshc2 = &ufshc_card; /* Removable UFS slot */
+	};
+
 	cpus {
 		#address-cells = <2>;
 		#size-cells = <0>;
@@ -50,6 +55,14 @@
 				      cache-level = <3>;
 				};
 			};
+			L1_I_0: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
+			L1_D_0: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
 		};
 
 		CPU1: cpu@1 {
@@ -66,6 +79,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_1: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
+			L1_D_1: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
 		};
 
 		CPU2: cpu@2 {
@@ -82,6 +103,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_2: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
+			L1_D_2: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
 		};
 
 		CPU3: cpu@3 {
@@ -98,6 +127,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_3: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
+			L1_D_3: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x9000>;
+			};
 		};
 
 		CPU4: cpu@100 {
@@ -114,6 +151,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_100: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
+			L1_D_100: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
 		};
 
 		CPU5: cpu@101 {
@@ -130,6 +175,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_101: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
+			L1_D_101: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
 		};
 
 		CPU6: cpu@102 {
@@ -146,6 +199,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_102: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
+			L1_D_102: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
 		};
 
 		CPU7: cpu@103 {
@@ -162,6 +223,14 @@
 			      cache-level = <2>;
 			      next-level-cache = <&L3_0>;
 			};
+			L1_I_103: l1-icache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
+			L1_D_103: l1-dcache {
+				compatible = "arm,arch-cache";
+				qcom,dump-size = <0x12000>;
+			};
 		};
 
 		cpu-map {
@@ -442,7 +511,7 @@
 		status = "disabled";
 	};
 
-	ufs_mem: ufshc_mem@1d84000 {
+	ufshc_mem: ufshc_mem@1d84000 {
 		compatible = "qcom,ufshc";
 		reg = <0x1d84000 0x2500>;
 		interrupts = <0 265 0>;
@@ -482,7 +551,7 @@
 			<0 0>,
 			<0 0>;
 
-		qcom,msm-bus,name = "ufs_mem";
+		qcom,msm-bus,name = "ufshc_mem";
 		qcom,msm-bus,num-cases = <22>;
 		qcom,msm-bus,num-paths = <2>;
 		qcom,msm-bus,vectors-KBps =
@@ -534,7 +603,7 @@
 		status = "disabled";
 	};
 
-	ufs_card: ufshc_card@1da4000 {
+	ufshc_card: ufshc_card@1da4000 {
 		compatible = "qcom,ufshc";
 		reg = <0x1da4000 0x2500>;
 		interrupts = <0 125 0>;
@@ -571,7 +640,7 @@
 			<0 0>,
 			<0 0>;
 
-		qcom,msm-bus,name = "ufs_card";
+		qcom,msm-bus,name = "ufshc_card";
 		qcom,msm-bus,num-cases = <9>;
 		qcom,msm-bus,num-paths = <2>;
 		qcom,msm-bus,vectors-KBps =
@@ -793,6 +862,59 @@
 		status = "ok";
 	};
 
+	qcom,msm_fastrpc {
+		compatible = "qcom,msm-fastrpc-compute";
+
+		qcom,msm_fastrpc_compute_cb1 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1401>,
+				 <&apps_smmu 0x1421>;
+		};
+		qcom,msm_fastrpc_compute_cb2 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1402>,
+				 <&apps_smmu 0x1422>;
+		};
+		qcom,msm_fastrpc_compute_cb3 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1403>,
+				 <&apps_smmu 0x1423>;
+		};
+		qcom,msm_fastrpc_compute_cb4 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1404>,
+				 <&apps_smmu 0x1424>;
+		};
+		qcom,msm_fastrpc_compute_cb5 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1405>,
+				 <&apps_smmu 0x1425>;
+		};
+		qcom,msm_fastrpc_compute_cb6 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1406>,
+				 <&apps_smmu 0x1426>;
+		};
+		qcom,msm_fastrpc_compute_cb7 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1407>,
+				 <&apps_smmu 0x1427>;
+		};
+		qcom,msm_fastrpc_compute_cb8 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "cdsprpc-smd";
+			iommus = <&apps_smmu 0x1408>,
+				 <&apps_smmu 0x1428>;
+		};
+	};
+
 	qcom,msm-imem@146bf000 {
 		compatible = "qcom,msm-imem";
 		reg = <0x146bf000 0x1000>;
@@ -837,6 +959,74 @@
 		status = "ok";
 	};
 
+	cpuss_dump {
+		compatible = "qcom,cpuss-dump";
+		qcom,l1_i_cache0 {
+			qcom,dump-node = <&L1_I_0>;
+			qcom,dump-id = <0x60>;
+		};
+		qcom,l1_i_cache1 {
+			qcom,dump-node = <&L1_I_1>;
+			qcom,dump-id = <0x61>;
+		};
+		qcom,l1_i_cache2 {
+			qcom,dump-node = <&L1_I_2>;
+			qcom,dump-id = <0x62>;
+		};
+		qcom,l1_i_cache3 {
+			qcom,dump-node = <&L1_I_3>;
+			qcom,dump-id = <0x63>;
+		};
+		qcom,l1_i_cache100 {
+			qcom,dump-node = <&L1_I_100>;
+			qcom,dump-id = <0x64>;
+		};
+		qcom,l1_i_cache101 {
+			qcom,dump-node = <&L1_I_101>;
+			qcom,dump-id = <0x65>;
+		};
+		qcom,l1_i_cache102 {
+			qcom,dump-node = <&L1_I_102>;
+			qcom,dump-id = <0x66>;
+		};
+		qcom,l1_i_cache103 {
+			qcom,dump-node = <&L1_I_103>;
+			qcom,dump-id = <0x67>;
+		};
+		qcom,l1_d_cache0 {
+			qcom,dump-node = <&L1_D_0>;
+			qcom,dump-id = <0x80>;
+		};
+		qcom,l1_d_cache1 {
+			qcom,dump-node = <&L1_D_1>;
+			qcom,dump-id = <0x81>;
+		};
+		qcom,l1_d_cache2 {
+			qcom,dump-node = <&L1_D_2>;
+			qcom,dump-id = <0x82>;
+		};
+		qcom,l1_d_cache3 {
+			qcom,dump-node = <&L1_D_3>;
+			qcom,dump-id = <0x83>;
+		};
+		qcom,l1_d_cache100 {
+			qcom,dump-node = <&L1_D_100>;
+			qcom,dump-id = <0x84>;
+		};
+		qcom,l1_d_cache101 {
+			qcom,dump-node = <&L1_D_101>;
+			qcom,dump-id = <0x85>;
+		};
+		qcom,l1_d_cache102 {
+			qcom,dump-node = <&L1_D_102>;
+			qcom,dump-id = <0x86>;
+		};
+		qcom,l1_d_cache103 {
+			qcom,dump-node = <&L1_D_103>;
+			qcom,dump-id = <0x87>;
+		};
+	};
+
 	kryo3xx-erp {
 		compatible = "arm,arm64-kryo3xx-cpu-erp";
 		interrupts = <1 6 4>,
@@ -1268,7 +1458,7 @@
 	};
 
 	qcom,ghd {
-		compatible = "qcom,gladiator-hang-detect";
+		compatible = "qcom,gladiator-hang-detect-v2";
 		qcom,threshold-arr = <0x1799041c 0x17990420>;
 		qcom,config-reg = <0x17990434>;
 	};
diff --git a/arch/arm64/configs/msmskunk-perf_defconfig b/arch/arm64/configs/msmskunk-perf_defconfig
index faf1351..7ea20da 100644
--- a/arch/arm64/configs/msmskunk-perf_defconfig
+++ b/arch/arm64/configs/msmskunk-perf_defconfig
@@ -243,6 +243,7 @@
 # CONFIG_DEVMEM is not set
 # CONFIG_DEVKMEM is not set
 CONFIG_HW_RANDOM=y
+CONFIG_MSM_ADSPRPC=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
@@ -298,6 +299,7 @@
 CONFIG_USB_CONFIGFS_F_FS=y
 CONFIG_USB_CONFIGFS_F_HID=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_TEST=y
diff --git a/arch/arm64/configs/msmskunk_defconfig b/arch/arm64/configs/msmskunk_defconfig
index dde0e84..1a7fdcd 100644
--- a/arch/arm64/configs/msmskunk_defconfig
+++ b/arch/arm64/configs/msmskunk_defconfig
@@ -250,6 +250,7 @@
 CONFIG_SERIAL_MSM_CONSOLE=y
 CONFIG_HVC_DCC=y
 CONFIG_HW_RANDOM=y
+CONFIG_MSM_ADSPRPC=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
@@ -304,6 +305,7 @@
 CONFIG_USB_CONFIGFS_F_FS=y
 CONFIG_USB_CONFIGFS_F_HID=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
+CONFIG_USB_CONFIGFS_F_CDEV=y
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_TEST=y
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 1a5f382..866ee6f 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -56,6 +56,7 @@
 #define BALIGN		128
 #define NUM_CHANNELS	4	/* adsp, mdsp, slpi, cdsp*/
 #define NUM_SESSIONS	9	/*8 compute, 1 cpz*/
+#define M_FDLIST	16
 
 #define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0)
 
@@ -429,7 +430,7 @@
 		ion_free(fl->apps->client, map->handle);
 	if (sess->smmu.enabled) {
 		if (map->size || map->phys)
-			msm_dma_unmap_sg(fl->sctx->dev,
+			msm_dma_unmap_sg(sess->dev,
 				map->table->sgl,
 				map->table->nents, DMA_BIDIRECTIONAL,
 				map->buf);
@@ -926,12 +927,13 @@
 	uint32_t sc = ctx->sc;
 	int inbufs = REMOTE_SCALARS_INBUFS(sc);
 	int outbufs = REMOTE_SCALARS_OUTBUFS(sc);
-	int bufs = inbufs + outbufs;
+	int handles, bufs = inbufs + outbufs;
 	uintptr_t args;
 	ssize_t rlen = 0, copylen = 0, metalen = 0;
-	int i, inh, oix;
+	int i, oix;
 	int err = 0;
 	int mflags = 0;
+	uint64_t *fdlist;
 
 	/* calculate size of the metadata */
 	rpra = 0;
@@ -949,7 +951,15 @@
 					mflags, &ctx->maps[i]);
 		ipage += 1;
 	}
-	metalen = copylen = (ssize_t)&ipage[0];
+	handles = REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc);
+	for (i = bufs; i < bufs + handles; i++) {
+		VERIFY(err, !fastrpc_mmap_create(ctx->fl, ctx->fds[i],
+				FASTRPC_ATTR_NOVA, 0, 0, 0, &ctx->maps[i]));
+		if (err)
+			goto bail;
+		ipage += 1;
+	}
+	metalen = copylen = (ssize_t)&ipage[0] + (sizeof(uint64_t) * M_FDLIST);
 	/* calculate len requreed for copying */
 	for (oix = 0; oix < inbufs + outbufs; ++oix) {
 		int i = ctx->overps[oix]->raix;
@@ -978,14 +988,11 @@
 	pages = smq_phy_page_start(sc, list);
 	ipage = pages;
 	args = (uintptr_t)ctx->buf->virt + metalen;
-	for (i = 0; i < bufs; ++i) {
-		ssize_t len = lpra[i].buf.len;
-
-		list[i].num = 0;
-		list[i].pgidx = 0;
-		if (!len)
-			continue;
-		list[i].num = 1;
+	for (i = 0; i < bufs + handles; ++i) {
+		if (lpra[i].buf.len)
+			list[i].num = 1;
+		else
+			list[i].num = 0;
 		list[i].pgidx = ipage - pages;
 		ipage++;
 	}
@@ -1006,7 +1013,7 @@
 			int idx = list[i].pgidx;
 
 			if (map->attr & FASTRPC_ATTR_NOVA) {
-				offset = (uintptr_t)lpra[i].buf.pv;
+				offset = 0;
 			} else {
 				down_read(&current->mm->mmap_sem);
 				VERIFY(err, NULL != (vma = find_vma(current->mm,
@@ -1026,6 +1033,15 @@
 		}
 		rpra[i].buf.pv = buf;
 	}
+	for (i = bufs; i < bufs + handles; ++i) {
+		struct fastrpc_mmap *map = ctx->maps[i];
+
+		pages[i].addr = map->phys;
+		pages[i].size = map->size;
+	}
+	fdlist = (uint64_t *)&pages[bufs + handles];
+	for (i = 0; i < M_FDLIST; i++)
+		fdlist[i] = 0;
 	/* copy non ion buffers */
 	rlen = copylen - metalen;
 	for (oix = 0; oix < inbufs + outbufs; ++oix) {
@@ -1076,11 +1092,10 @@
 			dmac_flush_range(uint64_to_ptr(rpra[i].buf.pv),
 			uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len));
 	}
-	inh = inbufs + outbufs;
-	for (i = 0; i < REMOTE_SCALARS_INHANDLES(sc); i++) {
-		rpra[inh + i].buf.pv = ptr_to_uint64(ctx->lpra[inh + i].buf.pv);
-		rpra[inh + i].buf.len = ctx->lpra[inh + i].buf.len;
-		rpra[inh + i].h = ctx->lpra[inh + i].h;
+	for (i = bufs; i < bufs + handles; i++) {
+		rpra[i].dma.fd = ctx->fds[i];
+		rpra[i].dma.len = (uint32_t)lpra[i].buf.len;
+		rpra[i].dma.offset = (uint32_t)(uintptr_t)lpra[i].buf.pv;
 	}
 	if (!ctx->fl->sctx->smmu.coherent)
 		dmac_flush_range((char *)rpra, (char *)rpra + ctx->used);
@@ -1092,12 +1107,20 @@
 		    remote_arg_t *upra)
 {
 	uint32_t sc = ctx->sc;
+	struct smq_invoke_buf *list;
+	struct smq_phy_page *pages;
+	struct fastrpc_mmap *mmap;
+	uint64_t *fdlist;
 	remote_arg64_t *rpra = ctx->rpra;
-	int i, inbufs, outbufs, outh, size;
+	int i, inbufs, outbufs, handles;
 	int err = 0;
 
 	inbufs = REMOTE_SCALARS_INBUFS(sc);
 	outbufs = REMOTE_SCALARS_OUTBUFS(sc);
+	handles = REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc);
+	list = smq_invoke_buf_start(ctx->rpra, sc);
+	pages = smq_phy_page_start(sc, list);
+	fdlist = (uint64_t *)(pages + inbufs + outbufs + handles);
 	for (i = inbufs; i < inbufs + outbufs; ++i) {
 		if (!ctx->maps[i]) {
 			K_COPY_TO_USER(err, kernel,
@@ -1111,12 +1134,14 @@
 			ctx->maps[i] = 0;
 		}
 	}
-	size = sizeof(*rpra) * REMOTE_SCALARS_OUTHANDLES(sc);
-	if (size) {
-		outh = inbufs + outbufs + REMOTE_SCALARS_INHANDLES(sc);
-		K_COPY_TO_USER(err, kernel, &upra[outh], &rpra[outh], size);
-		if (err)
-			goto bail;
+	if (inbufs + outbufs + handles) {
+		for (i = 0; i < M_FDLIST; i++) {
+			if (!fdlist[i])
+				break;
+			if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], 0, 0,
+						0, &mmap))
+				fastrpc_mmap_free(mmap);
+		}
 	}
  bail:
 	return err;
@@ -1158,7 +1183,6 @@
 	uint32_t sc = ctx->sc;
 	remote_arg64_t *rpra = ctx->rpra;
 	int used = ctx->used;
-	int inv = 0;
 
 	inbufs = REMOTE_SCALARS_INBUFS(sc);
 	outbufs = REMOTE_SCALARS_OUTBUFS(sc);
@@ -1171,7 +1195,6 @@
 			continue;
 		if (buf_page_start(ptr_to_uint64((void *)rpra)) ==
 				buf_page_start(rpra[i].buf.pv)) {
-			inv = 1;
 			continue;
 		}
 		if (map && map->handle)
@@ -1184,7 +1207,7 @@
 						 + rpra[i].buf.len));
 	}
 
-	if (inv || REMOTE_SCALARS_OUTHANDLES(sc))
+	if (rpra)
 		dmac_inv_range(rpra, (char *)rpra + used);
 }
 
diff --git a/drivers/char/adsprpc_compat.c b/drivers/char/adsprpc_compat.c
index 1e5649a..59d1ed7 100644
--- a/drivers/char/adsprpc_compat.c
+++ b/drivers/char/adsprpc_compat.c
@@ -94,7 +94,7 @@
 	struct fastrpc_ioctl_invoke_attrs *inv;
 	union compat_remote_arg *pra32;
 	union remote_arg *pra;
-	int err, len, num, j;
+	int err, len, j;
 
 	err = get_user(sc, &inv32->inv.sc);
 	if (err)
@@ -117,17 +117,12 @@
 
 	pra32 = compat_ptr(p);
 	pra = (union remote_arg *)(inv + 1);
-	num = REMOTE_SCALARS_INBUFS(sc) + REMOTE_SCALARS_OUTBUFS(sc);
-	for (j = 0; j < num; j++) {
+	for (j = 0; j < len; j++) {
 		err |= get_user(p, &pra32[j].buf.pv);
 		err |= put_user(p, (uintptr_t *)&pra[j].buf.pv);
 		err |= get_user(s, &pra32[j].buf.len);
 		err |= put_user(s, &pra[j].buf.len);
 	}
-	for (j = 0; j < REMOTE_SCALARS_INHANDLES(sc); j++) {
-		err |= get_user(u, &pra32[num + j].h);
-		err |= put_user(u, &pra[num + j].h);
-	}
 
 	err |= put_user(NULL, &inv->fds);
 	if (cmd != COMPAT_FASTRPC_IOCTL_INVOKE) {
@@ -144,33 +139,6 @@
 	return err;
 }
 
-static int compat_put_fastrpc_ioctl_invoke(
-			struct compat_fastrpc_ioctl_invoke_attrs __user *inv32,
-			struct fastrpc_ioctl_invoke_attrs __user *inv)
-{
-	compat_uptr_t p;
-	compat_uint_t u, h;
-	union compat_remote_arg *pra32;
-	union remote_arg *pra;
-	int err, i, num;
-
-	err = get_user(u, &inv32->inv.sc);
-	err |= get_user(p, &inv32->inv.pra);
-	if (err)
-		return err;
-
-	pra32 = compat_ptr(p);
-	pra = (union remote_arg *)(inv + 1);
-	num = REMOTE_SCALARS_INBUFS(u) + REMOTE_SCALARS_OUTBUFS(u)
-		+ REMOTE_SCALARS_INHANDLES(u);
-	for (i = 0;  i < REMOTE_SCALARS_OUTHANDLES(u); i++) {
-		err |= get_user(h, &pra[num + i].h);
-		err |= put_user(h, &pra32[num + i].h);
-	}
-
-	return err;
-}
-
 static int compat_get_fastrpc_ioctl_mmap(
 			struct compat_fastrpc_ioctl_mmap __user *map32,
 			struct fastrpc_ioctl_mmap __user *map)
@@ -264,19 +232,14 @@
 	{
 		struct compat_fastrpc_ioctl_invoke_attrs __user *inv32;
 		struct fastrpc_ioctl_invoke_attrs __user *inv;
-		long ret;
 
 		inv32 = compat_ptr(arg);
 		VERIFY(err, 0 == compat_get_fastrpc_ioctl_invoke(inv32,
 							&inv, cmd));
 		if (err)
 			return err;
-		ret = filp->f_op->unlocked_ioctl(filp,
+		return filp->f_op->unlocked_ioctl(filp,
 				FASTRPC_IOCTL_INVOKE_ATTRS, (unsigned long)inv);
-		if (ret)
-			return ret;
-		VERIFY(err, 0 == compat_put_fastrpc_ioctl_invoke(inv32, inv));
-		return err;
 	}
 	case COMPAT_FASTRPC_IOCTL_MMAP:
 	{
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index 4b6b1cb..5c12ea7 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -104,8 +104,15 @@
 	int64_t len;
 };
 
+struct remote_dma_handle64 {
+	int fd;
+	uint32_t offset;
+	uint32_t len;
+};
+
 union remote_arg64 {
 	struct remote_buf64	buf;
+	struct remote_dma_handle64 dma;
 	uint32_t h;
 };
 
@@ -116,8 +123,14 @@
 	ssize_t len;		/* length of buffer */
 };
 
+struct remote_dma_handle {
+	int fd;
+	uint32_t offset;
+};
+
 union remote_arg {
 	struct remote_buf buf;	/* buffer info */
+	struct remote_dma_handle dma;
 	uint32_t h;		/* remote handle */
 };
 
@@ -205,7 +218,7 @@
 static inline struct smq_phy_page *smq_phy_page_start(uint32_t sc,
 						struct smq_invoke_buf *buf)
 {
-	int nTotal = REMOTE_SCALARS_INBUFS(sc) + REMOTE_SCALARS_OUTBUFS(sc);
+	int nTotal = REMOTE_SCALARS_LENGTH(sc);
 
 	return (struct smq_phy_page *)(&buf[nTotal]);
 }
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index f00c142..37429c4 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -935,7 +935,7 @@
 		reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
-	else if (cfg->quirks && IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT)
+	else if (cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT)
 		reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c
index 2c34d10f..bc8e956 100644
--- a/drivers/iommu/io-pgtable-fast.c
+++ b/drivers/iommu/io-pgtable-fast.c
@@ -433,7 +433,7 @@
 	cfg->pgsize_bitmap = SZ_4K;
 
 	/* TCR */
-	if (cfg->quirks && IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT)
+	if (cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT)
 		reg = (AV8L_FAST_TCR_SH_OS << AV8L_FAST_TCR_SH0_SHIFT) |
 			(AV8L_FAST_TCR_RGN_NC << AV8L_FAST_TCR_IRGN0_SHIFT) |
 			(AV8L_FAST_TCR_RGN_WBWA << AV8L_FAST_TCR_ORGN0_SHIFT);
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index df39010..a617c9e 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -105,6 +105,8 @@
 
 	ch = gsi_readl(gsi_ctx->base +
 		GSI_EE_n_CNTXT_SRC_GSI_CH_IRQ_OFFS(ee));
+	gsi_writel(ch, gsi_ctx->base +
+		GSI_EE_n_CNTXT_SRC_GSI_CH_IRQ_CLR_OFFS(ee));
 	GSIDBG("ch %x\n", ch);
 	for (i = 0; i < 32; i++) {
 		if ((1 << i) & ch) {
@@ -124,9 +126,6 @@
 			gsi_ctx->ch_dbg[i].cmd_completed++;
 		}
 	}
-
-	gsi_writel(ch, gsi_ctx->base +
-			GSI_EE_n_CNTXT_SRC_GSI_CH_IRQ_CLR_OFFS(ee));
 }
 
 static void gsi_handle_ev_ctrl(int ee)
@@ -138,6 +137,8 @@
 
 	ch = gsi_readl(gsi_ctx->base +
 		GSI_EE_n_CNTXT_SRC_EV_CH_IRQ_OFFS(ee));
+	gsi_writel(ch, gsi_ctx->base +
+		GSI_EE_n_CNTXT_SRC_EV_CH_IRQ_CLR_OFFS(ee));
 	GSIDBG("ev %x\n", ch);
 	for (i = 0; i < 32; i++) {
 		if ((1 << i) & ch) {
@@ -156,9 +157,6 @@
 			complete(&ctx->compl);
 		}
 	}
-
-	gsi_writel(ch, gsi_ctx->base +
-			GSI_EE_n_CNTXT_SRC_EV_CH_IRQ_CLR_OFFS(ee));
 }
 
 static void gsi_handle_glob_err(uint32_t err)
@@ -439,9 +437,16 @@
 		GSI_EE_n_CNTXT_SRC_IEOB_IRQ_OFFS(ee));
 	msk = gsi_readl(gsi_ctx->base +
 		GSI_EE_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(ee));
+	gsi_writel(ch & msk, gsi_ctx->base +
+		GSI_EE_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(ee));
 
 	for (i = 0; i < 32; i++) {
 		if ((1 << i) & ch & msk) {
+			if (i >= gsi_ctx->max_ev || i >= GSI_EVT_RING_MAX) {
+				GSIERR("invalid event %d\n", i);
+				break;
+			}
+
 			ctx = &gsi_ctx->evtr[i];
 			BUG_ON(ctx->props.intf != GSI_EVT_CHTYPE_GPI_EV);
 			spin_lock_irqsave(&ctx->ring.slock, flags);
@@ -467,9 +472,6 @@
 			spin_unlock_irqrestore(&ctx->ring.slock, flags);
 		}
 	}
-
-	gsi_writel(ch & msk, gsi_ctx->base +
-			GSI_EE_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(ee));
 }
 
 static void gsi_handle_inter_ee_ch_ctrl(int ee)
@@ -479,15 +481,14 @@
 
 	ch = gsi_readl(gsi_ctx->base +
 		GSI_INTER_EE_n_SRC_GSI_CH_IRQ_OFFS(ee));
+	gsi_writel(ch, gsi_ctx->base +
+		GSI_INTER_EE_n_SRC_GSI_CH_IRQ_CLR_OFFS(ee));
 	for (i = 0; i < 32; i++) {
 		if ((1 << i) & ch) {
 			/* not currently expected */
 			GSIERR("ch %u was inter-EE changed\n", i);
 		}
 	}
-
-	gsi_writel(ch, gsi_ctx->base +
-			GSI_INTER_EE_n_SRC_GSI_CH_IRQ_CLR_OFFS(ee));
 }
 
 static void gsi_handle_inter_ee_ev_ctrl(int ee)
@@ -497,15 +498,14 @@
 
 	ch = gsi_readl(gsi_ctx->base +
 		GSI_INTER_EE_n_SRC_EV_CH_IRQ_OFFS(ee));
+	gsi_writel(ch, gsi_ctx->base +
+		GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_OFFS(ee));
 	for (i = 0; i < 32; i++) {
 		if ((1 << i) & ch) {
 			/* not currently expected */
 			GSIERR("evt %u was inter-EE changed\n", i);
 		}
 	}
-
-	gsi_writel(ch, gsi_ctx->base +
-			GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_OFFS(ee));
 }
 
 static void gsi_handle_general(int ee)
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 179a31b..5b2ae14 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
@@ -2325,6 +2325,7 @@
 	unsigned int used = *(unsigned int *)skb->cb;
 	unsigned int used_align = ALIGN(used, 32);
 	unsigned long unused = IPA_GENERIC_RX_BUFF_BASE_SZ - used;
+	u32 skb2_len;
 
 	IPA_DUMP_BUFF(skb->data, 0, skb->len);
 
@@ -2508,8 +2509,9 @@
 				sys->drop_packet = true;
 			}
 
-			skb2 = ipa_skb_copy_for_client(skb,
-				status->pkt_len + IPA_PKT_STATUS_SIZE);
+			skb2_len = status->pkt_len + IPA_PKT_STATUS_SIZE;
+			skb2_len = min(skb2_len, skb->len);
+			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",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 5759847..250a42c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -39,6 +39,7 @@
 #include <soc/qcom/subsystem_restart.h>
 #include <soc/qcom/smem.h>
 #include <soc/qcom/scm.h>
+#include <asm/cacheflush.h>
 
 #ifdef CONFIG_ARM64
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 574e81c..ab386a4 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -1723,6 +1723,7 @@
 	struct ipa3_sys_context *sys;
 	int src_ep_idx;
 	int num_frags, f;
+	struct ipa_gsi_ep_config *gsi_ep;
 
 	if (unlikely(!ipa3_ctx)) {
 		IPAERR("IPA3 driver was not initialized\n");
@@ -1734,23 +1735,6 @@
 		return -EINVAL;
 	}
 
-	num_frags = skb_shinfo(skb)->nr_frags;
-	if (num_frags) {
-		/* 1 desc for tag to resolve status out-of-order issue;
-		 * 1 desc is needed for the linear portion of skb;
-		 * 1 desc may be needed for the PACKET_INIT;
-		 * 1 desc for each frag
-		 */
-		desc = kzalloc(sizeof(*desc) * (num_frags + 3), GFP_ATOMIC);
-		if (!desc) {
-			IPAERR("failed to alloc desc array\n");
-			goto fail_mem;
-		}
-	} else {
-		memset(_desc, 0, 3 * sizeof(struct ipa3_desc));
-		desc = &_desc[0];
-	}
-
 	/*
 	 * USB_CONS: PKT_INIT ep_idx = dst pipe
 	 * Q6_CONS: PKT_INIT ep_idx = sender pipe
@@ -1787,6 +1771,37 @@
 		goto fail_gen;
 	}
 
+	num_frags = skb_shinfo(skb)->nr_frags;
+	/*
+	 * make sure TLV FIFO supports the needed frags.
+	 * 2 descriptors are needed for IP_PACKET_INIT and TAG_STATUS.
+	 * 1 descriptor needed for the linear portion of skb.
+	 */
+	gsi_ep = ipa3_get_gsi_ep_info(src_ep_idx);
+	if (gsi_ep && (num_frags + 3 > gsi_ep->ipa_if_tlv)) {
+		if (skb_linearize(skb)) {
+			IPAERR("Failed to linear skb with %d frags\n",
+				num_frags);
+			goto fail_gen;
+		}
+		num_frags = 0;
+	}
+	if (num_frags) {
+		/* 1 desc for tag to resolve status out-of-order issue;
+		 * 1 desc is needed for the linear portion of skb;
+		 * 1 desc may be needed for the PACKET_INIT;
+		 * 1 desc for each frag
+		 */
+		desc = kzalloc(sizeof(*desc) * (num_frags + 3), GFP_ATOMIC);
+		if (!desc) {
+			IPAERR("failed to alloc desc array\n");
+			goto fail_gen;
+		}
+	} else {
+		memset(_desc, 0, 3 * sizeof(struct ipa3_desc));
+		desc = &_desc[0];
+	}
+
 	if (dst_ep_idx != -1) {
 		/* SW data path */
 		cmd.destination_pipe_index = dst_ep_idx;
@@ -1794,7 +1809,7 @@
 			IPA_IMM_CMD_IP_PACKET_INIT, &cmd, true);
 		if (unlikely(!cmd_pyld)) {
 			IPAERR("failed to construct ip_packet_init imm cmd\n");
-			goto fail_gen;
+			goto fail_mem;
 		}
 
 		/* the tag field will be populated in ipa3_send() function */
@@ -1863,7 +1878,7 @@
 		if (num_frags == 0) {
 			if (ipa3_send(sys, 2, desc, true)) {
 				IPAERR("fail to send skb %p HWP\n", skb);
-				goto fail_gen;
+				goto fail_mem;
 			}
 		} else {
 			for (f = 0; f < num_frags; f++) {
@@ -1880,7 +1895,7 @@
 			if (ipa3_send(sys, num_frags + 2, desc, true)) {
 				IPAERR("fail to send skb %p num_frags %u HWP\n",
 					skb, num_frags);
-				goto fail_gen;
+				goto fail_mem;
 			}
 		}
 		IPA_STATS_INC_CNT(ipa3_ctx->stats.tx_hw_pkts);
@@ -1894,10 +1909,10 @@
 
 fail_send:
 	ipahal_destroy_imm_cmd(cmd_pyld);
-fail_gen:
+fail_mem:
 	if (num_frags)
 		kfree(desc);
-fail_mem:
+fail_gen:
 	return -EFAULT;
 }
 
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c
index f3b4b6c..6607fd4 100644
--- a/drivers/scsi/ufs/ufs-debugfs.c
+++ b/drivers/scsi/ufs/ufs-debugfs.c
@@ -1470,17 +1470,14 @@
 
 void ufsdbg_add_debugfs(struct ufs_hba *hba)
 {
-	char root_name[sizeof("ufshcd00")];
-
 	if (!hba) {
 		dev_err(hba->dev, "%s: NULL hba, exiting", __func__);
 		goto err_no_root;
 	}
 
-	snprintf(root_name, ARRAY_SIZE(root_name), "%s%d", UFSHCD,
-		hba->host->host_no);
+	hba->debugfs_files.debugfs_root = debugfs_create_dir(dev_name(hba->dev),
+							     NULL);
 
-	hba->debugfs_files.debugfs_root = debugfs_create_dir(root_name, NULL);
 	if (IS_ERR(hba->debugfs_files.debugfs_root))
 		/* Don't complain -- debugfs just isn't enabled */
 		goto err_no_root;
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index dfa2ef2..9706273 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1271,7 +1271,7 @@
 {
 	int err = 0;
 
-	if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME)
+	if (hba->dev_info.quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME)
 		err = ufs_qcom_quirk_host_pa_saveconfigtime(hba);
 
 	return err;
@@ -1820,18 +1820,6 @@
 }
 #endif /* CONFIG_SMP */
 
-#define	ANDROID_BOOT_DEV_MAX	30
-static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
-
-#ifndef MODULE
-static int __init get_android_boot_dev(char *str)
-{
-	strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX);
-	return 1;
-}
-__setup("androidboot.bootdevice=", get_android_boot_dev);
-#endif
-
 /*
  * ufs_qcom_parse_lpm - read from DTS whether LPM modes should be disabled.
  */
@@ -1844,6 +1832,24 @@
 		pr_info("%s: will disable all LPM modes\n", __func__);
 }
 
+static void ufs_qcom_save_host_ptr(struct ufs_hba *hba)
+{
+	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+	int id;
+
+	if (!hba->dev->of_node)
+		return;
+
+	/* Extract platform data */
+	id = of_alias_get_id(hba->dev->of_node, "ufshc");
+	if (id <= 0)
+		dev_err(hba->dev, "Failed to get host index %d\n", id);
+	else if (id <= MAX_UFS_QCOM_HOSTS)
+		ufs_qcom_hosts[id - 1] = host;
+	else
+		dev_err(hba->dev, "invalid host index %d\n", id);
+}
+
 /**
  * ufs_qcom_init - bind phy with controller
  * @hba: host controller instance
@@ -1862,9 +1868,6 @@
 	struct ufs_qcom_host *host;
 	struct resource *res;
 
-	if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev)))
-		return -ENODEV;
-
 	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
 	if (!host) {
 		err = -ENOMEM;
@@ -1983,9 +1986,6 @@
 
 	ufs_qcom_setup_clocks(hba, true, false);
 
-	if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
-		ufs_qcom_hosts[hba->dev->id] = host;
-
 	host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN;
 	ufs_qcom_get_default_testbus_cfg(host);
 	err = ufs_qcom_testbus_config(host);
@@ -1995,6 +1995,8 @@
 		err = 0;
 	}
 
+	ufs_qcom_save_host_ptr(hba);
+
 	goto out;
 
 out_disable_phy:
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index ba36d98..42e7aad8 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -18,7 +18,7 @@
 #include <linux/pm_qos.h>
 #include "ufshcd.h"
 
-#define MAX_UFS_QCOM_HOSTS	1
+#define MAX_UFS_QCOM_HOSTS	2
 #define MAX_U32                 (~(u32)0)
 #define MPHY_TX_FSM_STATE       0x41
 #define TX_FSM_HIBERN8          0x1
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 7b37d26..3245fe1 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -513,6 +513,11 @@
 
 	/* Keeps information if any of the LU is power on write protected */
 	bool is_lu_power_on_wp;
+	/* is Unit Attention Condition cleared on UFS Device LUN? */
+	unsigned is_ufs_dev_wlun_ua_cleared:1;
+
+	/* Device deviations from standard UFS device spec. */
+	unsigned int quirks;
 };
 
 #endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index b818028..b22a4c4 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -67,7 +67,7 @@
 		    (STR_PRFX_EQUAL(f->model, model) ||
 		     !strcmp(f->model, UFS_ANY_MODEL)))
 			/* update quirks */
-			hba->dev_quirks |= f->quirk;
+			hba->dev_info.quirks |= f->quirk;
 	}
 out:
 	kfree(model);
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a95cba5..4ad72b6 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -231,8 +231,24 @@
 		_ret;                                                   \
 	})
 
-#define ufshcd_hex_dump(prefix_str, buf, len) \
-print_hex_dump(KERN_ERR, prefix_str, DUMP_PREFIX_OFFSET, 16, 4, buf, len, false)
+static void ufshcd_hex_dump(struct ufs_hba *hba, const char * const str,
+			    const void *buf, size_t len)
+
+{
+	/*
+	 * device name is expected to take up ~20 characters and "str" passed
+	 * to this function is expected to be of ~10 character so we would need
+	 * ~30 characters string to hold the concatenation of these 2 strings.
+	 */
+	#define MAX_PREFIX_STR_SIZE 50
+	char prefix_str[MAX_PREFIX_STR_SIZE] = {0};
+
+	/* concatenate the device name and "str" */
+	snprintf(prefix_str, MAX_PREFIX_STR_SIZE, "%s %s: ",
+		 dev_name(hba->dev), str);
+	print_hex_dump(KERN_ERR, prefix_str, DUMP_PREFIX_OFFSET,
+		       16, 4, buf, len, false);
+}
 
 static u32 ufs_query_desc_max_size[] = {
 	QUERY_DESC_DEVICE_MAX_SIZE,
@@ -531,7 +547,8 @@
 	 * that IORESOURCE_MEM flag is on when xxx_get_resource() is invoked
 	 * during platform/pci probe function.
 	 */
-	ufshcd_hex_dump("host regs: ", hba->mmio_base, UFSHCI_REG_SPACE_SIZE);
+	ufshcd_hex_dump(hba, "host regs", hba->mmio_base,
+			UFSHCI_REG_SPACE_SIZE);
 	dev_err(hba->dev, "hba->ufs_version = 0x%x, hba->capabilities = 0x%x",
 		hba->ufs_version, hba->capabilities);
 	dev_err(hba->dev,
@@ -571,22 +588,22 @@
 		dev_err(hba->dev,
 			"UPIU[%d] - Transfer Request Descriptor phys@0x%llx",
 			tag, (u64)lrbp->utrd_dma_addr);
-		ufshcd_hex_dump("UPIU TRD: ", lrbp->utr_descriptor_ptr,
+		ufshcd_hex_dump(hba, "UPIU TRD", lrbp->utr_descriptor_ptr,
 				sizeof(struct utp_transfer_req_desc));
 		dev_err(hba->dev, "UPIU[%d] - Request UPIU phys@0x%llx", tag,
 			(u64)lrbp->ucd_req_dma_addr);
-		ufshcd_hex_dump("UPIU REQ: ", lrbp->ucd_req_ptr,
+		ufshcd_hex_dump(hba, "UPIU REQ", lrbp->ucd_req_ptr,
 				sizeof(struct utp_upiu_req));
 		dev_err(hba->dev, "UPIU[%d] - Response UPIU phys@0x%llx", tag,
 			(u64)lrbp->ucd_rsp_dma_addr);
-		ufshcd_hex_dump("UPIU RSP: ", lrbp->ucd_rsp_ptr,
+		ufshcd_hex_dump(hba, "UPIU RSP", lrbp->ucd_rsp_ptr,
 				sizeof(struct utp_upiu_rsp));
 		prdt_length =
 			le16_to_cpu(lrbp->utr_descriptor_ptr->prd_table_length);
 		dev_err(hba->dev, "UPIU[%d] - PRDT - %d entries  phys@0x%llx",
 			tag, prdt_length, (u64)lrbp->ucd_prdt_dma_addr);
 		if (pr_prdt)
-			ufshcd_hex_dump("UPIU PRDT: ", lrbp->ucd_prdt_ptr,
+			ufshcd_hex_dump(hba, "UPIU PRDT", lrbp->ucd_prdt_ptr,
 				sizeof(struct ufshcd_sg_entry) * prdt_length);
 	}
 }
@@ -602,15 +619,15 @@
 	for_each_set_bit(tag, &bitmap, hba->nutmrs) {
 		tmrdp = &hba->utmrdl_base_addr[tag];
 		dev_err(hba->dev, "TM[%d] - Task Management Header", tag);
-		ufshcd_hex_dump("TM TRD: ", &tmrdp->header,
+		ufshcd_hex_dump(hba, "TM TRD", &tmrdp->header,
 				sizeof(struct request_desc_header));
 		dev_err(hba->dev, "TM[%d] - Task Management Request UPIU",
 				tag);
-		ufshcd_hex_dump("TM REQ: ", tmrdp->task_req_upiu,
+		ufshcd_hex_dump(hba, "TM REQ", tmrdp->task_req_upiu,
 				sizeof(struct utp_upiu_req));
 		dev_err(hba->dev, "TM[%d] - Task Management Response UPIU",
 				tag);
-		ufshcd_hex_dump("TM RSP: ", tmrdp->task_rsp_upiu,
+		ufshcd_hex_dump(hba, "TM RSP", tmrdp->task_rsp_upiu,
 				sizeof(struct utp_task_req_desc));
 	}
 }
@@ -638,7 +655,7 @@
 	dev_err(hba->dev, "Host capabilities=0x%x, caps=0x%x\n",
 		hba->capabilities, hba->caps);
 	dev_err(hba->dev, "quirks=0x%x, dev. quirks=0x%x\n", hba->quirks,
-		hba->dev_quirks);
+		hba->dev_info.quirks);
 }
 
 /**
@@ -4611,7 +4628,7 @@
 			goto out;
 	}
 
-	if (hba->dev_quirks & UFS_DEVICE_QUIRK_BROKEN_LCC) {
+	if (hba->dev_info.quirks & UFS_DEVICE_QUIRK_BROKEN_LCC) {
 		ret = ufshcd_disable_host_tx_lcc(hba);
 		if (ret)
 			goto out;
@@ -5638,7 +5655,8 @@
 	/* Complete requests that have door-bell cleared by h/w */
 	ufshcd_complete_requests(hba);
 
-	if (hba->dev_quirks & UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
+	if (hba->dev_info.quirks &
+	    UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
 		bool ret;
 
 		spin_unlock_irqrestore(hba->host->host_lock, flags);
@@ -5808,7 +5826,7 @@
 
 	if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) {
 		hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
-	} else if (hba->dev_quirks &
+	} else if (hba->dev_info.quirks &
 		   UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
 		if (reg & UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED)
 			hba->uic_error |=
@@ -6818,11 +6836,11 @@
 		ufshcd_tune_pa_hibern8time(hba);
 	}
 
-	if (hba->dev_quirks & UFS_DEVICE_QUIRK_PA_TACTIVATE)
+	if (hba->dev_info.quirks & UFS_DEVICE_QUIRK_PA_TACTIVATE)
 		/* set 1ms timeout for PA_TACTIVATE */
 		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 10);
 
-	if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE)
+	if (hba->dev_info.quirks & UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE)
 		ufshcd_quirk_tune_host_pa_tactivate(hba);
 
 	ufshcd_vops_apply_dev_quirks(hba);
@@ -6846,7 +6864,7 @@
 
 static void ufshcd_apply_pm_quirks(struct ufs_hba *hba)
 {
-	if (hba->dev_quirks & UFS_DEVICE_QUIRK_NO_LINK_OFF) {
+	if (hba->dev_info.quirks & UFS_DEVICE_QUIRK_NO_LINK_OFF) {
 		if (ufs_get_pm_lvl_to_link_pwr_state(hba->rpm_lvl) ==
 		    UIC_LINK_OFF_STATE) {
 			hba->rpm_lvl =
@@ -6996,14 +7014,13 @@
 
 	ufshcd_apply_pm_quirks(hba);
 	ret = ufshcd_set_vccq_rail_unused(hba,
-		(hba->dev_quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
+		(hba->dev_info.quirks & UFS_DEVICE_NO_VCCQ) ? true : false);
 	if (ret)
 		goto out;
 
 	/* UFS device is also active now */
 	ufshcd_set_ufs_dev_active(hba);
 	ufshcd_force_reset_auto_bkops(hba);
-	hba->wlun_dev_clr_ua = true;
 
 	if (ufshcd_get_max_pwr_mode(hba)) {
 		dev_err(hba->dev,
@@ -7947,12 +7964,12 @@
 	 * handling context.
 	 */
 	hba->host->eh_noresume = 1;
-	if (hba->wlun_dev_clr_ua) {
+	if (!hba->dev_info.is_ufs_dev_wlun_ua_cleared) {
 		ret = ufshcd_send_request_sense(hba, sdp);
 		if (ret)
 			goto out;
 		/* Unit attention condition is cleared now */
-		hba->wlun_dev_clr_ua = false;
+		hba->dev_info.is_ufs_dev_wlun_ua_cleared = 1;
 	}
 
 	cmd[4] = pwr_mode << 4;
@@ -8881,7 +8898,7 @@
 			/* scale down gear */
 			new_pwr_info.gear_tx = scale_down_gear;
 			new_pwr_info.gear_rx = scale_down_gear;
-			if (!(hba->dev_quirks & UFS_DEVICE_NO_FASTAUTO)) {
+			if (!(hba->dev_info.quirks & UFS_DEVICE_NO_FASTAUTO)) {
 				new_pwr_info.pwr_tx = FASTAUTO_MODE;
 				new_pwr_info.pwr_rx = FASTAUTO_MODE;
 			}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 0740072..d17dd49 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -774,9 +774,6 @@
 
 	unsigned int quirks;	/* Deviations from standard UFSHCI spec. */
 
-	/* Device deviations from standard UFS device spec. */
-	unsigned int dev_quirks;
-
 	wait_queue_head_t tm_wq;
 	wait_queue_head_t tm_tag_wq;
 	unsigned long tm_condition;
diff --git a/drivers/soc/qcom/gladiator_hang_detect.c b/drivers/soc/qcom/gladiator_hang_detect.c
index 33b0fa4..7fc2825 100644
--- a/drivers/soc/qcom/gladiator_hang_detect.c
+++ b/drivers/soc/qcom/gladiator_hang_detect.c
@@ -20,26 +20,21 @@
 #include <soc/qcom/scm.h>
 #include <linux/platform_device.h>
 
-#define ACE_OFFSET	0
-#define IO_OFFSET	2
-#define M1_OFFSET	3
-#define M2_OFFSET	4
-#define PCIO_OFFSET	5
 #define ENABLE_MASK_BITS	0x1
 
-#define _VAL(z)			(ENABLE_MASK_BITS << z##_OFFSET)
-#define _VALUE(_val, z)		(_val<<(z##_OFFSET))
+#define _VAL(z)			(ENABLE_MASK_BITS << z)
+#define _VALUE(_val, z)		(_val<<(z))
 #define _WRITE(x, y, z)		(((~(_VAL(z))) & y) | _VALUE(x, z))
 
-#define NR_GLA_REG 6
 #define MODULE_NAME	"gladiator_hang_detect"
 #define MAX_THRES	0xFFFFFFFF
 #define MAX_LEN_SYSFS 12
 
 struct hang_detect {
-	phys_addr_t threshold[NR_GLA_REG];
+	phys_addr_t *threshold;
 	phys_addr_t config;
 	int ACE_enable, IO_enable, M1_enable, M2_enable, PCIO_enable;
+	int ACE_offset, IO_offset, M1_offset, M2_offset, PCIO_offset;
 	uint32_t ACE_threshold, IO_threshold, M1_threshold, M2_threshold,
 			 PCIO_threshold;
 	struct kobject kobj;
@@ -68,116 +63,68 @@
 static void set_threshold(int offset, struct hang_detect *hang_dev,
 		int32_t threshold_val)
 {
-	switch (offset) {
-	case ACE_OFFSET:
+	if (offset == hang_dev->ACE_offset)
 		hang_dev->ACE_threshold = threshold_val;
-		break;
-	case IO_OFFSET:
+	else if (offset == hang_dev->IO_offset)
 		hang_dev->IO_threshold = threshold_val;
-		break;
-	case M1_OFFSET:
+	else if (offset == hang_dev->M1_offset)
 		hang_dev->M1_threshold = threshold_val;
-		break;
-	case M2_OFFSET:
+	else if (offset == hang_dev->M2_offset)
 		hang_dev->M2_threshold = threshold_val;
-		break;
-	case PCIO_OFFSET:
+	else if (offset == hang_dev->PCIO_offset)
 		hang_dev->PCIO_threshold = threshold_val;
-		break;
-	}
 }
 
 static void get_threshold(int offset, struct hang_detect *hang_dev,
 		uint32_t *reg_value)
 {
-	switch (offset) {
-	case ACE_OFFSET:
+	if (offset == hang_dev->ACE_offset)
 		*reg_value = hang_dev->ACE_threshold;
-	break;
-	case IO_OFFSET:
+	else if (offset == hang_dev->IO_offset)
 		*reg_value = hang_dev->IO_threshold;
-		break;
-	case M1_OFFSET:
+	else if (offset == hang_dev->M1_offset)
 		*reg_value = hang_dev->M1_threshold;
-		break;
-	case M2_OFFSET:
+	else if (offset == hang_dev->M2_offset)
 		*reg_value = hang_dev->M2_threshold;
-		break;
-	case PCIO_OFFSET:
+	else if (offset == hang_dev->PCIO_offset)
 		*reg_value = hang_dev->PCIO_threshold;
-		break;
-	}
 }
 
 static void set_enable(int offset, struct hang_detect *hang_dev,
 		int enabled)
 {
-	switch (offset) {
-	case ACE_OFFSET:
+	if (offset == hang_dev->ACE_offset)
 		hang_dev->ACE_enable = enabled;
-		break;
-	case IO_OFFSET:
+	else if (offset == hang_dev->IO_offset)
 		hang_dev->IO_enable = enabled;
-		break;
-	case M1_OFFSET:
+	else if (offset == hang_dev->M1_offset)
 		hang_dev->M1_enable = enabled;
-		break;
-	case M2_OFFSET:
+	else if (offset == hang_dev->M2_offset)
 		hang_dev->M2_enable = enabled;
-		break;
-	case PCIO_OFFSET:
+	else if (offset == hang_dev->PCIO_offset)
 		hang_dev->PCIO_enable = enabled;
-		break;
-	}
 }
 
 static void get_enable(int offset, struct hang_detect *hang_dev,
 		uint32_t *reg_value)
 {
-	switch (offset) {
-	case ACE_OFFSET:
+	if (offset == hang_dev->ACE_offset)
 		*reg_value = hang_dev->ACE_enable;
-		break;
-	case IO_OFFSET:
+	else if (offset == hang_dev->IO_offset)
 		*reg_value = hang_dev->IO_enable;
-		break;
-	case M1_OFFSET:
+	else if (offset == hang_dev->M1_offset)
 		*reg_value = hang_dev->M1_enable;
-		break;
-	case M2_OFFSET:
+	else if (offset == hang_dev->M2_offset)
 		*reg_value = hang_dev->M2_enable;
-		break;
-	case PCIO_OFFSET:
+	else if (offset == hang_dev->PCIO_offset)
 		*reg_value = hang_dev->PCIO_enable;
-		break;
-	}
 }
 
 static void scm_enable_write(int offset, struct hang_detect *hang_dev,
 		int enabled, uint32_t reg_value, int *ret)
 {
-	switch (offset) {
-	case ACE_OFFSET:
-		*ret = scm_io_write(hang_dev->config,
-			_WRITE(enabled, reg_value, ACE));
-		break;
-	case IO_OFFSET:
-		*ret = scm_io_write(hang_dev->config,
-				_WRITE(enabled, reg_value, IO));
-		break;
-	case M1_OFFSET:
-		*ret = scm_io_write(hang_dev->config,
-				_WRITE(enabled, reg_value, M1));
-		break;
-	case M2_OFFSET:
-		*ret = scm_io_write(hang_dev->config,
-				_WRITE(enabled, reg_value, M2));
-		break;
-	case PCIO_OFFSET:
-		*ret = scm_io_write(hang_dev->config,
-				_WRITE(enabled, reg_value, PCIO));
-		break;
-	}
+	*ret = scm_io_write(hang_dev->config,
+			_WRITE(enabled, reg_value, offset));
 }
 
 static int enable_check(const char *buf, int *enabled_pt)
@@ -303,13 +250,18 @@
 static ssize_t show_ace_threshold(struct kobject *kobj, struct attribute *attr,
 				char *buf)
 {
-	return generic_threshold_show(kobj, attr, buf, ACE_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_show(kobj, attr, buf, hang_dev->ACE_offset);
 }
 
 static size_t store_ace_threshold(struct kobject *kobj, struct attribute *attr,
 				const char *buf, size_t count)
 {
-	return generic_threshold_store(kobj, attr, buf, count, ACE_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_store(kobj, attr, buf, count,
+					hang_dev->ACE_offset);
 }
 GLADIATOR_HANG_ATTR(ace_threshold, S_IRUGO|S_IWUSR, show_ace_threshold,
 					store_ace_threshold);
@@ -317,13 +269,18 @@
 static ssize_t show_io_threshold(struct kobject *kobj, struct attribute *attr,
 				char *buf)
 {
-	return generic_threshold_show(kobj, attr, buf, IO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_show(kobj, attr, buf, hang_dev->IO_offset);
 }
 
 static size_t store_io_threshold(struct kobject *kobj, struct attribute *attr,
 				const char *buf, size_t count)
 {
-	return generic_threshold_store(kobj, attr, buf, count, IO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_store(kobj, attr, buf, count,
+					hang_dev->IO_offset);
 }
 GLADIATOR_HANG_ATTR(io_threshold, S_IRUGO|S_IWUSR, show_io_threshold,
 					store_io_threshold);
@@ -331,13 +288,18 @@
 static ssize_t show_m1_threshold(struct kobject *kobj, struct attribute *attr,
 				char *buf)
 {
-	return generic_threshold_show(kobj, attr, buf, M1_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_show(kobj, attr, buf, hang_dev->M1_offset);
 }
 
 static size_t store_m1_threshold(struct kobject *kobj, struct attribute *attr,
 				const char *buf, size_t count)
 {
-	return generic_threshold_store(kobj, attr, buf, count, M1_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_store(kobj, attr, buf, count,
+					hang_dev->M1_offset);
 }
 GLADIATOR_HANG_ATTR(m1_threshold, S_IRUGO|S_IWUSR, show_m1_threshold,
 					store_m1_threshold);
@@ -345,13 +307,18 @@
 static ssize_t show_m2_threshold(struct kobject *kobj, struct attribute *attr,
 				char *buf)
 {
-	return generic_threshold_show(kobj, attr, buf, M2_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_show(kobj, attr, buf, hang_dev->M2_offset);
 }
 
 static size_t store_m2_threshold(struct kobject *kobj, struct attribute *attr,
 				const char *buf, size_t count)
 {
-	return generic_threshold_store(kobj, attr, buf, count, M2_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_store(kobj, attr, buf, count,
+					hang_dev->M2_offset);
 }
 GLADIATOR_HANG_ATTR(m2_threshold, S_IRUGO|S_IWUSR, show_m2_threshold,
 					store_m2_threshold);
@@ -359,13 +326,18 @@
 static ssize_t show_pcio_threshold(struct kobject *kobj, struct attribute *attr,
 				char *buf)
 {
-	return generic_threshold_show(kobj, attr, buf, PCIO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_show(kobj, attr, buf, hang_dev->PCIO_offset);
 }
 
 static size_t store_pcio_threshold(struct kobject *kobj, struct attribute *attr,
 				const char *buf, size_t count)
 {
-	return generic_threshold_store(kobj, attr, buf, count, PCIO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_threshold_store(kobj, attr, buf, count,
+					hang_dev->PCIO_offset);
 }
 GLADIATOR_HANG_ATTR(pcio_threshold, S_IRUGO|S_IWUSR, show_pcio_threshold,
 					store_pcio_threshold);
@@ -373,13 +345,18 @@
 static ssize_t show_ace_enable(struct kobject *kobj,
 			struct attribute *attr, char *buf)
 {
-	return generic_enable_show(kobj, attr, buf, ACE_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_show(kobj, attr, buf, hang_dev->ACE_offset);
 }
 
 static size_t store_ace_enable(struct kobject *kobj,
 			struct attribute *attr, const char *buf, size_t count)
 {
-	return generic_enable_store(kobj, attr, buf, count, ACE_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_store(kobj, attr, buf, count,
+					hang_dev->ACE_offset);
 }
 GLADIATOR_HANG_ATTR(ace_enable, S_IRUGO|S_IWUSR, show_ace_enable,
 		store_ace_enable);
@@ -387,13 +364,18 @@
 static ssize_t show_io_enable(struct kobject *kobj,
 			struct attribute *attr, char *buf)
 {
-	return generic_enable_show(kobj, attr, buf, IO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_show(kobj, attr, buf, hang_dev->IO_offset);
 }
 
 static size_t store_io_enable(struct kobject *kobj,
 			struct attribute *attr, const char *buf, size_t count)
 {
-	return generic_enable_store(kobj, attr, buf, count, IO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_store(kobj, attr, buf, count,
+					hang_dev->IO_offset);
 }
 GLADIATOR_HANG_ATTR(io_enable, S_IRUGO|S_IWUSR,
 		show_io_enable, store_io_enable);
@@ -402,13 +384,18 @@
 static ssize_t show_m1_enable(struct kobject *kobj,
 			struct attribute *attr, char *buf)
 {
-	return generic_enable_show(kobj, attr, buf, M1_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_show(kobj, attr, buf, hang_dev->M1_offset);
 }
 
 static size_t store_m1_enable(struct kobject *kobj,
 			struct attribute *attr, const char *buf, size_t count)
 {
-	return generic_enable_store(kobj, attr, buf, count, M1_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_store(kobj, attr, buf, count,
+					hang_dev->M1_offset);
 }
 GLADIATOR_HANG_ATTR(m1_enable, S_IRUGO|S_IWUSR,
 		show_m1_enable, store_m1_enable);
@@ -416,13 +403,18 @@
 static ssize_t show_m2_enable(struct kobject *kobj,
 			struct attribute *attr, char *buf)
 {
-	return generic_enable_show(kobj, attr, buf, M2_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_show(kobj, attr, buf, hang_dev->M2_offset);
 }
 
 static size_t store_m2_enable(struct kobject *kobj,
 			struct attribute *attr, const char *buf, size_t count)
 {
-	return generic_enable_store(kobj, attr, buf, count, M2_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_store(kobj, attr, buf, count,
+					hang_dev->M2_offset);
 }
 GLADIATOR_HANG_ATTR(m2_enable, S_IRUGO|S_IWUSR,
 		show_m2_enable, store_m2_enable);
@@ -430,13 +422,18 @@
 static ssize_t show_pcio_enable(struct kobject *kobj,
 			struct attribute *attr, char *buf)
 {
-	return generic_enable_show(kobj, attr, buf, PCIO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_show(kobj, attr, buf, hang_dev->PCIO_offset);
 }
 
 static size_t store_pcio_enable(struct kobject *kobj,
 			struct attribute *attr, const char *buf, size_t count)
 {
-	return generic_enable_store(kobj, attr, buf, count, PCIO_OFFSET);
+	struct hang_detect *hang_dev = to_gladiator_hang_dev(kobj);
+
+	return generic_enable_store(kobj, attr, buf, count,
+					hang_dev->PCIO_offset);
 }
 GLADIATOR_HANG_ATTR(pcio_enable, S_IRUGO|S_IWUSR,
 		show_pcio_enable, store_pcio_enable);
@@ -455,12 +452,21 @@
 	NULL
 };
 
+static struct attribute *hang_attrs_v2[] = {
+	&hang_attr_ace_threshold.attr,
+	&hang_attr_io_threshold.attr,
+	&hang_attr_ace_enable.attr,
+	&hang_attr_io_enable.attr,
+	NULL
+};
+
 static struct attribute_group hang_attr_group = {
 	.attrs = hang_attrs,
 };
 
 static const struct of_device_id msm_gladiator_hang_detect_table[] = {
 	{ .compatible = "qcom,gladiator-hang-detect" },
+	{ .compatible = "qcom,gladiator-hang-detect-v2" },
 	{}
 };
 
@@ -469,7 +475,9 @@
 	struct device_node *node = pdev->dev.of_node;
 	struct hang_detect *hang_det = NULL;
 	int i = 0, ret;
-	u32 treg[NR_GLA_REG], creg;
+	u32 NR_GLA_REG;
+	u32 *treg;
+	u32 creg;
 
 	if (!pdev->dev.of_node)
 		return -ENODEV;
@@ -482,6 +490,36 @@
 		return -ENOMEM;
 	}
 
+	if (of_device_is_compatible(node, "qcom,gladiator-hang-detect")) {
+		hang_det->ACE_offset = 0;
+		hang_det->IO_offset = 2;
+		hang_det->M1_offset = 3;
+		hang_det->M2_offset = 4;
+		hang_det->PCIO_offset = 5;
+		NR_GLA_REG = 6;
+	} else if (of_device_is_compatible(node,
+			"qcom,gladiator-hang-detect-v2")) {
+		hang_det->ACE_offset = 0;
+		hang_det->IO_offset = 1;
+		NR_GLA_REG = 2;
+		hang_attr_group.attrs = hang_attrs_v2;
+	}
+
+	hang_det->threshold = devm_kzalloc(&pdev->dev,
+			sizeof(phys_addr_t)*NR_GLA_REG, GFP_KERNEL);
+
+	if (!hang_det->threshold) {
+		pr_err("Can't allocate hang_detect threshold memory\n");
+		return -ENOMEM;
+	}
+
+	treg = devm_kzalloc(&pdev->dev, sizeof(u32)*NR_GLA_REG, GFP_KERNEL);
+
+	if (!treg) {
+		pr_err("Can't allocate threshold register memory\n");
+		return -ENOMEM;
+	}
+
 	ret = of_property_read_u32_array(node, "qcom,threshold-arr",
 			treg, NR_GLA_REG);
 	if (ret) {
@@ -512,6 +550,7 @@
 		pr_err("%s:Error in creation sysfs_create_group\n", __func__);
 		goto out_del_kobj;
 	}
+
 	mutex_init(&hang_det->lock);
 	platform_set_drvdata(pdev, hang_det);
 	return 0;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f4060d9..8745af1d 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -356,6 +356,9 @@
 	evt = dwc->ev_buf;
 	if (evt)
 		dwc3_free_one_event_buffer(dwc, evt);
+
+	/* free GSI related event buffers */
+	dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_FREE);
 }
 
 /**
@@ -377,6 +380,8 @@
 	}
 	dwc->ev_buf = evt;
 
+	/* alloc GSI related event buffers */
+	dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_ALLOC);
 	return 0;
 }
 
@@ -406,6 +411,8 @@
 			DWC3_GEVNTSIZ_SIZE(evt->length));
 	dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
 
+	/* setup GSI related event buffers */
+	dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_SETUP);
 	return 0;
 }
 
@@ -422,6 +429,9 @@
 	dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
 			| DWC3_GEVNTSIZ_SIZE(0));
 	dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
+
+	/* cleanup GSI related event buffers */
+	dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_CLEANUP);
 }
 
 static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
@@ -1208,6 +1218,9 @@
 	dwc->usb3_lpm_capable = device_property_read_bool(dev,
 				"snps,usb3_lpm_capable");
 
+	dwc->needs_fifo_resize = device_property_read_bool(dev,
+				"tx-fifo-resize");
+
 	dwc->disable_scramble_quirk = device_property_read_bool(dev,
 				"snps,disable_scramble_quirk");
 	dwc->u2exit_lfps_quirk = device_property_read_bool(dev,
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 246552e..17a27fe 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -780,6 +780,12 @@
 #define DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT	7
 #define DWC3_CONTROLLER_RESTART_USB_SESSION	8
 
+/* USB GSI event buffer related notification */
+#define DWC3_GSI_EVT_BUF_ALLOC			9
+#define DWC3_GSI_EVT_BUF_SETUP			10
+#define DWC3_GSI_EVT_BUF_CLEANUP		11
+#define DWC3_GSI_EVT_BUF_FREE			12
+
 #define MAX_INTR_STATS				10
 
 /**
@@ -852,7 +858,9 @@
  * 	1	- utmi_l1_suspend_n
  * @is_fpga: true when we are using the FPGA board
  * @pending_events: true when we have pending IRQs to be handled
+ * @needs_fifo_resize: not all users might want fifo resizing, flag it
  * @pullups_connected: true when Run/Stop bit is set
+ * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
  * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
  * @start_config_issued: true when StartConfig command has been issued
  * @three_stage_setup: set if we perform a three phase setup
@@ -1020,7 +1028,9 @@
 	unsigned		is_utmi_l1_suspend:1;
 	unsigned		is_fpga:1;
 	unsigned		pending_events:1;
+	unsigned		needs_fifo_resize:1;
 	unsigned		pullups_connected:1;
+	unsigned		resize_fifos:1;
 	unsigned		setup_packet_pending:1;
 	unsigned		three_stage_setup:1;
 	unsigned		usb3_lpm_capable:1;
@@ -1227,6 +1237,7 @@
 /* prototypes */
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
+int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
 
 /* check whether we are on the DWC_usb31 core */
 static inline bool dwc3_is_usb31(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 57db6d9..1dd3f88 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -117,6 +117,11 @@
 #define	GSI_IF_STS	(QSCRATCH_REG_OFFSET + 0x1A4)
 #define	GSI_WR_CTRL_STATE_MASK	BIT(15)
 
+#define DWC3_GEVNTCOUNT_EVNTINTRPTMASK		(1 << 31)
+#define DWC3_GEVNTADRHI_EVNTADRHI_GSI_EN(n)	(n << 22)
+#define DWC3_GEVNTADRHI_EVNTADRHI_GSI_IDX(n)	(n << 16)
+#define DWC3_GEVENT_TYPE_GSI			0x3
+
 struct dwc3_msm_req_complete {
 	struct list_head list_item;
 	struct usb_request *req;
@@ -213,6 +218,8 @@
 	unsigned int		lpm_to_suspend_delay;
 	bool			init;
 	enum plug_orientation	typec_orientation;
+	u32			num_gsi_event_buffers;
+	struct dwc3_event_buffer **gsi_ev_buff;
 };
 
 #define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -232,7 +239,7 @@
 
 static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc);
 static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA);
-
+static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned int event);
 /**
  *
  * Read register with debug info.
@@ -1608,10 +1615,9 @@
 static void dwc3_msm_notify_event(struct dwc3 *dwc, unsigned int event)
 {
 	struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent);
+	struct dwc3_event_buffer *evt;
 	u32 reg;
-
-	if (dwc->revision < DWC3_REVISION_230A)
-		return;
+	int i;
 
 	switch (event) {
 	case DWC3_CONTROLLER_ERROR_EVENT:
@@ -1696,6 +1702,93 @@
 		dev_dbg(mdwc->dev, "DWC3_CONTROLLER_RESTART_USB_SESSION received\n");
 		schedule_work(&mdwc->restart_usb_work);
 		break;
+	case DWC3_GSI_EVT_BUF_ALLOC:
+		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_ALLOC\n");
+
+		if (!mdwc->num_gsi_event_buffers)
+			break;
+
+		mdwc->gsi_ev_buff = devm_kzalloc(dwc->dev,
+			sizeof(*dwc->ev_buf) * mdwc->num_gsi_event_buffers,
+			GFP_KERNEL);
+		if (!mdwc->gsi_ev_buff) {
+			dev_err(dwc->dev, "can't allocate gsi_ev_buff\n");
+			break;
+		}
+
+		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
+
+			evt = devm_kzalloc(dwc->dev, sizeof(*evt), GFP_KERNEL);
+			if (!evt)
+				break;
+			evt->dwc	= dwc;
+			evt->length	= DWC3_EVENT_BUFFERS_SIZE;
+			evt->buf	= dma_alloc_coherent(dwc->dev,
+						DWC3_EVENT_BUFFERS_SIZE,
+						&evt->dma, GFP_KERNEL);
+			if (!evt->buf) {
+				dev_err(dwc->dev,
+					"can't allocate gsi_evt_buf(%d)\n", i);
+				break;
+			}
+			mdwc->gsi_ev_buff[i] = evt;
+		}
+		break;
+	case DWC3_GSI_EVT_BUF_SETUP:
+		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_SETUP\n");
+		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
+			evt = mdwc->gsi_ev_buff[i];
+			dev_dbg(mdwc->dev, "Evt buf %p dma %08llx length %d\n",
+				evt->buf, (unsigned long long) evt->dma,
+				evt->length);
+			memset(evt->buf, 0, evt->length);
+			evt->lpos = 0;
+			/*
+			 * Primary event buffer is programmed with registers
+			 * DWC3_GEVNT*(0). Hence use DWC3_GEVNT*(i+1) to
+			 * program USB GSI related event buffer with DWC3
+			 * controller.
+			 */
+			dwc3_writel(dwc->regs, DWC3_GEVNTADRLO((i+1)),
+				lower_32_bits(evt->dma));
+			dwc3_writel(dwc->regs, DWC3_GEVNTADRHI((i+1)),
+				DWC3_GEVNTADRHI_EVNTADRHI_GSI_EN(
+				DWC3_GEVENT_TYPE_GSI) |
+				DWC3_GEVNTADRHI_EVNTADRHI_GSI_IDX((i+1)));
+			dwc3_writel(dwc->regs, DWC3_GEVNTSIZ((i+1)),
+				DWC3_GEVNTCOUNT_EVNTINTRPTMASK |
+				((evt->length) & 0xffff));
+			dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT((i+1)), 0);
+		}
+		break;
+	case DWC3_GSI_EVT_BUF_CLEANUP:
+		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_CLEANUP\n");
+		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
+			evt = mdwc->gsi_ev_buff[i];
+			evt->lpos = 0;
+			/*
+			 * Primary event buffer is programmed with registers
+			 * DWC3_GEVNT*(0). Hence use DWC3_GEVNT*(i+1) to
+			 * program USB GSI related event buffer with DWC3
+			 * controller.
+			 */
+			dwc3_writel(dwc->regs, DWC3_GEVNTADRLO((i+1)), 0);
+			dwc3_writel(dwc->regs, DWC3_GEVNTADRHI((i+1)), 0);
+			dwc3_writel(dwc->regs, DWC3_GEVNTSIZ((i+1)),
+					DWC3_GEVNTSIZ_INTMASK |
+					DWC3_GEVNTSIZ_SIZE((i+1)));
+			dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT((i+1)), 0);
+		}
+		break;
+	case DWC3_GSI_EVT_BUF_FREE:
+		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_FREE\n");
+		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
+			evt = mdwc->gsi_ev_buff[i];
+			if (evt)
+				dma_free_coherent(dwc->dev, evt->length,
+							evt->buf, evt->dma);
+		}
+		break;
 	default:
 		dev_dbg(mdwc->dev, "unknown dwc3 event\n");
 		break;
@@ -2878,6 +2971,9 @@
 	if (cpu_to_affin)
 		register_cpu_notifier(&mdwc->dwc3_cpu_notifier);
 
+	ret = of_property_read_u32(node, "qcom,num-gsi-evt-buffs",
+				&mdwc->num_gsi_event_buffers);
+
 	/*
 	 * Clocks and regulators will not be turned on until the first time
 	 * runtime PM resume is called. This is to allow for booting up with
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index fe79d77..99e822d 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -593,6 +593,9 @@
 			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 			reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
 			dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+			dwc->resize_fifos = true;
+			dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
 		}
 		break;
 
@@ -1031,6 +1034,12 @@
 
 static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
+	if (dwc->resize_fifos) {
+		dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
+		dwc3_gadget_resize_tx_fifos(dwc);
+		dwc->resize_fifos = 0;
+	}
+
 	WARN_ON(dwc3_ep0_start_control_status(dep));
 }
 
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index b8fc012..3f5fad2 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -174,6 +174,112 @@
 	dwc3_ep_inc_trb(&dep->trb_dequeue);
 }
 
+/*
+ * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
+ * @dwc: pointer to our context structure
+ *
+ * This function will a best effort FIFO allocation in order
+ * to improve FIFO usage and throughput, while still allowing
+ * us to enable as many endpoints as possible.
+ *
+ * Keep in mind that this operation will be highly dependent
+ * on the configured size for RAM1 - which contains TxFifo -,
+ * the amount of endpoints enabled on coreConsultant tool, and
+ * the width of the Master Bus.
+ *
+ * In the ideal world, we would always be able to satisfy the
+ * following equation:
+ *
+ * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \
+ * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes
+ *
+ * Unfortunately, due to many variables that's not always the case.
+ */
+int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
+{
+	int		last_fifo_depth = 0;
+	int		ram1_depth;
+	int		fifo_size;
+	int		mdwidth;
+	int		num;
+	int		num_eps;
+	int		max_packet = 1024;
+	struct usb_composite_dev *cdev = get_gadget_data(&dwc->gadget);
+
+	if (!(cdev && cdev->config) || !dwc->needs_fifo_resize)
+		return 0;
+
+	num_eps = dwc->num_in_eps;
+	ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+
+	/* MDWIDTH is represented in bits, we need it in bytes */
+	mdwidth >>= 3;
+	last_fifo_depth = (dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)) & 0xFFFF);
+	dev_dbg(dwc->dev, "%s: num eps:%d max_packet:%d last_fifo_depth:%04x\n",
+				__func__, num_eps, max_packet, last_fifo_depth);
+
+	/* Don't resize ep0IN TxFIFO, start with ep1IN only. */
+	for (num = 1; num < num_eps; num++) {
+		/* bit0 indicates direction; 1 means IN ep */
+		struct dwc3_ep	*dep = dwc->eps[(num << 1) | 1];
+		int		mult = 1;
+		int		tmp;
+
+		tmp = max_packet + mdwidth;
+		/*
+		 * Interfaces like MBIM or ECM is having multiple data
+		 * interfaces. SET_CONFIG() happens before set_alt with
+		 * data interface 1 which results into calling this API
+		 * before GSI endpoint enabled. This results no txfifo
+		 * resize with GSI endpoint causing low throughput. Hence
+		 * use mult as 3 for GSI IN endpoint always irrespective
+		 * USB speed.
+		 */
+		if (dep->endpoint.ep_type == EP_TYPE_GSI ||
+				dep->endpoint.endless)
+			mult = 3;
+
+		if (!(dep->flags & DWC3_EP_ENABLED)) {
+			dev_dbg(dwc->dev, "ep%dIn not enabled", num);
+			goto resize_fifo;
+		}
+
+		if (((dep->endpoint.maxburst > 1) &&
+				usb_endpoint_xfer_bulk(dep->endpoint.desc))
+				|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
+			mult = 3;
+
+resize_fifo:
+		tmp *= mult;
+		tmp += mdwidth;
+
+		fifo_size = DIV_ROUND_UP(tmp, mdwidth);
+
+		fifo_size |= (last_fifo_depth << 16);
+
+		dev_dbg(dwc->dev, "%s: Fifo Addr %04x Size %d",
+				dep->name, last_fifo_depth, fifo_size & 0xffff);
+
+		last_fifo_depth += (fifo_size & 0xffff);
+		if (dwc->tx_fifo_size &&
+				(last_fifo_depth >= dwc->tx_fifo_size)) {
+			/*
+			 * Fifo size allocated exceeded available RAM size.
+			 * Hence return error.
+			 */
+			dev_err(dwc->dev, "Fifosize(%d) > available RAM(%d)\n",
+					last_fifo_depth, dwc->tx_fifo_size);
+			return -ENOMEM;
+		}
+
+		dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
+
+	}
+
+	return 0;
+}
+
 void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
 		int status)
 {
@@ -756,6 +862,13 @@
 					dep->name))
 		return 0;
 
+	/* Keep GSI ep names with "-gsi" suffix */
+	if (!strnstr(dep->name, "gsi", 10)) {
+		snprintf(dep->name, sizeof(dep->name), "ep%d%s",
+			dep->number >> 1,
+			(dep->number & 1) ? "in" : "out");
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	ret = __dwc3_gadget_ep_disable(dep);
 	spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2112,11 +2225,22 @@
 
 /* -------------------------------------------------------------------------- */
 
+#define NUM_GSI_OUT_EPS	1
+#define NUM_GSI_IN_EPS	2
+
 static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
 		u8 num, u32 direction)
 {
 	struct dwc3_ep			*dep;
-	u8				i;
+	u8				i, gsi_ep_count, gsi_ep_index = 0;
+
+	gsi_ep_count = NUM_GSI_OUT_EPS + NUM_GSI_IN_EPS;
+	/* OUT GSI EPs based on direction field */
+	if (gsi_ep_count && !direction)
+		gsi_ep_count = NUM_GSI_OUT_EPS;
+	/* IN GSI EPs */
+	else if (gsi_ep_count && direction)
+		gsi_ep_count = NUM_GSI_IN_EPS;
 
 	for (i = 0; i < num; i++) {
 		u8 epnum = (i << 1) | (direction ? 1 : 0);
@@ -2131,9 +2255,21 @@
 		dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
 		dwc->eps[epnum] = dep;
 
-		snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
-				(epnum & 1) ? "in" : "out");
+		/* Reserve EPs at the end for GSI based on gsi_ep_count */
+		if ((gsi_ep_index < gsi_ep_count) &&
+				(i > (num - 1 - gsi_ep_count))) {
+			gsi_ep_index++;
+			/* For GSI EPs, name eps as "gsi-epin" or "gsi-epout" */
+			snprintf(dep->name, sizeof(dep->name), "%s",
+				(epnum & 1) ? "gsi-epin" : "gsi-epout");
+			/* Set ep type as GSI */
+			dep->endpoint.ep_type = EP_TYPE_GSI;
+		} else {
+			snprintf(dep->name, sizeof(dep->name), "ep%d%s",
+				epnum >> 1, (epnum & 1) ? "in" : "out");
+		}
 
+		dep->endpoint.ep_num = epnum >> 1;
 		dep->endpoint.name = dep->name;
 		spin_lock_init(&dep->lock);
 
@@ -2762,6 +2898,9 @@
 	dwc3_stop_active_transfers(dwc);
 	dwc3_clear_stall_all_ep(dwc);
 
+	/* bus reset issued due to missing status stage of a control transfer */
+	dwc->resize_fifos = 0;
+
 	/* Reset device address to zero */
 	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
 	reg &= ~(DWC3_DCFG_DEVADDR_MASK);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 87f2b73..987dcef 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -224,6 +224,9 @@
 config USB_F_DIAG
 	tristate
 
+config USB_F_CDEV
+	tristate
+
 # this first set of drivers all depend on bulk-capable hardware.
 
 config USB_CONFIGFS
@@ -518,6 +521,13 @@
 	  Diag function driver enables support for Qualcomm diagnostics
 	  port over USB.
 
+config USB_CONFIGFS_F_CDEV
+	bool "USB Serial Character function"
+	select USB_F_CDEV
+	depends on USB_CONFIGFS
+	help
+	  Generic USB serial character function driver to support DUN/NMEA.
+
 choice
 	tristate "USB Gadget Drivers"
 	default USB_ETH
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index a354f76..f86e6ff 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -23,6 +23,9 @@
 #include <asm/unaligned.h>
 
 #include "u_os_desc.h"
+#define SSUSB_GADGET_VBUS_DRAW 900 /* in mA */
+#define SSUSB_GADGET_VBUS_DRAW_UNITS 8
+#define HSUSB_GADGET_VBUS_DRAW_UNITS 2
 
 /**
  * struct usb_os_string - represents OS String to be reported by a gadget
@@ -261,6 +264,7 @@
 		goto done;
 
 	function->config = config;
+	function->intf_id = -EINVAL;
 	list_add_tail(&function->list, &config->functions);
 
 	if (function->bind_deactivated) {
@@ -411,6 +415,8 @@
 
 	if (id < MAX_CONFIG_INTERFACES) {
 		config->interface[id] = function;
+		if (function->intf_id < 0)
+			function->intf_id = id;
 		config->next_interface_id = id + 1;
 		return id;
 	}
@@ -418,39 +424,13 @@
 }
 EXPORT_SYMBOL_GPL(usb_interface_id);
 
-/**
- * usb_get_func_interface_id() - Find the interface ID of a function
- * @function: the function for which want to find the interface ID
- * Context: single threaded
- *
- * Returns the interface ID of the function or -ENODEV if this function
- * is not part of this configuration
- */
-int usb_get_func_interface_id(struct usb_function *func)
-{
-	int id;
-	struct usb_configuration *config;
-
-	if (!func)
-		return -EINVAL;
-
-	config = func->config;
-
-	for (id = 0; id < MAX_CONFIG_INTERFACES; id++) {
-		if (config->interface[id] == func)
-			return id;
-	}
-	return -ENODEV;
-}
-
-int usb_func_wakeup(struct usb_function *func)
+static int usb_func_wakeup_int(struct usb_function *func)
 {
 	int ret;
-	unsigned int interface_id;
 	struct usb_gadget *gadget;
 
-	pr_debug("%s function wakeup\n",
-		func->name ? func->name : "");
+	pr_debug("%s - %s function wakeup\n",
+		__func__, func->name ? func->name : "");
 
 	if (!func || !func->config || !func->config->cdev ||
 		!func->config->cdev->gadget)
@@ -466,31 +446,34 @@
 		return -ENOTSUPP;
 	}
 
-	ret = usb_get_func_interface_id(func);
-	if (ret < 0) {
+	ret = usb_gadget_func_wakeup(gadget, func->intf_id);
+
+	return ret;
+}
+
+int usb_func_wakeup(struct usb_function *func)
+{
+	int ret;
+	unsigned long flags;
+
+	pr_debug("%s function wakeup\n",
+		func->name ? func->name : "");
+
+	spin_lock_irqsave(&func->config->cdev->lock, flags);
+	ret = usb_func_wakeup_int(func);
+	if (ret == -EAGAIN) {
+		DBG(func->config->cdev,
+			"Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n",
+			func->name ? func->name : "");
+		ret = 0;
+	} else if (ret < 0 && ret != -ENOTSUPP) {
 		ERROR(func->config->cdev,
-			"Function %s - Unknown interface id. Canceling USB request. ret=%d\n",
+			"Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
 			func->name ? func->name : "", ret);
-		return ret;
 	}
 
-	interface_id = ret;
-	ret = usb_gadget_func_wakeup(gadget, interface_id);
-	if (ret) {
-		if (ret == -EAGAIN) {
-			DBG(func->config->cdev,
-				"Function wakeup for %s could not be complete. Retry is needed.\n",
-				func->name ? func->name : "");
-		} else {
-			ERROR(func->config->cdev,
-				"Failed to wake function %s from suspend state. interface id: %d, ret=%d. Canceling USB request.\n",
-				func->name ? func->name : "",
-				interface_id, ret);
-		}
-		return ret;
-	}
-
-	return 0;
+	spin_unlock_irqrestore(&func->config->cdev->lock, flags);
+	return ret;
 }
 EXPORT_SYMBOL(usb_func_wakeup);
 
@@ -500,28 +483,38 @@
 	int ret;
 	struct usb_gadget *gadget;
 
-	if (!func || !ep || !req) {
-		pr_err("Invalid argument. func=%p, ep=%p, req=%p\n",
-			func, ep, req);
-		return -EINVAL;
+	if (!func || !func->config || !func->config->cdev ||
+			!func->config->cdev->gadget || !ep || !req) {
+		ret = -EINVAL;
+		goto done;
 	}
 
 	pr_debug("Function %s queueing new data into ep %u\n",
 		func->name ? func->name : "", ep->address);
 
 	gadget = func->config->cdev->gadget;
-	if ((gadget->speed == USB_SPEED_SUPER) && func->func_is_suspended) {
-		ret = usb_func_wakeup(func);
-		if (ret) {
-			if (ret != -EAGAIN)
-				pr_err("Failed to send function wake up notification. func name:%s, ep:%u\n",
-					func->name ? func->name : "",
-					ep->address);
-			return ret;
+	if (func->func_is_suspended && func->func_wakeup_allowed) {
+		ret = usb_gadget_func_wakeup(gadget, func->intf_id);
+		if (ret == -EAGAIN) {
+			pr_debug("bus suspended func wakeup for %s delayed until bus resume.\n",
+				func->name ? func->name : "");
+		} else if (ret < 0 && ret != -ENOTSUPP) {
+			pr_err("Failed to wake function %s from suspend state. ret=%d.\n",
+				func->name ? func->name : "", ret);
 		}
+		goto done;
+	}
+
+	if (!func->func_is_suspended)
+		ret = 0;
+
+	if (func->func_is_suspended && !func->func_wakeup_allowed) {
+		ret = -ENOTSUPP;
+		goto done;
 	}
 
 	ret = usb_ep_queue(ep, req, gfp_flags);
+done:
 	return ret;
 }
 EXPORT_SYMBOL(usb_func_ep_queue);
@@ -529,19 +522,15 @@
 static u8 encode_bMaxPower(enum usb_device_speed speed,
 		struct usb_configuration *c)
 {
-	unsigned val;
+	unsigned int val = CONFIG_USB_GADGET_VBUS_DRAW;
 
-	if (c->MaxPower)
-		val = c->MaxPower;
-	else
-		val = CONFIG_USB_GADGET_VBUS_DRAW;
-	if (!val)
-		return 0;
 	switch (speed) {
 	case USB_SPEED_SUPER:
-		return DIV_ROUND_UP(val, 8);
+		/* with super-speed report 900mA */
+		val = SSUSB_GADGET_VBUS_DRAW;
+		return (u8)(val / SSUSB_GADGET_VBUS_DRAW_UNITS);
 	default:
-		return DIV_ROUND_UP(val, 2);
+		return DIV_ROUND_UP(val, HSUSB_GADGET_VBUS_DRAW_UNITS);
 	}
 }
 
@@ -722,7 +711,8 @@
 
 	/*
 	 * A SuperSpeed device shall include the USB2.0 extension descriptor
-	 * and shall support LPM when operating in USB2.0 HS mode.
+	 * and shall support LPM when operating in USB2.0 HS mode, as well as
+	 * a HS device when operating in USB2.1 HS mode.
 	 */
 	usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
 	bos->bNumDeviceCaps++;
@@ -732,33 +722,37 @@
 	usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
 	usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT);
 
-	/*
-	 * The Superspeed USB Capability descriptor shall be implemented by all
-	 * SuperSpeed devices.
-	 */
-	ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
-	bos->bNumDeviceCaps++;
-	le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
-	ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
-	ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
-	ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
-	ss_cap->bmAttributes = 0; /* LTM is not supported yet */
-	ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
-				USB_FULL_SPEED_OPERATION |
-				USB_HIGH_SPEED_OPERATION |
-				USB_5GBPS_OPERATION);
-	ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+	if (gadget_is_superspeed(cdev->gadget)) {
+		/*
+		 * The Superspeed USB Capability descriptor shall be
+		 * implemented by all SuperSpeed devices.
+		 */
+		ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+		bos->bNumDeviceCaps++;
+		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+		ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+		ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+		ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+		ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+		ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+					USB_FULL_SPEED_OPERATION |
+					USB_HIGH_SPEED_OPERATION |
+					USB_5GBPS_OPERATION);
+		ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
 
-	/* Get Controller configuration */
-	if (cdev->gadget->ops->get_config_params)
-		cdev->gadget->ops->get_config_params(&dcd_config_params);
-	else {
-		dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
-		dcd_config_params.bU2DevExitLat =
-			cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+		/* Get Controller configuration */
+		if (cdev->gadget->ops->get_config_params)
+			cdev->gadget->ops->get_config_params
+				(&dcd_config_params);
+		else {
+			dcd_config_params.bU1devExitLat =
+				USB_DEFAULT_U1_DEV_EXIT_LAT;
+			dcd_config_params.bU2DevExitLat =
+				cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+		}
+		ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+		ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
 	}
-	ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
-	ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
 
 	/* The SuperSpeedPlus USB Device Capability descriptor */
 	if (gadget_is_superspeed_plus(cdev->gadget)) {
@@ -841,6 +835,7 @@
 		/* USB 3.0 addition */
 		f->func_is_suspended = false;
 		f->func_wakeup_allowed = false;
+		f->func_wakeup_pending = false;
 
 		bitmap_zero(f->endpoints, 32);
 	}
@@ -888,6 +883,8 @@
 
 	usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
 	cdev->config = c;
+	c->num_ineps_used = 0;
+	c->num_outeps_used = 0;
 
 	/* Initialize all interfaces by setting them to altsetting zero. */
 	for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
@@ -916,6 +913,10 @@
 			addr = ((ep->bEndpointAddress & 0x80) >> 3)
 			     |  (ep->bEndpointAddress & 0x0f);
 			set_bit(addr, f->endpoints);
+			if (usb_endpoint_dir_in(ep))
+				c->num_ineps_used++;
+			else
+				c->num_outeps_used++;
 		}
 
 		result = f->set_alt(f, tmp, 0);
@@ -937,8 +938,12 @@
 		}
 	}
 
-	/* when we return, be sure our power usage is valid */
-	power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
+	/* Allow 900mA to draw with Super-Speed */
+	if (gadget->speed == USB_SPEED_SUPER)
+		power = SSUSB_GADGET_VBUS_DRAW;
+	else
+		power = CONFIG_USB_GADGET_VBUS_DRAW;
+
 done:
 	usb_gadget_vbus_draw(gadget, power);
 	if (result >= 0 && cdev->delayed_status)
@@ -1702,6 +1707,7 @@
 				count_configs(cdev, USB_DT_DEVICE);
 			cdev->desc.bMaxPacketSize0 =
 				cdev->gadget->ep0->maxpacket;
+			cdev->desc.bcdUSB = cpu_to_le16(0x0200);
 			if (gadget_is_superspeed(gadget)) {
 				if (gadget->speed >= USB_SPEED_SUPER) {
 					cdev->desc.bcdUSB = cpu_to_le16(0x0310);
@@ -1709,8 +1715,9 @@
 				} else {
 					cdev->desc.bcdUSB = cpu_to_le16(0x0210);
 				}
-			} else {
-				cdev->desc.bcdUSB = cpu_to_le16(0x0200);
+			} else if (gadget->l1_supported) {
+				cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+				DBG(cdev, "Config HS device with LPM(L1)\n");
 			}
 
 			value = min(w_length, (u16) sizeof cdev->desc);
@@ -1741,7 +1748,8 @@
 				value = min(w_length, (u16) value);
 			break;
 		case USB_DT_BOS:
-			if (gadget_is_superspeed(gadget)) {
+			if (gadget_is_superspeed(gadget) ||
+				gadget->l1_supported) {
 				value = bos_desc(cdev);
 				value = min(w_length, (u16) value);
 			}
@@ -2069,6 +2077,14 @@
 			if (f->setup)
 				value = f->setup(f, ctrl);
 		}
+		if (value == USB_GADGET_DELAYED_STATUS) {
+			DBG(cdev,
+			 "%s: interface %d (%s) requested delayed status\n",
+					__func__, intf, f->name);
+			cdev->delayed_status++;
+			DBG(cdev, "delayed_status count %d\n",
+					cdev->delayed_status);
+		}
 
 		goto done;
 	}
@@ -2359,11 +2375,13 @@
 {
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
 	struct usb_function		*f;
+	unsigned long			flags;
 
 	/* REVISIT:  should we have config level
 	 * suspend/resume callbacks?
 	 */
 	DBG(cdev, "suspend\n");
+	spin_lock_irqsave(&cdev->lock, flags);
 	if (cdev->config) {
 		list_for_each_entry(f, &cdev->config->functions, list) {
 			if (f->suspend)
@@ -2374,6 +2392,7 @@
 		cdev->driver->suspend(cdev);
 
 	cdev->suspended = 1;
+	spin_unlock_irqrestore(&cdev->lock, flags);
 
 	usb_gadget_vbus_draw(gadget, 2);
 }
@@ -2383,6 +2402,8 @@
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
 	struct usb_function		*f;
 	u16				maxpower;
+	int				ret;
+	unsigned long			flags;
 
 	/* REVISIT:  should we have config level
 	 * suspend/resume callbacks?
@@ -2390,8 +2411,25 @@
 	DBG(cdev, "resume\n");
 	if (cdev->driver->resume)
 		cdev->driver->resume(cdev);
+
+	spin_lock_irqsave(&cdev->lock, flags);
 	if (cdev->config) {
 		list_for_each_entry(f, &cdev->config->functions, list) {
+			ret = usb_func_wakeup_int(f);
+			if (ret) {
+				if (ret == -EAGAIN) {
+					ERROR(f->config->cdev,
+						"Function wakeup for %s could not complete due to suspend state.\n",
+						f->name ? f->name : "");
+					break;
+				} else if (ret != -ENOTSUPP) {
+					ERROR(f->config->cdev,
+						"Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n",
+						f->name ? f->name : "",
+						ret);
+				}
+			}
+
 			if (f->resume)
 				f->resume(f);
 		}
@@ -2402,6 +2440,7 @@
 			maxpower : CONFIG_USB_GADGET_VBUS_DRAW);
 	}
 
+	spin_unlock_irqrestore(&cdev->lock, flags);
 	cdev->suspended = 0;
 }
 
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 459953d..1d774e6 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -56,3 +56,5 @@
 obj-$(CONFIG_USB_F_ACC)         += usb_f_accessory.o
 usb_f_diag-y			:= f_diag.o
 obj-$(CONFIG_USB_F_DIAG)	+= usb_f_diag.o
+usb_f_cdev-y			:= f_cdev.o
+obj-$(CONFIG_USB_F_CDEV)	+= usb_f_cdev.o
diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c
new file mode 100644
index 0000000..920c08a
--- /dev/null
+++ b/drivers/usb/gadget/function/f_cdev.c
@@ -0,0 +1,1844 @@
+/*
+ * Copyright (c) 2011, 2013-2016, The Linux Foundation. All rights reserved.
+ * Linux Foundation chooses to take subject only to the GPLv2 license terms,
+ * and distributes only under these terms.
+ *
+ * This code also borrows from drivers/usb/gadget/u_serial.c, which is
+ * Copyright (C) 2000 - 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ *
+ * f_cdev_read() API implementation is using borrowed code from
+ * drivers/usb/gadget/legacy/printer.c, which is
+ * Copyright (C) 2003-2005 David Brownell
+ * Copyright (C) 2006 Craig W. Nadler
+ *
+ * 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.
+ */
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/spinlock.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+#include <linux/module.h>
+#include <asm/ioctls.h>
+#include <asm-generic/termios.h>
+
+#define DEVICE_NAME "at_usb"
+#define MODULE_NAME "msm_usb_bridge"
+#define NUM_INSTANCE 2
+
+#define MAX_CDEV_INST_NAME	15
+#define MAX_CDEV_FUNC_NAME	5
+
+#define BRIDGE_RX_QUEUE_SIZE	8
+#define BRIDGE_RX_BUF_SIZE	2048
+#define BRIDGE_TX_QUEUE_SIZE	8
+#define BRIDGE_TX_BUF_SIZE	2048
+
+#define GS_LOG2_NOTIFY_INTERVAL		5  /* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET		10 /* notification + 2 bytes */
+
+struct cserial {
+	struct usb_function		func;
+	struct usb_ep			*in;
+	struct usb_ep			*out;
+	struct usb_ep			*notify;
+	struct usb_request		*notify_req;
+	struct usb_cdc_line_coding	port_line_coding;
+	u8				pending;
+	u8				q_again;
+	u8				data_id;
+	u16				serial_state;
+	u16				port_handshake_bits;
+	/* control signal callbacks*/
+	unsigned int (*get_dtr)(struct cserial *p);
+	unsigned int (*get_rts)(struct cserial *p);
+
+	/* notification callbacks */
+	void (*connect)(struct cserial *p);
+	void (*disconnect)(struct cserial *p);
+	int (*send_break)(struct cserial *p, int duration);
+	unsigned int (*send_carrier_detect)(struct cserial *p,
+						unsigned int val);
+	unsigned int (*send_ring_indicator)(struct cserial *p,
+						unsigned int val);
+	int (*send_modem_ctrl_bits)(struct cserial *p, int ctrl_bits);
+
+	/* notification changes to modem */
+	void (*notify_modem)(void *port, int ctrl_bits);
+};
+
+struct f_cdev {
+	struct cdev		fcdev_cdev;
+	struct device		*dev;
+	unsigned int		port_num;
+	char			name[sizeof(DEVICE_NAME) + 2];
+	int			minor;
+
+	spinlock_t		port_lock;
+
+	wait_queue_head_t	open_wq;
+	wait_queue_head_t	read_wq;
+
+	struct list_head	read_pool;
+	struct list_head	read_queued;
+	struct list_head	write_pool;
+
+	/* current active USB RX request */
+	struct usb_request	*current_rx_req;
+	/* number of pending bytes */
+	size_t			pending_rx_bytes;
+	/* current USB RX buffer */
+	u8			*current_rx_buf;
+
+	struct cserial		port_usb;
+
+#define ACM_CTRL_DTR		0x01
+#define ACM_CTRL_RTS		0x02
+#define ACM_CTRL_DCD		0x01
+#define ACM_CTRL_DSR		0x02
+#define ACM_CTRL_BRK		0x04
+#define ACM_CTRL_RI		0x08
+
+	unsigned int		cbits_to_modem;
+	bool			cbits_updated;
+
+	struct workqueue_struct *fcdev_wq;
+	bool			is_connected;
+	bool			port_open;
+
+	unsigned long           nbytes_from_host;
+	unsigned long		nbytes_to_host;
+	unsigned long           nbytes_to_port_bridge;
+	unsigned long		nbytes_from_port_bridge;
+};
+
+struct f_cdev_opts {
+	struct usb_function_instance func_inst;
+	struct f_cdev *port;
+	char *func_name;
+	u8 port_num;
+};
+
+static int major, minors;
+struct class *fcdev_classp;
+static DEFINE_IDA(chardev_ida);
+static DEFINE_MUTEX(chardev_ida_lock);
+
+static int usb_cser_alloc_chardev_region(void);
+static void usb_cser_chardev_deinit(void);
+static void usb_cser_read_complete(struct usb_ep *ep, struct usb_request *req);
+static int usb_cser_connect(struct f_cdev *port);
+static void usb_cser_disconnect(struct f_cdev *port);
+static struct f_cdev *f_cdev_alloc(char *func_name, int portno);
+static void usb_cser_free_req(struct usb_ep *ep, struct usb_request *req);
+
+static struct usb_interface_descriptor cser_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc cser_header_desc  = {
+	.bLength =		sizeof(cser_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+cser_call_mgmt_descriptor  = {
+	.bLength =		sizeof(cser_call_mgmt_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
+	.bmCapabilities =	0,
+	/* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor cser_descriptor  = {
+	.bLength =		sizeof(cser_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
+	.bmCapabilities =	USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc cser_union_desc  = {
+	.bLength =		sizeof(cser_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+/* full speed support: */
+static struct usb_endpoint_descriptor cser_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor cser_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor cser_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *cser_fs_function[] = {
+	(struct usb_descriptor_header *) &cser_interface_desc,
+	(struct usb_descriptor_header *) &cser_header_desc,
+	(struct usb_descriptor_header *) &cser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &cser_descriptor,
+	(struct usb_descriptor_header *) &cser_union_desc,
+	(struct usb_descriptor_header *) &cser_fs_notify_desc,
+	(struct usb_descriptor_header *) &cser_fs_in_desc,
+	(struct usb_descriptor_header *) &cser_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+static struct usb_endpoint_descriptor cser_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_endpoint_descriptor cser_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor cser_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *cser_hs_function[] = {
+	(struct usb_descriptor_header *) &cser_interface_desc,
+	(struct usb_descriptor_header *) &cser_header_desc,
+	(struct usb_descriptor_header *) &cser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &cser_descriptor,
+	(struct usb_descriptor_header *) &cser_union_desc,
+	(struct usb_descriptor_header *) &cser_hs_notify_desc,
+	(struct usb_descriptor_header *) &cser_hs_in_desc,
+	(struct usb_descriptor_header *) &cser_hs_out_desc,
+	NULL,
+};
+
+static struct usb_endpoint_descriptor cser_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor cser_ss_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor cser_ss_bulk_comp_desc = {
+	.bLength =              sizeof(cser_ss_bulk_comp_desc),
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor cser_ss_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+	.bInterval =		GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_ss_ep_comp_descriptor cser_ss_notify_comp_desc = {
+	.bLength =		sizeof(cser_ss_notify_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+	.wBytesPerInterval =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
+};
+
+static struct usb_descriptor_header *cser_ss_function[] = {
+	(struct usb_descriptor_header *) &cser_interface_desc,
+	(struct usb_descriptor_header *) &cser_header_desc,
+	(struct usb_descriptor_header *) &cser_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &cser_descriptor,
+	(struct usb_descriptor_header *) &cser_union_desc,
+	(struct usb_descriptor_header *) &cser_ss_notify_desc,
+	(struct usb_descriptor_header *) &cser_ss_notify_comp_desc,
+	(struct usb_descriptor_header *) &cser_ss_in_desc,
+	(struct usb_descriptor_header *) &cser_ss_bulk_comp_desc,
+	(struct usb_descriptor_header *) &cser_ss_out_desc,
+	(struct usb_descriptor_header *) &cser_ss_bulk_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+static struct usb_string cser_string_defs[] = {
+	[0].s = "CDEV Serial",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings cser_string_table = {
+	.language =	0x0409,	/* en-us */
+	.strings =	cser_string_defs,
+};
+
+static struct usb_gadget_strings *usb_cser_strings[] = {
+	&cser_string_table,
+	NULL,
+};
+
+static inline struct f_cdev *func_to_port(struct usb_function *f)
+{
+	return container_of(f, struct f_cdev, port_usb.func);
+}
+
+static inline struct f_cdev *cser_to_port(struct cserial *cser)
+{
+	return container_of(cser, struct f_cdev, port_usb);
+}
+
+static unsigned int convert_acm_sigs_to_uart(unsigned int acm_sig)
+{
+	unsigned int uart_sig = 0;
+
+	acm_sig &= (ACM_CTRL_DTR | ACM_CTRL_RTS);
+	if (acm_sig & ACM_CTRL_DTR)
+		uart_sig |= TIOCM_DTR;
+
+	if (acm_sig & ACM_CTRL_RTS)
+		uart_sig |= TIOCM_RTS;
+
+	return uart_sig;
+}
+
+static void port_complete_set_line_coding(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct f_cdev            *port = ep->driver_data;
+	struct usb_composite_dev *cdev = port->port_usb.func.config->cdev;
+
+	if (req->status != 0) {
+		dev_dbg(&cdev->gadget->dev, "port(%s) completion, err %d\n",
+				port->name, req->status);
+		return;
+	}
+
+	/* normal completion */
+	if (req->actual != sizeof(port->port_usb.port_line_coding)) {
+		dev_dbg(&cdev->gadget->dev, "port(%s) short resp, len %d\n",
+			port->name, req->actual);
+		usb_ep_set_halt(ep);
+	} else {
+		struct usb_cdc_line_coding	*value = req->buf;
+
+		port->port_usb.port_line_coding = *value;
+	}
+}
+
+static void usb_cser_free_func(struct usb_function *f)
+{
+	/* Do nothing as cser_alloc() doesn't alloc anything. */
+}
+
+static int
+usb_cser_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_cdev            *port = func_to_port(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	 *req = cdev->req;
+	int			 value = -EOPNOTSUPP;
+	u16			 w_index = le16_to_cpu(ctrl->wIndex);
+	u16			 w_value = le16_to_cpu(ctrl->wValue);
+	u16			 w_length = le16_to_cpu(ctrl->wLength);
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	/* SET_LINE_CODING ... just read and save what the host sends */
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_LINE_CODING:
+		if (w_length != sizeof(struct usb_cdc_line_coding))
+			goto invalid;
+
+		value = w_length;
+		cdev->gadget->ep0->driver_data = port;
+		req->complete = port_complete_set_line_coding;
+		break;
+
+	/* GET_LINE_CODING ... return what host sent, or initial value */
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_GET_LINE_CODING:
+		value = min_t(unsigned int, w_length,
+				sizeof(struct usb_cdc_line_coding));
+		memcpy(req->buf, &port->port_usb.port_line_coding, value);
+		break;
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+
+		value = 0;
+		port->port_usb.port_handshake_bits = w_value;
+		pr_debug("USB_CDC_REQ_SET_CONTROL_LINE_STATE: DTR:%d RST:%d\n",
+			w_value & ACM_CTRL_DTR ? 1 : 0,
+			w_value & ACM_CTRL_RTS ? 1 : 0);
+		if (port->port_usb.notify_modem)
+			port->port_usb.notify_modem(port, w_value);
+
+		break;
+
+	default:
+invalid:
+		dev_dbg(&cdev->gadget->dev,
+			"invalid control req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		dev_dbg(&cdev->gadget->dev,
+			"port(%s) req%02x.%02x v%04x i%04x l%d\n",
+			port->name, ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = 0;
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			pr_err("port response on (%s), err %d\n",
+					port->name, value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static int usb_cser_set_alt(struct usb_function *f, unsigned int intf,
+						unsigned int alt)
+{
+	struct f_cdev *port = func_to_port(f);
+	struct usb_composite_dev *cdev	= f->config->cdev;
+	int rc = 0;
+
+	if (port->port_usb.notify->driver_data) {
+		dev_dbg(&cdev->gadget->dev,
+			"reset port(%s)\n", port->name);
+		usb_ep_disable(port->port_usb.notify);
+	}
+
+	if (!port->port_usb.notify->desc) {
+		if (config_ep_by_speed(cdev->gadget, f,
+				port->port_usb.notify)) {
+			port->port_usb.notify->desc = NULL;
+			return -EINVAL;
+		}
+	}
+
+	rc = usb_ep_enable(port->port_usb.notify);
+	if (rc) {
+		dev_err(&cdev->gadget->dev, "can't enable %s, result %d\n",
+				port->port_usb.notify->name, rc);
+		return rc;
+	}
+	port->port_usb.notify->driver_data = port;
+
+	if (port->port_usb.in->driver_data) {
+		dev_dbg(&cdev->gadget->dev,
+			"reset port(%s)\n", port->name);
+		usb_cser_disconnect(port);
+	}
+	if (!port->port_usb.in->desc || !port->port_usb.out->desc) {
+		dev_dbg(&cdev->gadget->dev,
+			"activate port(%s)\n", port->name);
+		if (config_ep_by_speed(cdev->gadget, f, port->port_usb.in) ||
+			config_ep_by_speed(cdev->gadget, f,
+					port->port_usb.out)) {
+			port->port_usb.in->desc = NULL;
+			port->port_usb.out->desc = NULL;
+			return -EINVAL;
+		}
+	}
+
+	usb_cser_connect(port);
+	return rc;
+}
+
+static void usb_cser_disable(struct usb_function *f)
+{
+	struct f_cdev	*port = func_to_port(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	dev_dbg(&cdev->gadget->dev,
+		"port(%s) deactivated\n", port->name);
+
+	usb_cser_disconnect(port);
+	usb_ep_disable(port->port_usb.notify);
+	port->port_usb.notify->driver_data = NULL;
+}
+
+static int usb_cser_notify(struct f_cdev *port, u8 type, u16 value,
+		void *data, unsigned int length)
+{
+	struct usb_ep			*ep = port->port_usb.notify;
+	struct usb_request		*req;
+	struct usb_cdc_notification	*notify;
+	const unsigned int		len = sizeof(*notify) + length;
+	void				*buf;
+	int				status;
+	unsigned long			flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->is_connected) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_debug("%s: port disconnected\n", __func__);
+		return -ENODEV;
+	}
+
+	req = port->port_usb.notify_req;
+
+	req->length = len;
+	notify = req->buf;
+	buf = notify + 1;
+
+	notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+			| USB_RECIP_INTERFACE;
+	notify->bNotificationType = type;
+	notify->wValue = cpu_to_le16(value);
+	notify->wIndex = cpu_to_le16(port->port_usb.data_id);
+	notify->wLength = cpu_to_le16(length);
+	/* 2 byte data copy */
+	memcpy(buf, data, length);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status < 0) {
+		pr_err("port %s can't notify serial state, %d\n",
+				port->name, status);
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->port_usb.pending = false;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+	return status;
+}
+
+static int port_notify_serial_state(struct cserial *cser)
+{
+	struct f_cdev *port = cser_to_port(cser);
+	int status;
+	unsigned long flags;
+	struct usb_composite_dev *cdev = port->port_usb.func.config->cdev;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_usb.pending) {
+		port->port_usb.pending = true;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		dev_dbg(&cdev->gadget->dev, "port %d serial state %04x\n",
+				port->port_num, port->port_usb.serial_state);
+		status = usb_cser_notify(port, USB_CDC_NOTIFY_SERIAL_STATE,
+				0, &port->port_usb.serial_state,
+				sizeof(port->port_usb.serial_state));
+		spin_lock_irqsave(&port->port_lock, flags);
+	} else {
+		port->port_usb.q_again = true;
+		status = 0;
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return status;
+}
+
+static void usb_cser_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_cdev *port = req->context;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_usb.pending = false;
+	if (req->status != -ESHUTDOWN && port->port_usb.q_again) {
+		port->port_usb.q_again = false;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		port_notify_serial_state(&port->port_usb);
+		spin_lock_irqsave(&port->port_lock, flags);
+	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+static void dun_cser_connect(struct cserial *cser)
+{
+	cser->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+	port_notify_serial_state(cser);
+}
+
+unsigned int dun_cser_get_dtr(struct cserial *cser)
+{
+	if (cser->port_handshake_bits & ACM_CTRL_DTR)
+		return 1;
+	else
+		return 0;
+}
+
+unsigned int dun_cser_get_rts(struct cserial *cser)
+{
+	if (cser->port_handshake_bits & ACM_CTRL_RTS)
+		return 1;
+	else
+		return 0;
+}
+
+unsigned int dun_cser_send_carrier_detect(struct cserial *cser,
+				unsigned int yes)
+{
+	u16 state;
+
+	state = cser->serial_state;
+	state &= ~ACM_CTRL_DCD;
+	if (yes)
+		state |= ACM_CTRL_DCD;
+
+	cser->serial_state = state;
+	return port_notify_serial_state(cser);
+}
+
+unsigned int dun_cser_send_ring_indicator(struct cserial *cser,
+				unsigned int yes)
+{
+	u16 state;
+
+	state = cser->serial_state;
+	state &= ~ACM_CTRL_RI;
+	if (yes)
+		state |= ACM_CTRL_RI;
+
+	cser->serial_state = state;
+	return port_notify_serial_state(cser);
+}
+
+static void dun_cser_disconnect(struct cserial *cser)
+{
+	cser->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+	port_notify_serial_state(cser);
+}
+
+static int dun_cser_send_break(struct cserial *cser, int duration)
+{
+	u16 state;
+
+	state = cser->serial_state;
+	state &= ~ACM_CTRL_BRK;
+	if (duration)
+		state |= ACM_CTRL_BRK;
+
+	cser->serial_state = state;
+	return port_notify_serial_state(cser);
+}
+
+static int dun_cser_send_ctrl_bits(struct cserial *cser, int ctrl_bits)
+{
+	cser->serial_state = ctrl_bits;
+	return port_notify_serial_state(cser);
+}
+
+static void usb_cser_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+		req = NULL;
+	}
+}
+
+static void usb_cser_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+	struct usb_request	*req;
+
+	while (!list_empty(head)) {
+		req = list_entry(head->next, struct usb_request, list);
+		list_del_init(&req->list);
+		usb_cser_free_req(ep, req);
+	}
+}
+
+static struct usb_request *
+usb_cser_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, flags);
+	if (!req) {
+		pr_err("usb alloc request failed\n");
+		return 0;
+	}
+
+	req->length = len;
+	req->buf = kmalloc(len, flags);
+	if (!req->buf) {
+		pr_err("request buf allocation failed\n");
+		usb_ep_free_request(ep, req);
+		return 0;
+	}
+
+	return req;
+}
+
+static int usb_cser_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct f_cdev *port = func_to_port(f);
+	int status;
+	struct usb_ep *ep;
+
+	if (cser_string_defs[0].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			return status;
+		cser_string_defs[0].id = status;
+	}
+
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+	port->port_usb.data_id = status;
+	cser_interface_desc.bInterfaceNumber = status;
+
+	status = -ENODEV;
+	ep = usb_ep_autoconfig(cdev->gadget, &cser_fs_in_desc);
+	if (!ep)
+		goto fail;
+	port->port_usb.in = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &cser_fs_out_desc);
+	if (!ep)
+		goto fail;
+	port->port_usb.out = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &cser_fs_notify_desc);
+	if (!ep)
+		goto fail;
+	port->port_usb.notify = ep;
+	ep->driver_data = cdev;
+	/* allocate notification */
+	port->port_usb.notify_req = usb_cser_alloc_req(ep,
+			sizeof(struct usb_cdc_notification) + 2, GFP_KERNEL);
+	if (!port->port_usb.notify_req)
+		goto fail;
+
+	port->port_usb.notify_req->complete = usb_cser_notify_complete;
+	port->port_usb.notify_req->context = port;
+
+	cser_hs_in_desc.bEndpointAddress = cser_fs_in_desc.bEndpointAddress;
+	cser_hs_out_desc.bEndpointAddress = cser_fs_out_desc.bEndpointAddress;
+
+	cser_ss_in_desc.bEndpointAddress = cser_fs_in_desc.bEndpointAddress;
+	cser_ss_out_desc.bEndpointAddress = cser_fs_out_desc.bEndpointAddress;
+
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		cser_hs_notify_desc.bEndpointAddress =
+				cser_fs_notify_desc.bEndpointAddress;
+	}
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		cser_ss_notify_desc.bEndpointAddress =
+				cser_fs_notify_desc.bEndpointAddress;
+	}
+
+	status = usb_assign_descriptors(f, cser_fs_function, cser_hs_function,
+			cser_ss_function, NULL);
+	if (status)
+		goto fail;
+
+	dev_dbg(&cdev->gadget->dev, "usb serial port(%d): %s speed IN/%s OUT/%s\n",
+		port->port_num,
+		gadget_is_superspeed(c->cdev->gadget) ? "super" :
+		gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+		port->port_usb.in->name, port->port_usb.out->name);
+	return 0;
+
+fail:
+	if (port->port_usb.notify_req)
+		usb_cser_free_req(port->port_usb.notify,
+				port->port_usb.notify_req);
+
+	if (port->port_usb.notify)
+		port->port_usb.notify->driver_data = NULL;
+	if (port->port_usb.out)
+		port->port_usb.out->driver_data = NULL;
+	if (port->port_usb.in)
+		port->port_usb.in->driver_data = NULL;
+
+	pr_err("%s: can't bind, err %d\n", f->name, status);
+	return status;
+}
+
+static void cser_free_inst(struct usb_function_instance *fi)
+{
+	struct f_cdev_opts *opts;
+
+	opts = container_of(fi, struct f_cdev_opts, func_inst);
+
+	device_destroy(fcdev_classp, MKDEV(major, opts->port->minor));
+	cdev_del(&opts->port->fcdev_cdev);
+	usb_cser_chardev_deinit();
+	kfree(opts->func_name);
+	kfree(opts->port);
+	kfree(opts);
+}
+
+static void usb_cser_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_cdev *port = func_to_port(f);
+
+	usb_free_all_descriptors(f);
+	usb_cser_free_req(port->port_usb.notify, port->port_usb.notify_req);
+}
+
+static int usb_cser_alloc_requests(struct usb_ep *ep, struct list_head *head,
+		int num, int size,
+		void (*cb)(struct usb_ep *ep, struct usb_request *))
+{
+	int i;
+	struct usb_request *req;
+
+	pr_debug("ep:%p head:%p num:%d size:%d cb:%p",
+				ep, head, num, size, cb);
+
+	for (i = 0; i < num; i++) {
+		req = usb_cser_alloc_req(ep, size, GFP_ATOMIC);
+		if (!req) {
+			pr_debug("req allocated:%d\n", i);
+			return list_empty(head) ? -ENOMEM : 0;
+		}
+		req->complete = cb;
+		list_add_tail(&req->list, head);
+	}
+
+	return 0;
+}
+
+static void usb_cser_start_rx(struct f_cdev *port)
+{
+	struct list_head	*pool;
+	struct usb_ep		*ep;
+	unsigned long		flags;
+	int ret;
+
+	pr_debug("start RX(USB OUT)\n");
+	if (!port) {
+		pr_err("port is null\n");
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!(port->is_connected && port->port_open)) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_debug("can't start rx.\n");
+		return;
+	}
+
+	pool = &port->read_pool;
+	ep = port->port_usb.out;
+
+	while (!list_empty(pool)) {
+		struct usb_request	*req;
+
+		req = list_entry(pool->next, struct usb_request, list);
+		list_del_init(&req->list);
+		req->length = BRIDGE_RX_BUF_SIZE;
+		req->complete = usb_cser_read_complete;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		ret = usb_ep_queue(ep, req, GFP_KERNEL);
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (ret) {
+			pr_err("port(%d):%p usb ep(%s) queue failed\n",
+					port->port_num, port, ep->name);
+			list_add(&req->list, pool);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void usb_cser_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_cdev *port = ep->driver_data;
+	unsigned long flags;
+
+	pr_debug("ep:(%p)(%s) port:%p req_status:%d req->actual:%u\n",
+			ep, ep->name, port, req->status, req->actual);
+	if (!port) {
+		pr_err("port is null\n");
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->port_open || req->status || !req->actual) {
+		list_add_tail(&req->list, &port->read_pool);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return;
+	}
+
+	port->nbytes_from_host += req->actual;
+	list_add_tail(&req->list, &port->read_queued);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	wake_up(&port->read_wq);
+}
+
+static void usb_cser_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	unsigned long flags;
+	struct f_cdev *port = ep->driver_data;
+
+	pr_debug("ep:(%p)(%s) port:%p req_stats:%d\n",
+			ep, ep->name, port, req->status);
+
+	if (!port) {
+		pr_err("port is null\n");
+		return;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->nbytes_to_host += req->actual;
+	list_add_tail(&req->list, &port->write_pool);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	switch (req->status) {
+	default:
+		pr_debug("unexpected %s status %d\n", ep->name, req->status);
+		/* FALL THROUGH */
+	case 0:
+		/* normal completion */
+		break;
+
+	case -ESHUTDOWN:
+		/* disconnect */
+		pr_debug("%s shutdown\n", ep->name);
+		break;
+	}
+}
+
+static void usb_cser_start_io(struct f_cdev *port)
+{
+	int ret = -ENODEV;
+	unsigned long	flags;
+
+	pr_debug("port: %p\n", port);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (!port->is_connected)
+		goto start_io_out;
+
+	port->current_rx_req = NULL;
+	port->pending_rx_bytes = 0;
+	port->current_rx_buf = NULL;
+
+	ret = usb_cser_alloc_requests(port->port_usb.out,
+				&port->read_pool,
+				BRIDGE_RX_QUEUE_SIZE, BRIDGE_RX_BUF_SIZE,
+				usb_cser_read_complete);
+	if (ret) {
+		pr_err("unable to allocate out requests\n");
+		goto start_io_out;
+	}
+
+	ret = usb_cser_alloc_requests(port->port_usb.in,
+				&port->write_pool,
+				BRIDGE_TX_QUEUE_SIZE, BRIDGE_TX_BUF_SIZE,
+				usb_cser_write_complete);
+	if (ret) {
+		usb_cser_free_requests(port->port_usb.out, &port->read_pool);
+		pr_err("unable to allocate IN requests\n");
+		goto start_io_out;
+	}
+
+start_io_out:
+	spin_unlock_irqrestore(&port->port_lock, flags);
+	if (ret)
+		return;
+
+	usb_cser_start_rx(port);
+}
+
+static void usb_cser_stop_io(struct f_cdev *port)
+{
+	struct usb_ep	*in;
+	struct usb_ep	*out;
+	unsigned long	flags;
+
+	pr_debug("port:%p\n", port);
+
+	in = port->port_usb.in;
+	out = port->port_usb.out;
+
+	/* disable endpoints, aborting down any active I/O */
+	usb_ep_disable(out);
+	out->driver_data = NULL;
+	usb_ep_disable(in);
+	in->driver_data = NULL;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	if (port->current_rx_req != NULL) {
+		kfree(port->current_rx_req->buf);
+		usb_ep_free_request(out, port->current_rx_req);
+	}
+
+	port->pending_rx_bytes = 0;
+	port->current_rx_buf = NULL;
+	usb_cser_free_requests(out, &port->read_queued);
+	usb_cser_free_requests(out, &port->read_pool);
+	usb_cser_free_requests(in, &port->write_pool);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+int f_cdev_open(struct inode *inode, struct file *file)
+{
+	int ret;
+	unsigned long flags;
+	struct f_cdev *port;
+
+	port = container_of(inode->i_cdev, struct f_cdev, fcdev_cdev);
+	if (!port) {
+		pr_err("Port is NULL.\n");
+		return -EINVAL;
+	}
+
+	if (port && port->port_open) {
+		pr_err("port is already opened.\n");
+		return -EBUSY;
+	}
+
+	file->private_data = port;
+	pr_debug("opening port(%s)(%p)\n", port->name, port);
+	ret = wait_event_interruptible(port->open_wq,
+					port->is_connected);
+	if (ret) {
+		pr_debug("open interrupted.\n");
+		return ret;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_open = true;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+	usb_cser_start_rx(port);
+
+	pr_debug("port(%s)(%p) open is success\n", port->name, port);
+
+	return 0;
+}
+
+int f_cdev_release(struct inode *inode, struct file *file)
+{
+	unsigned long flags;
+	struct f_cdev *port;
+
+	port = file->private_data;
+	if (!port) {
+		pr_err("port is NULL.\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->port_open = false;
+	port->cbits_updated = false;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+	pr_debug("port(%s)(%p) is closed.\n", port->name, port);
+
+	return 0;
+}
+
+ssize_t f_cdev_read(struct file *file,
+		       char __user *buf,
+		       size_t count,
+		       loff_t *ppos)
+{
+	unsigned long flags;
+	struct f_cdev *port;
+	struct usb_request *req;
+	struct list_head *pool;
+	struct usb_request *current_rx_req;
+	size_t pending_rx_bytes, bytes_copied = 0, size;
+	u8 *current_rx_buf;
+
+	port = file->private_data;
+	if (!port) {
+		pr_err("port is NULL.\n");
+		return -EINVAL;
+	}
+
+	pr_debug("read on port(%s)(%p) count:%zu\n", port->name, port, count);
+	spin_lock_irqsave(&port->port_lock, flags);
+	current_rx_req = port->current_rx_req;
+	pending_rx_bytes = port->pending_rx_bytes;
+	current_rx_buf = port->current_rx_buf;
+	port->current_rx_req = NULL;
+	port->current_rx_buf = NULL;
+	port->pending_rx_bytes = 0;
+	bytes_copied = 0;
+
+	if (list_empty(&port->read_queued) && !pending_rx_bytes) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_debug("%s(): read_queued list is empty.\n", __func__);
+		goto start_rx;
+	}
+
+	/*
+	 * Consider below cases:
+	 * 1. If available read buffer size (i.e. count value) is greater than
+	 * available data as part of one USB OUT request buffer, then consider
+	 * copying multiple USB OUT request buffers until read buffer is filled.
+	 * 2. If available read buffer size (i.e. count value) is smaller than
+	 * available data as part of one USB OUT request buffer, then copy this
+	 * buffer data across multiple read() call until whole USB OUT request
+	 * buffer is copied.
+	 */
+	while ((pending_rx_bytes || !list_empty(&port->read_queued)) && count) {
+		if (pending_rx_bytes == 0) {
+			pool = &port->read_queued;
+			req = list_first_entry(pool, struct usb_request, list);
+			list_del_init(&req->list);
+			current_rx_req = req;
+			pending_rx_bytes = req->actual;
+			current_rx_buf = req->buf;
+		}
+
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		size = count;
+		if (size > pending_rx_bytes)
+			size = pending_rx_bytes;
+
+		pr_debug("pending_rx_bytes:%zu count:%zu size:%zu\n",
+					pending_rx_bytes, count, size);
+		size -= copy_to_user(buf, current_rx_buf, size);
+		port->nbytes_to_port_bridge += size;
+		bytes_copied += size;
+		count -= size;
+		buf += size;
+
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (!port->is_connected) {
+			list_add_tail(&current_rx_req->list, &port->read_pool);
+			spin_unlock_irqrestore(&port->port_lock, flags);
+			return -EAGAIN;
+		}
+
+		/*
+		 * partial data available, then update pending_rx_bytes,
+		 * otherwise add USB request back to read_pool for next data.
+		 */
+		if (size < pending_rx_bytes) {
+			pending_rx_bytes -= size;
+			current_rx_buf += size;
+		} else {
+			list_add_tail(&current_rx_req->list, &port->read_pool);
+			pending_rx_bytes = 0;
+			current_rx_req = NULL;
+			current_rx_buf = NULL;
+		}
+	}
+
+	port->pending_rx_bytes = pending_rx_bytes;
+	port->current_rx_buf = current_rx_buf;
+	port->current_rx_req = current_rx_req;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+start_rx:
+	usb_cser_start_rx(port);
+	return bytes_copied;
+}
+
+ssize_t f_cdev_write(struct file *file,
+		       const char __user *buf,
+		       size_t count,
+		       loff_t *ppos)
+{
+	int ret;
+	unsigned long flags;
+	struct f_cdev *port;
+	struct usb_request *req;
+	struct list_head *pool;
+	unsigned int xfer_size;
+	struct usb_ep *in;
+
+	port = file->private_data;
+	if (!port) {
+		pr_err("port is NULL.\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	pr_debug("write on port(%s)(%p)\n", port->name, port);
+
+	if (!port->is_connected) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_err("%s: cable is disconnected.\n", __func__);
+		return -ENODEV;
+	}
+
+	if (list_empty(&port->write_pool)) {
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		pr_debug("%s: Request list is empty.\n", __func__);
+		return 0;
+	}
+
+	in = port->port_usb.in;
+	pool = &port->write_pool;
+	req = list_first_entry(pool, struct usb_request, list);
+	list_del_init(&req->list);
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	pr_debug("%s: write buf size:%zu\n", __func__, count);
+	if (count > BRIDGE_TX_BUF_SIZE)
+		xfer_size = BRIDGE_TX_BUF_SIZE;
+	else
+		xfer_size = count;
+
+	ret = copy_from_user(req->buf, buf, xfer_size);
+	if (ret) {
+		pr_err("copy_from_user failed: err %d\n", ret);
+		ret = -EFAULT;
+	} else {
+		req->length = xfer_size;
+		ret = usb_ep_queue(in, req, GFP_KERNEL);
+		if (ret) {
+			pr_err("EP QUEUE failed:%d\n", ret);
+			ret = -EIO;
+			goto err_exit;
+		}
+		spin_lock_irqsave(&port->port_lock, flags);
+		port->nbytes_from_port_bridge += req->length;
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	}
+
+err_exit:
+	if (ret) {
+		spin_lock_irqsave(&port->port_lock, flags);
+		/* USB cable is connected, add it back otherwise free request */
+		if (port->is_connected)
+			list_add(&req->list, &port->write_pool);
+		else
+			usb_cser_free_req(in, req);
+		spin_unlock_irqrestore(&port->port_lock, flags);
+		return ret;
+	}
+
+	return xfer_size;
+}
+
+static unsigned int f_cdev_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	struct f_cdev *port;
+	unsigned long flags;
+
+	port = file->private_data;
+	if (port && port->is_connected) {
+		poll_wait(file, &port->read_wq, wait);
+		spin_lock_irqsave(&port->port_lock, flags);
+		if (!list_empty(&port->read_queued)) {
+			mask |= POLLIN | POLLRDNORM;
+			pr_debug("sets POLLIN for %s\n", port->name);
+		}
+
+		if (port->cbits_updated) {
+			mask |= POLLPRI;
+			pr_debug("sets POLLPRI for %s\n", port->name);
+		}
+		spin_unlock_irqrestore(&port->port_lock, flags);
+	} else {
+		pr_err("Failed due to NULL device or disconnected.\n");
+		mask = POLLERR;
+	}
+
+	return mask;
+}
+
+static int f_cdev_tiocmget(struct f_cdev *port)
+{
+	struct cserial	*cser;
+	unsigned int result = 0;
+
+	if (!port) {
+		pr_err("port is NULL.\n");
+		return -ENODEV;
+	}
+
+	cser = &port->port_usb;
+	if (cser->get_dtr)
+		result |= (cser->get_dtr(cser) ? TIOCM_DTR : 0);
+
+	if (cser->get_rts)
+		result |= (cser->get_rts(cser) ? TIOCM_RTS : 0);
+
+	if (cser->serial_state & TIOCM_CD)
+		result |= TIOCM_CD;
+
+	if (cser->serial_state & TIOCM_RI)
+		result |= TIOCM_RI;
+	return result;
+}
+
+static int f_cdev_tiocmset(struct f_cdev *port,
+			unsigned int set, unsigned int clear)
+{
+	struct cserial *cser;
+	int status = 0;
+
+	if (!port) {
+		pr_err("port is NULL.\n");
+		return -ENODEV;
+	}
+
+	cser = &port->port_usb;
+	if (set & TIOCM_RI) {
+		if (cser->send_ring_indicator) {
+			cser->serial_state |= TIOCM_RI;
+			status = cser->send_ring_indicator(cser, 1);
+		}
+	}
+	if (clear & TIOCM_RI) {
+		if (cser->send_ring_indicator) {
+			cser->serial_state &= ~TIOCM_RI;
+			status = cser->send_ring_indicator(cser, 0);
+		}
+	}
+	if (set & TIOCM_CD) {
+		if (cser->send_carrier_detect) {
+			cser->serial_state |= TIOCM_CD;
+			status = cser->send_carrier_detect(cser, 1);
+		}
+	}
+	if (clear & TIOCM_CD) {
+		if (cser->send_carrier_detect) {
+			cser->serial_state &= ~TIOCM_CD;
+			status = cser->send_carrier_detect(cser, 0);
+		}
+	}
+
+	return status;
+}
+
+static long f_cdev_ioctl(struct file *fp, unsigned int cmd,
+						unsigned long arg)
+{
+	long ret = 0;
+	int i = 0;
+	uint32_t val;
+	struct f_cdev *port;
+
+	port = fp->private_data;
+	if (!port) {
+		pr_err("port is null.\n");
+		return POLLERR;
+	}
+
+	switch (cmd) {
+	case TIOCMBIC:
+	case TIOCMBIS:
+	case TIOCMSET:
+		pr_debug("TIOCMSET on port(%s)%p\n", port->name, port);
+		i = get_user(val, (uint32_t *)arg);
+		if (i) {
+			pr_err("Error getting TIOCMSET value\n");
+			return i;
+		}
+		ret = f_cdev_tiocmset(port, val, ~val);
+		break;
+	case TIOCMGET:
+		pr_debug("TIOCMGET on port(%s)%p\n", port->name, port);
+		ret = f_cdev_tiocmget(port);
+		if (ret >= 0) {
+			ret = put_user(ret, (uint32_t *)arg);
+			port->cbits_updated = false;
+		}
+		break;
+	default:
+		pr_err("Received cmd:%d not supported\n", cmd);
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+static void usb_cser_notify_modem(void *fport, int ctrl_bits)
+{
+	int temp;
+	struct f_cdev *port = fport;
+
+	if (!port) {
+		pr_err("port is null\n");
+		return;
+	}
+
+	pr_debug("port(%s): ctrl_bits:%x\n", port->name, ctrl_bits);
+
+	temp = convert_acm_sigs_to_uart(ctrl_bits);
+
+	if (temp == port->cbits_to_modem)
+		return;
+
+	port->cbits_to_modem = temp;
+	port->cbits_updated = true;
+
+	wake_up(&port->read_wq);
+}
+
+int usb_cser_connect(struct f_cdev *port)
+{
+	unsigned long flags;
+	int ret;
+	struct cserial *cser;
+
+	if (!port) {
+		pr_err("port is NULL.\n");
+		return -ENODEV;
+	}
+
+	pr_debug("port(%s) (%p)\n", port->name, port);
+
+	cser = &port->port_usb;
+	cser->notify_modem = usb_cser_notify_modem;
+
+	ret = usb_ep_enable(cser->in);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:IN ep:%p, err:%d",
+					cser->in, ret);
+		return ret;
+	}
+	cser->in->driver_data = port;
+
+	ret = usb_ep_enable(cser->out);
+	if (ret) {
+		pr_err("usb_ep_enable failed eptype:OUT ep:%p, err: %d",
+					cser->out, ret);
+		cser->in->driver_data = 0;
+		return ret;
+	}
+	cser->out->driver_data = port;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	cser->pending = false;
+	cser->q_again = false;
+	port->is_connected = true;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	usb_cser_start_io(port);
+	wake_up(&port->open_wq);
+	return 0;
+}
+
+void usb_cser_disconnect(struct f_cdev *port)
+{
+	unsigned long flags;
+
+	usb_cser_stop_io(port);
+
+	/* lower DTR to modem */
+	usb_cser_notify_modem(port, 0);
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->is_connected = false;
+	port->nbytes_from_host = port->nbytes_to_host = 0;
+	port->nbytes_to_port_bridge = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static const struct file_operations f_cdev_fops = {
+	.owner = THIS_MODULE,
+	.open = f_cdev_open,
+	.release = f_cdev_release,
+	.read = f_cdev_read,
+	.write = f_cdev_write,
+	.poll = f_cdev_poll,
+	.unlocked_ioctl = f_cdev_ioctl,
+	.compat_ioctl = f_cdev_ioctl,
+};
+
+static struct f_cdev *f_cdev_alloc(char *func_name, int portno)
+{
+	int ret;
+	dev_t dev;
+	struct device *device;
+	struct f_cdev *port;
+
+	port = kzalloc(sizeof(struct f_cdev), GFP_KERNEL);
+	if (!port) {
+		ret = -ENOMEM;
+		return  ERR_PTR(ret);
+	}
+
+	mutex_lock(&chardev_ida_lock);
+	if (idr_is_empty(&chardev_ida.idr)) {
+		ret = usb_cser_alloc_chardev_region();
+		if (ret) {
+			mutex_unlock(&chardev_ida_lock);
+			pr_err("alloc chardev failed\n");
+			goto err_alloc_chardev;
+		}
+	}
+
+	ret = ida_simple_get(&chardev_ida, 0, 0, GFP_KERNEL);
+	if (ret >= NUM_INSTANCE) {
+		ida_simple_remove(&chardev_ida, ret);
+		mutex_unlock(&chardev_ida_lock);
+		ret = -ENODEV;
+		goto err_get_ida;
+	}
+
+	port->port_num = portno;
+	port->minor = ret;
+	mutex_unlock(&chardev_ida_lock);
+
+	snprintf(port->name, sizeof(port->name), "%s%d", DEVICE_NAME, portno);
+	spin_lock_init(&port->port_lock);
+
+	init_waitqueue_head(&port->open_wq);
+	init_waitqueue_head(&port->read_wq);
+	INIT_LIST_HEAD(&port->read_pool);
+	INIT_LIST_HEAD(&port->read_queued);
+	INIT_LIST_HEAD(&port->write_pool);
+
+	port->fcdev_wq = create_singlethread_workqueue(port->name);
+	if (!port->fcdev_wq) {
+		pr_err("Unable to create workqueue fcdev_wq for port:%s\n",
+						port->name);
+		ret = -ENOMEM;
+		goto err_get_ida;
+	}
+
+	/* create char device */
+	cdev_init(&port->fcdev_cdev, &f_cdev_fops);
+	dev = MKDEV(major, port->minor);
+	ret = cdev_add(&port->fcdev_cdev, dev, 1);
+	if (ret) {
+		pr_err("Failed to add cdev for port(%s)\n", port->name);
+		goto err_cdev_add;
+	}
+
+	device = device_create(fcdev_classp, NULL, dev, NULL, port->name);
+	if (IS_ERR(device)) {
+		ret = PTR_ERR(device);
+		goto err_create_dev;
+	}
+
+	pr_info("port_name:%s (%p) portno:(%d)\n",
+			port->name, port, port->port_num);
+	return port;
+
+err_create_dev:
+	cdev_del(&port->fcdev_cdev);
+err_cdev_add:
+	destroy_workqueue(port->fcdev_wq);
+err_get_ida:
+	usb_cser_chardev_deinit();
+err_alloc_chardev:
+	kfree(port);
+
+	return ERR_PTR(ret);
+}
+
+static void usb_cser_chardev_deinit(void)
+{
+
+	if (idr_is_empty(&chardev_ida.idr)) {
+
+		if (major) {
+			unregister_chrdev_region(MKDEV(major, 0), minors);
+			major = minors = 0;
+		}
+
+		if (!IS_ERR_OR_NULL(fcdev_classp))
+			class_destroy(fcdev_classp);
+	}
+}
+
+static int usb_cser_alloc_chardev_region(void)
+{
+	int ret;
+	dev_t dev;
+
+	ret = alloc_chrdev_region(&dev,
+			       0,
+			       NUM_INSTANCE,
+			       MODULE_NAME);
+	if (ret) {
+		pr_err("alloc_chrdev_region() failed ret:%i\n", ret);
+		return ret;
+	}
+
+	major = MAJOR(dev);
+	minors = NUM_INSTANCE;
+
+	fcdev_classp = class_create(THIS_MODULE, MODULE_NAME);
+	if (IS_ERR(fcdev_classp)) {
+		pr_err("class_create() failed ENOMEM\n");
+		ret = -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline struct f_cdev_opts *to_f_cdev_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_cdev_opts,
+			func_inst.group);
+}
+
+static struct f_cdev_opts *to_fi_cdev_opts(struct usb_function_instance *fi)
+{
+	return container_of(fi, struct f_cdev_opts, func_inst);
+}
+
+static void cserial_attr_release(struct config_item *item)
+{
+	struct f_cdev_opts *opts = to_f_cdev_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations cserial_item_ops = {
+	.release	= cserial_attr_release,
+};
+
+static ssize_t usb_cser_status_show(struct config_item *item, char *page)
+{
+	struct f_cdev *port = to_f_cdev_opts(item)->port;
+	char *buf;
+	unsigned long flags;
+	int temp = 0;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * 512, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	temp += scnprintf(buf + temp, 512 - temp,
+			"###PORT:%s###\n"
+			"port_no:%d\n"
+			"func:%s\n"
+			"nbytes_to_host: %lu\n"
+			"nbytes_from_host: %lu\n"
+			"nbytes_to_port_bridge:  %lu\n"
+			"nbytes_from_port_bridge: %lu\n"
+			"cbits_to_modem:  %u\n"
+			"Port Opened: %s\n",
+			port->name,
+			port->port_num,
+			to_f_cdev_opts(item)->func_name,
+			port->nbytes_to_host,
+			port->nbytes_from_host,
+			port->nbytes_to_port_bridge,
+			port->nbytes_from_port_bridge,
+			port->cbits_to_modem,
+			(port->port_open ? "Opened" : "Closed"));
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	ret = scnprintf(page, temp, buf);
+	kfree(buf);
+
+	return ret;
+}
+
+static ssize_t usb_cser_status_store(struct config_item *item,
+			const char *page, size_t len)
+{
+	struct f_cdev *port = to_f_cdev_opts(item)->port;
+	unsigned long flags;
+	u8 stats;
+
+	if (page == NULL) {
+		pr_err("Invalid buffer");
+		return len;
+	}
+
+	if (kstrtou8(page, 0, &stats) != 0 || stats != 0) {
+		pr_err("(%u)Wrong value. enter 0 to clear.\n", stats);
+		return len;
+	}
+
+	spin_lock_irqsave(&port->port_lock, flags);
+	port->nbytes_to_host = port->nbytes_from_host = 0;
+	port->nbytes_to_port_bridge = port->nbytes_from_port_bridge = 0;
+	spin_unlock_irqrestore(&port->port_lock, flags);
+
+	return len;
+}
+
+CONFIGFS_ATTR(usb_cser_, status);
+static struct configfs_attribute *cserial_attrs[] = {
+	&usb_cser_attr_status,
+	NULL,
+};
+
+static struct config_item_type cserial_func_type = {
+	.ct_item_ops	= &cserial_item_ops,
+	.ct_attrs	= cserial_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static int cser_set_inst_name(struct usb_function_instance *f, const char *name)
+{
+	struct f_cdev_opts *opts =
+		container_of(f, struct f_cdev_opts, func_inst);
+	char *ptr, *str;
+	size_t name_len, str_size;
+	int ret;
+	struct f_cdev *port;
+
+	name_len = strlen(name) + 1;
+	if (name_len > MAX_CDEV_INST_NAME)
+		return -ENAMETOOLONG;
+
+	/* expect name as cdev.<func>.<port_num> */
+	str = strnchr(name, strlen(name), '.');
+	if (!str) {
+		pr_err("invalid input (%s)\n", name);
+		return -EINVAL;
+	}
+
+	/* get function name */
+	str_size = name_len - strlen(str);
+	if (str_size > MAX_CDEV_FUNC_NAME)
+		return -ENAMETOOLONG;
+
+	ptr = kstrndup(name, str_size - 1, GFP_KERNEL);
+	if (!ptr) {
+		pr_err("error:%ld\n", PTR_ERR(ptr));
+		return -ENOMEM;
+	}
+
+	opts->func_name = ptr;
+
+	/* get port number */
+	str = strrchr(name, '.');
+	if (!str) {
+		pr_err("err: port number not found\n");
+		return -EINVAL;
+	}
+	pr_debug("str:%s\n", str);
+
+	*str = '\0';
+	str++;
+
+	ret = kstrtou8(str, 0, &opts->port_num);
+	if (ret) {
+		pr_err("erro: not able to get port number\n");
+		return -EINVAL;
+	}
+
+	pr_debug("gser: port_num:%d func_name:%s\n",
+			opts->port_num, opts->func_name);
+
+	port = f_cdev_alloc(opts->func_name, opts->port_num);
+	if (IS_ERR(port)) {
+		pr_err("Failed to create cdev port(%d)\n", opts->port_num);
+		return -ENOMEM;
+	}
+
+	opts->port = port;
+
+	/* For DUN functionality only sets control signal handling */
+	if (!strcmp(opts->func_name, "dun")) {
+		port->port_usb.connect = dun_cser_connect;
+		port->port_usb.get_dtr = dun_cser_get_dtr;
+		port->port_usb.get_rts = dun_cser_get_rts;
+		port->port_usb.send_carrier_detect =
+				dun_cser_send_carrier_detect;
+		port->port_usb.send_ring_indicator =
+				dun_cser_send_ring_indicator;
+		port->port_usb.send_modem_ctrl_bits = dun_cser_send_ctrl_bits;
+		port->port_usb.disconnect = dun_cser_disconnect;
+		port->port_usb.send_break = dun_cser_send_break;
+	}
+
+	return 0;
+}
+
+static struct usb_function_instance *cser_alloc_inst(void)
+{
+	struct f_cdev_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.free_func_inst = cser_free_inst;
+	opts->func_inst.set_inst_name = cser_set_inst_name;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &cserial_func_type);
+	return &opts->func_inst;
+}
+
+static struct usb_function *cser_alloc(struct usb_function_instance *fi)
+{
+	struct f_cdev_opts *opts = to_fi_cdev_opts(fi);
+	struct f_cdev *port = opts->port;
+
+	port->port_usb.func.name = "cser";
+	port->port_usb.func.strings = usb_cser_strings;
+	port->port_usb.func.bind = usb_cser_bind;
+	port->port_usb.func.unbind = usb_cser_unbind;
+	port->port_usb.func.set_alt = usb_cser_set_alt;
+	port->port_usb.func.disable = usb_cser_disable;
+	port->port_usb.func.setup = usb_cser_setup;
+	port->port_usb.func.free_func = usb_cser_free_func;
+
+	return &port->port_usb.func;
+}
+
+DECLARE_USB_FUNCTION_INIT(cser, cser_alloc_inst, cser_alloc);
+MODULE_DESCRIPTION("USB Serial Character Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 8f3659b..e55ebcb4 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2452,9 +2452,13 @@
 				       &common->fsg->atomic_bitflags))
 			usb_ep_clear_halt(common->fsg->bulk_in);
 
-		if (common->ep0_req_tag == exception_req_tag)
-			ep0_queue(common);	/* Complete the status stage */
-
+		if (common->ep0_req_tag == exception_req_tag) {
+			/* Complete the status stage */
+			if (common->cdev)
+				usb_composite_setup_continue(common->cdev);
+			else
+				ep0_queue(common);
+		}
 		/*
 		 * Technically this should go here, but it would only be
 		 * a waste of time.  Ditto for the INTERFACE_CHANGE and
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 2f713f5..b5ae88f 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -118,6 +118,7 @@
 /**
  * struct usb_function - describes one function of a configuration
  * @name: For diagnostics, identifies the function.
+ * @intf_id: Interface ID
  * @strings: tables of strings, keyed by identifiers assigned during bind()
  *	and by language IDs provided in control requests
  * @fs_descriptors: Table of full (or low) speed descriptors, using interface and
@@ -167,6 +168,9 @@
  *	Function Suspend state (used in Super Speed mode only).
  * @func_wakeup_allowed: Tells whether Function Remote Wakeup has been allowed
  *	by the USB host (used in Super Speed mode only).
+ * @func_wakeup_pending: Marks that the function has issued a Function Wakeup
+ *	while the USB bus was suspended and therefore a Function Wakeup
+ *	notification needs to be sent once the USB bus is resumed.
  *
  * A single USB function uses one or more interfaces, and should in most
  * cases support operation at both full and high speeds.  Each function is
@@ -194,6 +198,7 @@
 
 struct usb_function {
 	const char			*name;
+	int				intf_id;
 	struct usb_gadget_strings	**strings;
 	struct usb_descriptor_header	**fs_descriptors;
 	struct usb_descriptor_header	**hs_descriptors;
@@ -239,6 +244,7 @@
 						u8 suspend_opt);
 	unsigned		func_is_suspended:1;
 	unsigned		func_wakeup_allowed:1;
+	unsigned		func_wakeup_pending:1;
 	/* private: */
 	/* internals */
 	struct list_head		list;
@@ -338,6 +344,10 @@
 	unsigned		fullspeed:1;
 	unsigned		superspeed_plus:1;
 	struct usb_function	*interface[MAX_CONFIG_INTERFACES];
+
+	/* number of in and out eps used in this configuration */
+	int			num_ineps_used;
+	int			num_outeps_used;
 };
 
 int usb_add_config(struct usb_composite_dev *,
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 9cd86ff..509a8f4 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -300,6 +300,8 @@
  * @ep_type: Used to specify type of EP eg. normal vs h/w accelerated.
  * @ep_num: Used EP number
  * @ep_intr_num: Interrupter number for EP.
+ * @endless: In case where endless transfer is being initiated, this is set
+ *      to disable usb event interrupt for few events.
  *
  * the bus controller driver lists all the general purpose endpoints in
  * gadget->ep_list.  the control endpoint (gadget->ep0) is not in that list,
@@ -326,6 +328,7 @@
 	enum ep_type		ep_type;
 	u8			ep_num;
 	u8			ep_intr_num;
+	bool			endless;
 };
 
 /*-------------------------------------------------------------------------*/