diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 7111fbc82a4e9bd11acd749262fa52947c6c59c0..199cd36fe1ba44b4efe750aa72d179d0e1bbc666 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -74,6 +74,29 @@ Example:
 		reboot-offset = <0x4>;
 	};
 
+-----------------------------------------------------------------------
+Hisilicon Hi3798CV200 Peripheral Controller
+
+The Hi3798CV200 Peripheral Controller controls peripherals, queries
+their status, and configures some functions of peripherals.
+
+Required properties:
+- compatible: Should contain "hisilicon,hi3798cv200-perictrl", "syscon"
+  and "simple-mfd".
+- reg: Register address and size of Peripheral Controller.
+- #address-cells: Should be 1.
+- #size-cells: Should be 1.
+
+Examples:
+
+	perictrl: peripheral-controller@8a20000 {
+		compatible = "hisilicon,hi3798cv200-perictrl", "syscon",
+			     "simple-mfd";
+		reg = <0x8a20000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+	};
+
 -----------------------------------------------------------------------
 Hisilicon Hi6220 system controller
 
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
index a105494a0fc94b9940298ae0cb67ed3b9e413f20..b84a02ebffdf0373cd5bfc88d50579966fc7699a 100644
--- a/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
@@ -6,6 +6,10 @@ Required properties:
 - #phys-cells:	must be 0 (see phy-bindings.txt in this directory)
 
 Optional properties:
+- clocks:	a phandle to the clock of this PHY
+- clock-names:	must be "phy"
+- resets:	a phandle to the reset line of this PHY
+- reset-names:	must be "phy"
 - phy-supply:	see phy-bindings.txt in this directory
 
 
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
new file mode 100644
index 0000000000000000000000000000000000000000..114947e1de3da3a00b41370080ddac18d93c765d
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
@@ -0,0 +1,31 @@
+* Amlogic Meson GXL and GXM USB3 PHY and OTG detection binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb3-phy"
+- #phys-cells:	must be 0 (see phy-bindings.txt in this directory)
+- reg:		The base address and length of the registers
+- interrupts:	the interrupt specifier for the OTG detection
+- clocks:	phandles to the clocks for
+		- the USB3 PHY
+		- and peripheral mode/OTG detection
+- clock-names:	must contain "phy" and "peripheral"
+- resets:	phandle to the reset lines for:
+		- the USB3 PHY and
+		- peripheral mode/OTG detection
+- reset-names:	must contain "phy" and "peripheral"
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb3_phy0: phy@78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clkc CLKID_USB_OTG>, <&clkc_AO CLKID_AO_CEC_32K>;
+		clock-names = "phy", "peripheral";
+		resets = <&reset RESET_USB_OTG>, <&reset RESET_USB_OTG>;
+		reset-names = "phy", "peripheral";
+	};
diff --git a/Documentation/devicetree/bindings/phy/phy-hi3798cv200-combphy.txt b/Documentation/devicetree/bindings/phy/phy-hi3798cv200-combphy.txt
new file mode 100644
index 0000000000000000000000000000000000000000..17b0c761370a4a607d43388786f1d28625f0bce6
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-hi3798cv200-combphy.txt
@@ -0,0 +1,59 @@
+HiSilicon STB PCIE/SATA/USB3 PHY
+
+Required properties:
+- compatible: Should be "hisilicon,hi3798cv200-combphy"
+- reg: Should be the address space for COMBPHY configuration and state
+  registers in peripheral controller, e.g. PERI_COMBPHY0_CFG and
+  PERI_COMBPHY0_STATE for COMBPHY0 Hi3798CV200 SoC.
+- #phy-cells: Should be 1.  The cell number is used to select the phy mode
+  as defined in <dt-bindings/phy/phy.h>.
+- clocks: The phandle to clock provider and clock specifier pair.
+- resets: The phandle to reset controller and reset specifier pair.
+
+Refer to phy/phy-bindings.txt for the generic PHY binding properties.
+
+Optional properties:
+- hisilicon,fixed-mode: If the phy device doesn't support mode select
+  but a fixed mode setting, the property should be present to specify
+  the particular mode.
+- hisilicon,mode-select-bits: If the phy device support mode select,
+  this property should be present to specify the register bits in
+  peripheral controller, as a 3 integers tuple:
+  <register_offset bit_shift bit_mask>.
+
+Notes:
+- Between hisilicon,fixed-mode and hisilicon,mode-select-bits, one and only
+  one of them should be present.
+- The device node should be a child of peripheral controller that contains
+  COMBPHY configuration/state and PERI_CTRL register used to select PHY mode.
+  Refer to arm/hisilicon/hisilicon.txt for the parent peripheral controller
+  bindings.
+
+Examples:
+
+perictrl: peripheral-controller@8a20000 {
+	compatible = "hisilicon,hi3798cv200-perictrl", "syscon",
+		     "simple-mfd";
+	reg = <0x8a20000 0x1000>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0x0 0x8a20000 0x1000>;
+
+	combphy0: phy@850 {
+		compatible = "hisilicon,hi3798cv200-combphy";
+		reg = <0x850 0x8>;
+		#phy-cells = <1>;
+		clocks = <&crg HISTB_COMBPHY0_CLK>;
+		resets = <&crg 0x188 4>;
+		hisilicon,fixed-mode = <PHY_TYPE_USB3>;
+	};
+
+	combphy1: phy@858 {
+		compatible = "hisilicon,hi3798cv200-combphy";
+		reg = <0x858 0x8>;
+		#phy-cells = <1>;
+		clocks = <&crg HISTB_COMBPHY1_CLK>;
+		resets = <&crg 0x188 12>;
+		hisilicon,mode-select-bits = <0x0008 11 (0x3 << 11)>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0d70c8341095dbb3f464cca6eebaebd77807a189
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-hisi-inno-usb2.txt
@@ -0,0 +1,71 @@
+Device tree bindings for HiSilicon INNO USB2 PHY
+
+Required properties:
+- compatible: Should be one of the following strings:
+	"hisilicon,inno-usb2-phy",
+	"hisilicon,hi3798cv200-usb2-phy".
+- reg: Should be the address space for PHY configuration register in peripheral
+  controller, e.g. PERI_USB0 for USB 2.0 PHY01 on Hi3798CV200 SoC.
+- clocks: The phandle and clock specifier pair for INNO USB2 PHY device
+  reference clock.
+- resets: The phandle and reset specifier pair for INNO USB2 PHY device reset
+  signal.
+- #address-cells: Must be 1.
+- #size-cells: Must be 0.
+
+The INNO USB2 PHY device should be a child node of peripheral controller that
+contains the PHY configuration register, and each device suppports up to 2 PHY
+ports which are represented as child nodes of INNO USB2 PHY device.
+
+Required properties for PHY port node:
+- reg: The PHY port instance number.
+- #phy-cells: Defined by generic PHY bindings.  Must be 0.
+- resets: The phandle and reset specifier pair for PHY port reset signal.
+
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Example:
+
+perictrl: peripheral-controller@8a20000 {
+	compatible = "hisilicon,hi3798cv200-perictrl", "simple-mfd";
+	reg = <0x8a20000 0x1000>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0x0 0x8a20000 0x1000>;
+
+	usb2_phy1: usb2-phy@120 {
+		compatible = "hisilicon,hi3798cv200-usb2-phy";
+		reg = <0x120 0x4>;
+		clocks = <&crg HISTB_USB2_PHY1_REF_CLK>;
+		resets = <&crg 0xbc 4>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		usb2_phy1_port0: phy@0 {
+			reg = <0>;
+			#phy-cells = <0>;
+			resets = <&crg 0xbc 8>;
+		};
+
+		usb2_phy1_port1: phy@1 {
+			reg = <1>;
+			#phy-cells = <0>;
+			resets = <&crg 0xbc 9>;
+		};
+	};
+
+	usb2_phy2: usb2-phy@124 {
+		compatible = "hisilicon,hi3798cv200-usb2-phy";
+		reg = <0x124 0x4>;
+		clocks = <&crg HISTB_USB2_PHY2_REF_CLK>;
+		resets = <&crg 0xbc 6>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		usb2_phy2_port0: phy@0 {
+			reg = <0>;
+			#phy-cells = <0>;
+			resets = <&crg 0xbc 10>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt
new file mode 100644
index 0000000000000000000000000000000000000000..29427d4f047a15891e345ad31465dc141f92af0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-mapphone-mdm6600.txt
@@ -0,0 +1,29 @@
+Device tree binding documentation for Motorola Mapphone MDM6600 USB PHY
+
+Required properties:
+- compatible		Must be "motorola,mapphone-mdm6600"
+- enable-gpios		GPIO to enable the USB PHY
+- power-gpios		GPIO to power on the device
+- reset-gpios		GPIO to reset the device
+- motorola,mode-gpios	Two GPIOs to configure MDM6600 USB start-up mode for
+			normal mode versus USB flashing mode
+- motorola,cmd-gpios	Three GPIOs to control the power state of the MDM6600
+- motorola,status-gpios	Three GPIOs to read the power state of the MDM6600
+
+Example:
+
+usb-phy {
+	compatible = "motorola,mapphone-mdm6600";
+	enable-gpios = <&gpio3 31 GPIO_ACTIVE_LOW>;
+	power-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>;
+	reset-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>;
+	motorola,mode-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>,
+			      <&gpio5 21 GPIO_ACTIVE_HIGH>;
+	motorola,cmd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>,
+			     <&gpio4 8 GPIO_ACTIVE_HIGH>,
+			     <&gpio5 14 GPIO_ACTIVE_HIGH>;
+	motorola,status-gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>,
+				<&gpio2 21 GPIO_ACTIVE_HIGH>,
+				<&gpio2 23 GPIO_ACTIVE_HIGH>;
+	#phy-cells = <0>;
+};
diff --git a/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt b/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt
index 41e09ed2ca70e2caaf7ee65b2f67651fba7bb9f4..0d34b2b4a6b79600906515679ec45771359d11ac 100644
--- a/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt
+++ b/Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt
@@ -27,6 +27,10 @@ Optional properties (controller (parent) node):
  - reg		: offset and length of register shared by multiple ports,
 		  exclude port's private register. It is needed on mt2701
 		  and mt8173, but not on mt2712.
+ - mediatek,src-ref-clk-mhz	: frequency of reference clock for slew rate
+		  calibrate
+ - mediatek,src-coef	: coefficient for slew rate calibrate, depends on
+		  SoC process
 
 Required properties (port (child) node):
 - reg		: address and length of the register set for the port.
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
index 6ea867e3176fb40c1f03d9f9ce28d9a5513f2d56..960da7fcaa9e4d0aa3b2d957429e26aabec34e82 100644
--- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -14,25 +14,9 @@ Required properties:
  - resets : a list of phandle + reset specifier pairs
  - reset-names : string reset name, must be:
 		 "uphy", "uphy-pipe", "uphy-tcphy"
- - extcon : extcon specifier for the Power Delivery
 
-Note, there are 2 type-c phys for RK3399, and they are almost identical, except
-these registers(description below), every register node contains 3 sections:
-offset, enable bit, write mask bit.
- - rockchip,typec-conn-dir : the register of type-c connector direction,
-   for type-c phy0, it must be <0xe580 0 16>;
-   for type-c phy1, it must be <0xe58c 0 16>;
- - rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
-   control.
-   for type-c phy0, it must be <0xe580 3 19>;
-   for type-c phy1, it must be <0xe58c 3 19>;
- - rockchip,external-psm : the register of type-c phy external psm clock
-   selection.
-   for type-c phy0, it must be <0xe588 14 30>;
-   for type-c phy1, it must be <0xe594 14 30>;
- - rockchip,pipe-status : the register of type-c phy pipe status.
-   for type-c phy0, it must be <0xe5c0 0 0>;
-   for type-c phy1, it must be <0xe5c0 16 16>;
+Optional properties:
+ - extcon : extcon specifier for the Power Delivery
 
 Required nodes : a sub-node is required for each port the phy provides.
 		 The sub-node name is used to identify dp or usb3 port,
@@ -43,6 +27,13 @@ Required nodes : a sub-node is required for each port the phy provides.
 Required properties (port (child) node):
 - #phy-cells : must be 0, See ./phy-bindings.txt for details.
 
+Deprecated properties, do not use in new device tree sources, these
+properties are determined by the compatible value:
+ - rockchip,typec-conn-dir
+ - rockchip,usb3tousb2-en
+ - rockchip,external-psm
+ - rockchip,pipe-status
+
 Example:
 	tcphy0: phy@ff7c0000 {
 		compatible = "rockchip,rk3399-typec-phy";
@@ -58,10 +49,6 @@ Example:
 			 <&cru SRST_UPHY0_PIPE_L00>,
 			 <&cru SRST_P_UPHY0_TCPHY>;
 		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
-		rockchip,typec-conn-dir = <0xe580 0 16>;
-		rockchip,usb3tousb2-en = <0xe580 3 19>;
-		rockchip,external-psm = <0xe588 14 30>;
-		rockchip,pipe-status = <0xe5c0 0 0>;
 
 		tcphy0_dp: dp-port {
 			#phy-cells = <0>;
@@ -86,10 +73,6 @@ Example:
 			 <&cru SRST_UPHY1_PIPE_L00>,
 			 <&cru SRST_P_UPHY1_TCPHY>;
 		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
-		rockchip,typec-conn-dir = <0xe58c 0 16>;
-		rockchip,usb3tousb2-en = <0xe58c 3 19>;
-		rockchip,external-psm = <0xe594 14 30>;
-		rockchip,pipe-status = <0xe5c0 16 16>;
 
 		tcphy1_dp: dp-port {
 			#phy-cells = <0>;
diff --git a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt
new file mode 100644
index 0000000000000000000000000000000000000000..725ae71ae6535a0bdb8ccbf2ed83e168d3002d56
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt
@@ -0,0 +1,73 @@
+STMicroelectronics STM32 USB HS PHY controller
+
+The STM32 USBPHYC block contains a dual port High Speed UTMI+ PHY and a UTMI
+switch. It controls PHY configuration and status, and the UTMI+ switch that
+selects either OTG or HOST controller for the second PHY port. It also sets
+PLL configuration.
+
+USBPHYC
+      |_ PLL
+      |
+      |_ PHY port#1 _________________ HOST controller
+      |                    _                 |
+      |                  / 1|________________|
+      |_ PHY port#2 ----|   |________________
+      |                  \_0|                |
+      |_ UTMI switch_______|          OTG controller
+
+
+Phy provider node
+=================
+
+Required properties:
+- compatible: must be "st,stm32mp1-usbphyc"
+- reg: address and length of the usb phy control register set
+- clocks: phandle + clock specifier for the PLL phy clock
+- #address-cells: number of address cells for phys sub-nodes, must be <1>
+- #size-cells: number of size cells for phys sub-nodes, must be <0>
+
+Optional properties:
+- assigned-clocks: phandle + clock specifier for the PLL phy clock
+- assigned-clock-parents: the PLL phy clock parent
+- resets: phandle + reset specifier
+
+Required nodes: one sub-node per port the controller provides.
+
+Phy sub-nodes
+==============
+
+Required properties:
+- reg: phy port index
+- phy-supply: phandle to the regulator providing 3V3 power to the PHY,
+	      see phy-bindings.txt in the same directory.
+- vdda1v1-supply: phandle to the regulator providing 1V1 power to the PHY
+- vdda1v8-supply: phandle to the regulator providing 1V8 power to the PHY
+- #phy-cells: see phy-bindings.txt in the same directory, must be <0> for PHY
+  port#1 and must be <1> for PHY port#2, to select USB controller
+
+
+Example:
+		usbphyc: usb-phy@5a006000 {
+			compatible = "st,stm32mp1-usbphyc";
+			reg = <0x5a006000 0x1000>;
+			clocks = <&rcc_clk USBPHY_K>;
+			resets = <&rcc_rst USBPHY_R>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			usbphyc_port0: usb-phy@0 {
+				reg = <0>;
+				phy-supply = <&vdd_usb>;
+				vdda1v1-supply = <&reg11>;
+				vdda1v8-supply = <&reg18>
+				#phy-cells = <0>;
+			};
+
+			usbphyc_port1: usb-phy@1 {
+				reg = <1>;
+				phy-supply = <&vdd_usb>;
+				vdda1v1-supply = <&reg11>;
+				vdda1v8-supply = <&reg18>
+				#phy-cells = <1>;
+			};
+		};
diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
index b6a9f2b92bab11c0a303c92535ddc193996de537..dcf1b8f691d508b4186ffb1cd7de31d78a6ff8fb 100644
--- a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
+++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
@@ -8,7 +8,8 @@ Required properties:
  - compatible: compatible list, contains:
 	       "qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074
 	       "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
-	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
+	       "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
+	       "qcom,qmp-v3-usb3-phy" for USB3 QMP V3 phy.
 
  - reg: offset and length of register set for PHY's common serdes block.
 
@@ -25,10 +26,13 @@ Required properties:
  - clock-names: "cfg_ahb" for phy config clock,
 		"aux" for phy aux clock,
 		"ref" for 19.2 MHz ref clk,
+		"com_aux" for phy common block aux clock,
 		For "qcom,msm8996-qmp-pcie-phy" must contain:
 			"aux", "cfg_ahb", "ref".
 		For "qcom,msm8996-qmp-usb3-phy" must contain:
 			"aux", "cfg_ahb", "ref".
+		For "qcom,qmp-v3-usb3-phy" must contain:
+			"aux", "cfg_ahb", "ref", "com_aux".
 
  - resets: a list of phandles and reset controller specifier pairs,
 	   one for each entry in reset-names.
diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
index aa0fcb05acb3e4d309224f21fff291c71a6286e9..42c97426836eaf50fbccb2271d1d719c5918fa06 100644
--- a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
+++ b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
@@ -4,7 +4,10 @@ Qualcomm QUSB2 phy controller
 QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
 
 Required properties:
- - compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
+ - compatible: compatible list, contains
+	       "qcom,msm8996-qusb2-phy" for 14nm PHY on msm8996,
+	       "qcom,qusb2-v2-phy" for QUSB2 V2 PHY.
+
  - reg: offset and length of the PHY register set.
  - #phy-cells: must be 0.
 
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
index 99b651b33110361ed1cf94bc4859ed400b2a4977..dbd137c079e2de33dd9bbbdfc6e2c516c4fc5eec 100644
--- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
+++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
@@ -8,6 +8,8 @@ Required properties:
 	      SoC.
 	      "renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
 	      SoC.
+	      "renesas,usb2-phy-r8a77965" if the device is a part of an
+	      R8A77965 SoC.
 	      "renesas,usb2-phy-r8a77995" if the device is a part of an
 	      R8A77995 SoC.
 	      "renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt
index f94cea48f6b1c4ffd90267651b4d7add1d580a66..47dd296ecead9598e94835d4d5ce41386ef7940d 100644
--- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt
+++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt
@@ -11,6 +11,8 @@ Required properties:
 	      SoC.
 	      "renesas,r8a7796-usb3-phy" if the device is a part of an R8A7796
 	      SoC.
+	      "renesas,r8a77965-usb3-phy" if the device is a part of an
+	      R8A77965 SoC.
 	      "renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 compatible
 	      device.
 
diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
index c1ce5a0a652ecbbbb9e61b251b6d41eaad52433f..07ca4ec4a745c94e38e65474e63e49c93a0e30c1 100644
--- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -11,6 +11,7 @@ Required properties:
   * allwinner,sun8i-a33-usb-phy
   * allwinner,sun8i-a83t-usb-phy
   * allwinner,sun8i-h3-usb-phy
+  * allwinner,sun8i-r40-usb-phy
   * allwinner,sun8i-v3s-usb-phy
   * allwinner,sun50i-a64-usb-phy
 - reg : a list of offset + length pairs
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index aa857be692cf8d7dccbdd0d9d40815cdcca09a44..d4dcd39b8d76f0e8b1ee3e99416de6d2ec576c55 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -112,6 +112,7 @@ enum sun4i_usb_phy_type {
 	sun8i_a33_phy,
 	sun8i_a83t_phy,
 	sun8i_h3_phy,
+	sun8i_r40_phy,
 	sun8i_v3s_phy,
 	sun50i_a64_phy,
 };
@@ -410,11 +411,13 @@ static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data)
 		return true;
 
 	/*
-	 * The A31 companion pmic (axp221) does not generate vbus change
-	 * interrupts when the board is driving vbus, so we must poll
+	 * The A31/A23/A33 companion pmics (AXP221/AXP223) do not
+	 * generate vbus change interrupts when the board is driving
+	 * vbus using the N_VBUSEN pin on the pmic, so we must poll
 	 * when using the pmic for vbus-det _and_ we're driving vbus.
 	 */
-	if (data->cfg->type == sun6i_a31_phy &&
+	if ((data->cfg->type == sun6i_a31_phy ||
+	     data->cfg->type == sun8i_a33_phy) &&
 	    data->vbus_power_supply && data->phys[0].regulator_on)
 		return true;
 
@@ -885,7 +888,7 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
 
 static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
 	.num_phys = 2,
-	.type = sun4i_a10_phy,
+	.type = sun6i_a31_phy,
 	.disc_thresh = 3,
 	.phyctl_offset = REG_PHYCTL_A10,
 	.dedicated_clocks = true,
@@ -919,6 +922,16 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
 	.phy0_dual_route = true,
 };
 
+static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
+	.num_phys = 3,
+	.type = sun8i_r40_phy,
+	.disc_thresh = 3,
+	.phyctl_offset = REG_PHYCTL_A33,
+	.dedicated_clocks = true,
+	.enable_pmu_unk1 = true,
+	.phy0_dual_route = true,
+};
+
 static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
 	.num_phys = 1,
 	.type = sun8i_v3s_phy,
@@ -948,6 +961,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
 	{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
 	{ .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg },
 	{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
+	{ .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
 	{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
 	{ .compatible = "allwinner,sun50i-a64-usb-phy",
 	  .data = &sun50i_a64_cfg},
diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
index cb8f4501652b133fffcc3ff327acc8174dfecb36..23fe1cda2f70d18fd948aa77f34a1e5adf37af93 100644
--- a/drivers/phy/amlogic/Kconfig
+++ b/drivers/phy/amlogic/Kconfig
@@ -18,10 +18,21 @@ config PHY_MESON_GXL_USB2
 	default ARCH_MESON
 	depends on OF && (ARCH_MESON || COMPILE_TEST)
 	depends on USB_SUPPORT
-	select USB_COMMON
 	select GENERIC_PHY
 	select REGMAP_MMIO
 	help
 	  Enable this to support the Meson USB2 PHYs found in Meson
 	  GXL and GXM SoCs.
 	  If unsure, say N.
+
+config PHY_MESON_GXL_USB3
+	tristate "Meson GXL and GXM USB3 PHY drivers"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	depends on USB_SUPPORT
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB3 PHY and OTG detection
+	  IP block found in Meson GXL and GXM SoCs.
+	  If unsure, say N.
diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
index cfdc98715c30a886b9c10e79448c4471d1f2da3f..4fd8848c194d69995ac3495ca323426ffff47a74 100644
--- a/drivers/phy/amlogic/Makefile
+++ b/drivers/phy/amlogic/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
 obj-$(CONFIG_PHY_MESON_GXL_USB2)	+= phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB3)	+= phy-meson-gxl-usb3.o
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
index e90c4ee25dfe5cde10912fb1c06a9ed642b1551e..9f9b5414b97a03f7af318df0cfa497e1b9b7320a 100644
--- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c
+++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c
@@ -11,14 +11,15 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
-#include <linux/usb/of.h>
 
 /* bits [31:27] are read-only */
 #define U2P_R0							0x0
@@ -70,12 +71,11 @@
 
 /* bits [31:14] are read-only */
 #define U2P_R2							0x8
-	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
-	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
-	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
-	#define U2P_R2_DATA_OUT_SEL				BIT(12)
-	#define U2P_R2_CLK					BIT(13)
-	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
+	#define U2P_R2_TESTDATA_IN_MASK				GENMASK(7, 0)
+	#define U2P_R2_TESTADDR_MASK				GENMASK(11, 8)
+	#define U2P_R2_TESTDATA_OUT_SEL				BIT(12)
+	#define U2P_R2_TESTCLK					BIT(13)
+	#define U2P_R2_TESTDATA_OUT_MASK			GENMASK(17, 14)
 	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
 	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
 	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
@@ -99,6 +99,8 @@ struct phy_meson_gxl_usb2_priv {
 	struct regmap		*regmap;
 	enum phy_mode		mode;
 	int			is_enabled;
+	struct clk		*clk;
+	struct reset_control	*reset;
 };
 
 static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
@@ -108,6 +110,31 @@ static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
 	.max_register = U2P_R3,
 };
 
+static int phy_meson_gxl_usb2_init(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = reset_control_reset(priv->reset);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_exit(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	clk_disable_unprepare(priv->clk);
+
+	return 0;
+}
+
 static int phy_meson_gxl_usb2_reset(struct phy *phy)
 {
 	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
@@ -195,6 +222,8 @@ static int phy_meson_gxl_usb2_power_on(struct phy *phy)
 }
 
 static const struct phy_ops phy_meson_gxl_usb2_ops = {
+	.init		= phy_meson_gxl_usb2_init,
+	.exit		= phy_meson_gxl_usb2_exit,
 	.power_on	= phy_meson_gxl_usb2_power_on,
 	.power_off	= phy_meson_gxl_usb2_power_off,
 	.set_mode	= phy_meson_gxl_usb2_set_mode,
@@ -210,6 +239,7 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
 	struct phy_meson_gxl_usb2_priv *priv;
 	struct phy *phy;
 	void __iomem *base;
+	int ret;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
@@ -222,28 +252,34 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
 	if (IS_ERR(base))
 		return PTR_ERR(base);
 
-	switch (of_usb_get_dr_mode_by_phy(dev->of_node, -1)) {
-	case USB_DR_MODE_PERIPHERAL:
-		priv->mode = PHY_MODE_USB_DEVICE;
-		break;
-	case USB_DR_MODE_OTG:
-		priv->mode = PHY_MODE_USB_OTG;
-		break;
-	case USB_DR_MODE_HOST:
-	default:
-		priv->mode = PHY_MODE_USB_HOST;
-		break;
-	}
+	/* start in host mode */
+	priv->mode = PHY_MODE_USB_HOST;
 
 	priv->regmap = devm_regmap_init_mmio(dev, base,
 					     &phy_meson_gxl_usb2_regmap_conf);
 	if (IS_ERR(priv->regmap))
 		return PTR_ERR(priv->regmap);
 
+	priv->clk = devm_clk_get(dev, "phy");
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		if (ret == -ENOENT)
+			priv->clk = NULL;
+		else
+			return ret;
+	}
+
+	priv->reset = devm_reset_control_get_optional_shared(dev, "phy");
+	if (IS_ERR(priv->reset))
+		return PTR_ERR(priv->reset);
+
 	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
 	if (IS_ERR(phy)) {
-		dev_err(dev, "failed to create PHY\n");
-		return PTR_ERR(phy);
+		ret = PTR_ERR(phy);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to create PHY\n");
+
+		return ret;
 	}
 
 	phy_set_drvdata(phy, priv);
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
new file mode 100644
index 0000000000000000000000000000000000000000..d37d94ddf9c09bb3c306c8136dc2ccacdd412b7a
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Meson GXL USB3 PHY and OTG mode detection driver
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/platform_device.h>
+
+#define USB_R0							0x00
+	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
+	#define USB_R0_P30_PHY_RESET				BIT(6)
+	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
+	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
+	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
+	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
+	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
+	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x04
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 12)
+	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
+	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
+	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x08
+	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
+	#define USB_R2_P30_CR_READ				BIT(16)
+	#define USB_R2_P30_CR_WRITE				BIT(17)
+	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
+	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x0c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
+	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
+	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
+
+#define USB_R4							0x10
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x14
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
+	#define USB_R5_ID_DIG_EN_0				BIT(4)
+	#define USB_R5_ID_DIG_EN_1				BIT(5)
+	#define USB_R5_ID_DIG_CURR				BIT(6)
+	#define USB_R5_ID_DIG_IRQ				BIT(7)
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+/* read-only register */
+#define USB_R6							0x18
+	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
+	#define USB_R6_P30_CR_ACK				BIT(16)
+
+struct phy_meson_gxl_usb3_priv {
+	struct regmap		*regmap;
+	enum phy_mode		mode;
+	struct clk		*clk_phy;
+	struct clk		*clk_peripheral;
+	struct reset_control	*reset;
+};
+
+static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
+	.reg_bits = 8,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = USB_R6,
+};
+
+static int phy_meson_gxl_usb3_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
+			   USB_R5_ID_DIG_EN_0);
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
+			   USB_R5_ID_DIG_EN_1);
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
+			   FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+	switch (mode) {
+	case PHY_MODE_USB_HOST:
+		regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
+		regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
+				   0);
+		break;
+
+	case PHY_MODE_USB_DEVICE:
+		regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
+				   USB_R0_U2D_ACT);
+		regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
+				   USB_R4_P21_SLEEP_M0);
+		break;
+
+	default:
+		dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
+		return -EINVAL;
+	}
+
+	priv->mode = mode;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_init(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = reset_control_reset(priv->reset);
+	if (ret)
+		goto err;
+
+	ret = clk_prepare_enable(priv->clk_phy);
+	if (ret)
+		goto err;
+
+	ret = clk_prepare_enable(priv->clk_peripheral);
+	if (ret)
+		goto err_disable_clk_phy;
+
+	ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode);
+	if (ret)
+		goto err_disable_clk_peripheral;
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			   FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
+
+	return 0;
+
+err_disable_clk_peripheral:
+	clk_disable_unprepare(priv->clk_peripheral);
+err_disable_clk_phy:
+	clk_disable_unprepare(priv->clk_phy);
+err:
+	return ret;
+}
+
+static int phy_meson_gxl_usb3_exit(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+
+	clk_disable_unprepare(priv->clk_peripheral);
+	clk_disable_unprepare(priv->clk_phy);
+
+	return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb3_ops = {
+	.power_on	= phy_meson_gxl_usb3_power_on,
+	.power_off	= phy_meson_gxl_usb3_power_off,
+	.set_mode	= phy_meson_gxl_usb3_set_mode,
+	.init		= phy_meson_gxl_usb3_init,
+	.exit		= phy_meson_gxl_usb3_exit,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_meson_gxl_usb3_priv *priv;
+	struct resource *res;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	void __iomem *base;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	priv->regmap = devm_regmap_init_mmio(dev, base,
+					     &phy_meson_gxl_usb3_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	priv->clk_phy = devm_clk_get(dev, "phy");
+	if (IS_ERR(priv->clk_phy))
+		return PTR_ERR(priv->clk_phy);
+
+	priv->clk_peripheral = devm_clk_get(dev, "peripheral");
+	if (IS_ERR(priv->clk_peripheral))
+		return PTR_ERR(priv->clk_peripheral);
+
+	priv->reset = devm_reset_control_array_get_shared(dev);
+	if (IS_ERR(priv->reset))
+		return PTR_ERR(priv->reset);
+
+	/*
+	 * default to host mode as hardware defaults and/or boot-loader
+	 * behavior can result in this PHY starting up in device mode. this
+	 * default and the initialization in phy_meson_gxl_usb3_init ensure
+	 * that we reproducibly start in a known mode on all devices.
+	 */
+	priv->mode = PHY_MODE_USB_HOST;
+
+	phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
+	if (IS_ERR(phy)) {
+		ret = PTR_ERR(phy);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to create PHY\n");
+
+		return ret;
+	}
+
+	phy_set_drvdata(phy, priv);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb3-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
+
+static struct platform_driver phy_meson_gxl_usb3_driver = {
+	.probe	= phy_meson_gxl_usb3_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb3",
+		.of_match_table	= phy_meson_gxl_usb3_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb3_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index 6164c4cd0f6579a9a539fefd33d94937fcf7342b..b40ee54a1a502e1cd822d25611e871d4b1db54f5 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -4,6 +4,7 @@
 config PHY_HI6220_USB
 	tristate "hi6220 USB PHY support"
 	depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+	depends on HAS_IOMEM
 	select GENERIC_PHY
 	select MFD_SYSCON
 	help
@@ -11,6 +12,25 @@ config PHY_HI6220_USB
 
 	  To compile this driver as a module, choose M here.
 
+config PHY_HISTB_COMBPHY
+	tristate "HiSilicon STB SoCs COMBPHY support"
+	depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+	select GENERIC_PHY
+	select MFD_SYSCON
+	help
+	  Enable this to support the HISILICON STB SoCs COMBPHY.
+	  If unsure, say N.
+
+config PHY_HISI_INNO_USB2
+       tristate "HiSilicon INNO USB2 PHY support"
+       depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+       select GENERIC_PHY
+       select MFD_SYSCON
+       help
+         Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
+         USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
+         USB host port to accept one USB device.
+
 config PHY_HIX5HD2_SATA
 	tristate "HIX5HD2 SATA PHY Driver"
 	depends on ARCH_HIX5HD2 && OF && HAS_IOMEM
diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile
index 541b348187a8512cf31c845a2bde9dfd2d4603ad..f662a4fe18d85b9e7a432aa42ebc7b4a128cf384 100644
--- a/drivers/phy/hisilicon/Makefile
+++ b/drivers/phy/hisilicon/Makefile
@@ -1,2 +1,4 @@
 obj-$(CONFIG_PHY_HI6220_USB)		+= phy-hi6220-usb.o
+obj-$(CONFIG_PHY_HISTB_COMBPHY)		+= phy-histb-combphy.o
+obj-$(CONFIG_PHY_HISI_INNO_USB2)	+= phy-hisi-inno-usb2.o
 obj-$(CONFIG_PHY_HIX5HD2_SATA)		+= phy-hix5hd2-sata.o
diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
new file mode 100644
index 0000000000000000000000000000000000000000..524381249a2b80ad857369b691a29195232a29f0
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
@@ -0,0 +1,197 @@
+/*
+ * HiSilicon INNO USB2 PHY Driver.
+ *
+ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+
+#define INNO_PHY_PORT_NUM	2
+#define REF_CLK_STABLE_TIME	100	/* unit:us */
+#define UTMI_CLK_STABLE_TIME	200	/* unit:us */
+#define TEST_CLK_STABLE_TIME	2	/* unit:ms */
+#define PHY_CLK_STABLE_TIME	2	/* unit:ms */
+#define UTMI_RST_COMPLETE_TIME	2	/* unit:ms */
+#define POR_RST_COMPLETE_TIME	300	/* unit:us */
+#define PHY_TEST_DATA		GENMASK(7, 0)
+#define PHY_TEST_ADDR		GENMASK(15, 8)
+#define PHY_TEST_PORT		GENMASK(18, 16)
+#define PHY_TEST_WREN		BIT(21)
+#define PHY_TEST_CLK		BIT(22)	/* rising edge active */
+#define PHY_TEST_RST		BIT(23)	/* low active */
+#define PHY_CLK_ENABLE		BIT(2)
+
+struct hisi_inno_phy_port {
+	struct reset_control *utmi_rst;
+	struct hisi_inno_phy_priv *priv;
+};
+
+struct hisi_inno_phy_priv {
+	void __iomem *mmio;
+	struct clk *ref_clk;
+	struct reset_control *por_rst;
+	struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM];
+};
+
+static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv,
+				    u8 port, u32 addr, u32 data)
+{
+	void __iomem *reg = priv->mmio;
+	u32 val;
+
+	val = (data & PHY_TEST_DATA) |
+	      ((addr << 8) & PHY_TEST_ADDR) |
+	      ((port << 16) & PHY_TEST_PORT) |
+	      PHY_TEST_WREN | PHY_TEST_RST;
+	writel(val, reg);
+
+	val |= PHY_TEST_CLK;
+	writel(val, reg);
+
+	val &= ~PHY_TEST_CLK;
+	writel(val, reg);
+}
+
+static void hisi_inno_phy_setup(struct hisi_inno_phy_priv *priv)
+{
+	/* The phy clk is controlled by the port0 register 0x06. */
+	hisi_inno_phy_write_reg(priv, 0, 0x06, PHY_CLK_ENABLE);
+	msleep(PHY_CLK_STABLE_TIME);
+}
+
+static int hisi_inno_phy_init(struct phy *phy)
+{
+	struct hisi_inno_phy_port *port = phy_get_drvdata(phy);
+	struct hisi_inno_phy_priv *priv = port->priv;
+	int ret;
+
+	ret = clk_prepare_enable(priv->ref_clk);
+	if (ret)
+		return ret;
+	udelay(REF_CLK_STABLE_TIME);
+
+	reset_control_deassert(priv->por_rst);
+	udelay(POR_RST_COMPLETE_TIME);
+
+	/* Set up phy registers */
+	hisi_inno_phy_setup(priv);
+
+	reset_control_deassert(port->utmi_rst);
+	udelay(UTMI_RST_COMPLETE_TIME);
+
+	return 0;
+}
+
+static int hisi_inno_phy_exit(struct phy *phy)
+{
+	struct hisi_inno_phy_port *port = phy_get_drvdata(phy);
+	struct hisi_inno_phy_priv *priv = port->priv;
+
+	reset_control_assert(port->utmi_rst);
+	reset_control_assert(priv->por_rst);
+	clk_disable_unprepare(priv->ref_clk);
+
+	return 0;
+}
+
+static const struct phy_ops hisi_inno_phy_ops = {
+	.init = hisi_inno_phy_init,
+	.exit = hisi_inno_phy_exit,
+	.owner = THIS_MODULE,
+};
+
+static int hisi_inno_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct hisi_inno_phy_priv *priv;
+	struct phy_provider *provider;
+	struct device_node *child;
+	struct resource *res;
+	int i = 0;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->mmio)) {
+		ret = PTR_ERR(priv->mmio);
+		return ret;
+	}
+
+	priv->ref_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->ref_clk))
+		return PTR_ERR(priv->ref_clk);
+
+	priv->por_rst = devm_reset_control_get_exclusive(dev, NULL);
+	if (IS_ERR(priv->por_rst))
+		return PTR_ERR(priv->por_rst);
+
+	for_each_child_of_node(np, child) {
+		struct reset_control *rst;
+		struct phy *phy;
+
+		rst = of_reset_control_get_exclusive(child, NULL);
+		if (IS_ERR(rst))
+			return PTR_ERR(rst);
+		priv->ports[i].utmi_rst = rst;
+		priv->ports[i].priv = priv;
+
+		phy = devm_phy_create(dev, child, &hisi_inno_phy_ops);
+		if (IS_ERR(phy))
+			return PTR_ERR(phy);
+
+		phy_set_bus_width(phy, 8);
+		phy_set_drvdata(phy, &priv->ports[i]);
+		i++;
+
+		if (i > INNO_PHY_PORT_NUM) {
+			dev_warn(dev, "Support %d ports in maximum\n", i);
+			break;
+		}
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id hisi_inno_phy_of_match[] = {
+	{ .compatible = "hisilicon,inno-usb2-phy", },
+	{ .compatible = "hisilicon,hi3798cv200-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match);
+
+static struct platform_driver hisi_inno_phy_driver = {
+	.probe	= hisi_inno_phy_probe,
+	.driver = {
+		.name	= "hisi-inno-phy",
+		.of_match_table	= hisi_inno_phy_of_match,
+	}
+};
+module_platform_driver(hisi_inno_phy_driver);
+
+MODULE_DESCRIPTION("HiSilicon INNO USB2 PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/hisilicon/phy-histb-combphy.c b/drivers/phy/hisilicon/phy-histb-combphy.c
new file mode 100644
index 0000000000000000000000000000000000000000..5777b312001745a3c0db025e3275399b37ca6226
--- /dev/null
+++ b/drivers/phy/hisilicon/phy-histb-combphy.c
@@ -0,0 +1,289 @@
+/*
+ * COMBPHY driver for HiSilicon STB SoCs
+ *
+ * Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com
+ *
+ * Authors: Jianguo Sun <sunjianguo1@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <dt-bindings/phy/phy.h>
+
+#define COMBPHY_MODE_PCIE		0
+#define COMBPHY_MODE_USB3		1
+#define COMBPHY_MODE_SATA		2
+
+#define COMBPHY_CFG_REG			0x0
+#define COMBPHY_BYPASS_CODEC		BIT(31)
+#define COMBPHY_TEST_WRITE		BIT(24)
+#define COMBPHY_TEST_DATA_SHIFT		20
+#define COMBPHY_TEST_DATA_MASK		GENMASK(23, 20)
+#define COMBPHY_TEST_ADDR_SHIFT		12
+#define COMBPHY_TEST_ADDR_MASK		GENMASK(16, 12)
+#define COMBPHY_CLKREF_OUT_OEN		BIT(0)
+
+struct histb_combphy_mode {
+	int fixed;
+	int select;
+	u32 reg;
+	u32 shift;
+	u32 mask;
+};
+
+struct histb_combphy_priv {
+	void __iomem *mmio;
+	struct regmap *syscon;
+	struct reset_control *por_rst;
+	struct clk *ref_clk;
+	struct phy *phy;
+	struct histb_combphy_mode mode;
+};
+
+static void nano_register_write(struct histb_combphy_priv *priv,
+				u32 addr, u32 data)
+{
+	void __iomem *reg = priv->mmio + COMBPHY_CFG_REG;
+	u32 val;
+
+	/* Set up address and data for the write */
+	val = readl(reg);
+	val &= ~COMBPHY_TEST_ADDR_MASK;
+	val |= addr << COMBPHY_TEST_ADDR_SHIFT;
+	val &= ~COMBPHY_TEST_DATA_MASK;
+	val |= data << COMBPHY_TEST_DATA_SHIFT;
+	writel(val, reg);
+
+	/* Flip strobe control to trigger the write */
+	val &= ~COMBPHY_TEST_WRITE;
+	writel(val, reg);
+	val |= COMBPHY_TEST_WRITE;
+	writel(val, reg);
+}
+
+static int is_mode_fixed(struct histb_combphy_mode *mode)
+{
+	return (mode->fixed != PHY_NONE) ? true : false;
+}
+
+static int histb_combphy_set_mode(struct histb_combphy_priv *priv)
+{
+	struct histb_combphy_mode *mode = &priv->mode;
+	struct regmap *syscon = priv->syscon;
+	u32 hw_sel;
+
+	if (is_mode_fixed(mode))
+		return 0;
+
+	switch (mode->select) {
+	case PHY_TYPE_SATA:
+		hw_sel = COMBPHY_MODE_SATA;
+		break;
+	case PHY_TYPE_PCIE:
+		hw_sel = COMBPHY_MODE_PCIE;
+		break;
+	case PHY_TYPE_USB3:
+		hw_sel = COMBPHY_MODE_USB3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(syscon, mode->reg, mode->mask,
+				  hw_sel << mode->shift);
+}
+
+static int histb_combphy_init(struct phy *phy)
+{
+	struct histb_combphy_priv *priv = phy_get_drvdata(phy);
+	u32 val;
+	int ret;
+
+	ret = histb_combphy_set_mode(priv);
+	if (ret)
+		return ret;
+
+	/* Clear bypass bit to enable encoding/decoding */
+	val = readl(priv->mmio + COMBPHY_CFG_REG);
+	val &= ~COMBPHY_BYPASS_CODEC;
+	writel(val, priv->mmio + COMBPHY_CFG_REG);
+
+	ret = clk_prepare_enable(priv->ref_clk);
+	if (ret)
+		return ret;
+
+	reset_control_deassert(priv->por_rst);
+
+	/* Enable EP clock */
+	val = readl(priv->mmio + COMBPHY_CFG_REG);
+	val |= COMBPHY_CLKREF_OUT_OEN;
+	writel(val, priv->mmio + COMBPHY_CFG_REG);
+
+	/* Need to wait for EP clock stable */
+	mdelay(5);
+
+	/* Configure nano phy registers as suggested by vendor */
+	nano_register_write(priv, 0x1, 0x8);
+	nano_register_write(priv, 0xc, 0x9);
+	nano_register_write(priv, 0x1a, 0x4);
+
+	return 0;
+}
+
+static int histb_combphy_exit(struct phy *phy)
+{
+	struct histb_combphy_priv *priv = phy_get_drvdata(phy);
+	u32 val;
+
+	/* Disable EP clock */
+	val = readl(priv->mmio + COMBPHY_CFG_REG);
+	val &= ~COMBPHY_CLKREF_OUT_OEN;
+	writel(val, priv->mmio + COMBPHY_CFG_REG);
+
+	reset_control_assert(priv->por_rst);
+	clk_disable_unprepare(priv->ref_clk);
+
+	return 0;
+}
+
+static const struct phy_ops histb_combphy_ops = {
+	.init = histb_combphy_init,
+	.exit = histb_combphy_exit,
+	.owner = THIS_MODULE,
+};
+
+static struct phy *histb_combphy_xlate(struct device *dev,
+				       struct of_phandle_args *args)
+{
+	struct histb_combphy_priv *priv = dev_get_drvdata(dev);
+	struct histb_combphy_mode *mode = &priv->mode;
+
+	if (args->args_count < 1) {
+		dev_err(dev, "invalid number of arguments\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mode->select = args->args[0];
+
+	if (mode->select < PHY_TYPE_SATA || mode->select > PHY_TYPE_USB3) {
+		dev_err(dev, "invalid phy mode select argument\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (is_mode_fixed(mode) && mode->select != mode->fixed) {
+		dev_err(dev, "mode select %d mismatch fixed phy mode %d\n",
+			mode->select, mode->fixed);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return priv->phy;
+}
+
+static int histb_combphy_probe(struct platform_device *pdev)
+{
+	struct phy_provider *phy_provider;
+	struct device *dev = &pdev->dev;
+	struct histb_combphy_priv *priv;
+	struct device_node *np = dev->of_node;
+	struct histb_combphy_mode *mode;
+	struct resource *res;
+	u32 vals[3];
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->mmio)) {
+		ret = PTR_ERR(priv->mmio);
+		return ret;
+	}
+
+	priv->syscon = syscon_node_to_regmap(np->parent);
+	if (IS_ERR(priv->syscon)) {
+		dev_err(dev, "failed to find peri_ctrl syscon regmap\n");
+		return PTR_ERR(priv->syscon);
+	}
+
+	mode = &priv->mode;
+	mode->fixed = PHY_NONE;
+
+	ret = of_property_read_u32(np, "hisilicon,fixed-mode", &mode->fixed);
+	if (ret == 0)
+		dev_dbg(dev, "found fixed phy mode %d\n", mode->fixed);
+
+	ret = of_property_read_u32_array(np, "hisilicon,mode-select-bits",
+					 vals, ARRAY_SIZE(vals));
+	if (ret == 0) {
+		if (is_mode_fixed(mode)) {
+			dev_err(dev, "found select bits for fixed mode phy\n");
+			return -EINVAL;
+		}
+
+		mode->reg = vals[0];
+		mode->shift = vals[1];
+		mode->mask = vals[2];
+		dev_dbg(dev, "found mode select bits\n");
+	} else {
+		if (!is_mode_fixed(mode)) {
+			dev_err(dev, "no valid select bits found for non-fixed phy\n");
+			return -ENODEV;
+		}
+	}
+
+	priv->ref_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->ref_clk)) {
+		dev_err(dev, "failed to find ref clock\n");
+		return PTR_ERR(priv->ref_clk);
+	}
+
+	priv->por_rst = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(priv->por_rst)) {
+		dev_err(dev, "failed to get poweron reset\n");
+		return PTR_ERR(priv->por_rst);
+	}
+
+	priv->phy = devm_phy_create(dev, NULL, &histb_combphy_ops);
+	if (IS_ERR(priv->phy)) {
+		dev_err(dev, "failed to create combphy\n");
+		return PTR_ERR(priv->phy);
+	}
+
+	dev_set_drvdata(dev, priv);
+	phy_set_drvdata(priv->phy, priv);
+
+	phy_provider = devm_of_phy_provider_register(dev, histb_combphy_xlate);
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id histb_combphy_of_match[] = {
+	{ .compatible = "hisilicon,hi3798cv200-combphy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, histb_combphy_of_match);
+
+static struct platform_driver histb_combphy_driver = {
+	.probe	= histb_combphy_probe,
+	.driver = {
+		.name = "combphy",
+		.of_match_table = histb_combphy_of_match,
+	},
+};
+module_platform_driver(histb_combphy_driver);
+
+MODULE_DESCRIPTION("HiSilicon STB COMBPHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c
index 2017751ede2635b6ae1c927ad3d831128fc75ef8..8f2b5cae360f2cb04f7789fac25c16a35a38e43c 100644
--- a/drivers/phy/marvell/phy-berlin-usb.c
+++ b/drivers/phy/marvell/phy-berlin-usb.c
@@ -127,7 +127,7 @@ static int phy_berlin_usb_power_on(struct phy *phy)
 	writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
 	       priv->base + USB_PHY_ANALOG);
 	writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
-	       DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
+	       DISCON_THRESHOLD_270 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
 	       INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
 
 	writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index 1e96d0740ef5f1ea16f8ea81d8052ab92f514adb..38c281b5abbb0dfe339cf04929ee06fce6a5688e 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -306,6 +306,8 @@ struct mtk_tphy {
 	const struct mtk_phy_pdata *pdata;
 	struct mtk_phy_instance **phys;
 	int nphys;
+	int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */
+	int src_coef; /* coefficient for slew rate calibrate */
 };
 
 static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
@@ -360,16 +362,17 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
 	writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
 
 	if (fm_out) {
-		/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
-		tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
-		tmp /= fm_out;
+		/* ( 1024 / FM_OUT ) x reference clock frequency x coef */
+		tmp = tphy->src_ref_clk * tphy->src_coef;
+		tmp = (tmp * U3P_FM_DET_CYCLE_CNT) / fm_out;
 		calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
 	} else {
 		/* if FM detection fail, set default value */
 		calibration_val = 4;
 	}
-	dev_dbg(tphy->dev, "phy:%d, fm_out:%d, calib:%d\n",
-		instance->index, fm_out, calibration_val);
+	dev_dbg(tphy->dev, "phy:%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n",
+		instance->index, fm_out, calibration_val,
+		tphy->src_ref_clk, tphy->src_coef);
 
 	/* set HS slew rate */
 	tmp = readl(com + U3P_USBPHYACR5);
@@ -688,8 +691,7 @@ static void pcie_phy_instance_power_on(struct mtk_tphy *tphy,
 	u32 tmp;
 
 	tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD);
-	tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_MCU_BUS_CK_GATE_EN |
-		P3C_REG_IP_SW_RST);
+	tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST);
 	writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD);
 
 	tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE);
@@ -1042,6 +1044,13 @@ static int mtk_tphy_probe(struct platform_device *pdev)
 		tphy->u3phya_ref = NULL;
 	}
 
+	tphy->src_ref_clk = U3P_REF_CLK;
+	tphy->src_coef = U3P_SLEW_RATE_COEF;
+	/* update parameters of slew rate calibrate if exist */
+	device_property_read_u32(dev, "mediatek,src-ref-clk-mhz",
+		&tphy->src_ref_clk);
+	device_property_read_u32(dev, "mediatek,src-coef", &tphy->src_coef);
+
 	port = 0;
 	for_each_child_of_node(np, child_np) {
 		struct mtk_phy_instance *instance;
diff --git a/drivers/phy/motorola/Kconfig b/drivers/phy/motorola/Kconfig
index 6bb7d6bdf1bf12cc958279d1fa48bfb9c3f605d8..82651524ffb9ce33424dd5a3eb9851cc14eef11f 100644
--- a/drivers/phy/motorola/Kconfig
+++ b/drivers/phy/motorola/Kconfig
@@ -10,3 +10,11 @@ config PHY_CPCAP_USB
 	help
 	  Enable this for USB to work on Motorola phones and tablets
 	  such as Droid 4.
+
+config PHY_MAPPHONE_MDM6600
+	tristate "Motorola Mapphone MDM6600 modem USB PHY driver"
+	depends on OF && USB_SUPPORT
+	select GENERIC_PHY
+	help
+	  Enable this for MDM6600 USB modem to work on Motorola phones
+	  and tablets such as Droid 4.
diff --git a/drivers/phy/motorola/Makefile b/drivers/phy/motorola/Makefile
index b6cd618d671ac5a556fb99801062821ea804e280..3514f985b355a59aa1890dc96a5b2008a2a34260 100644
--- a/drivers/phy/motorola/Makefile
+++ b/drivers/phy/motorola/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_PHY_CPCAP_USB)		+= phy-cpcap-usb.o
+obj-$(CONFIG_PHY_MAPPHONE_MDM6600)	+= phy-mapphone-mdm6600.o
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
new file mode 100644
index 0000000000000000000000000000000000000000..5439dd90d0ddc4f3fe12e0196765759f6dd88867
--- /dev/null
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver
+ * Copyright (C) 2018 Tony Lindgren <tony@atomide.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+
+#define PHY_MDM6600_PHY_DELAY_MS	4000	/* PHY enable 2.2s to 3.5s */
+#define PHY_MDM6600_ENABLED_DELAY_MS	8000	/* 8s more total for MDM6600 */
+
+enum phy_mdm6600_ctrl_lines {
+	PHY_MDM6600_ENABLE,			/* USB PHY enable */
+	PHY_MDM6600_POWER,			/* Device power */
+	PHY_MDM6600_RESET,			/* Device reset */
+	PHY_MDM6600_NR_CTRL_LINES,
+};
+
+enum phy_mdm6600_bootmode_lines {
+	PHY_MDM6600_MODE0,			/* out USB mode0 and OOB wake */
+	PHY_MDM6600_MODE1,			/* out USB mode1, in OOB wake */
+	PHY_MDM6600_NR_MODE_LINES,
+};
+
+enum phy_mdm6600_cmd_lines {
+	PHY_MDM6600_CMD0,
+	PHY_MDM6600_CMD1,
+	PHY_MDM6600_CMD2,
+	PHY_MDM6600_NR_CMD_LINES,
+};
+
+enum phy_mdm6600_status_lines {
+	PHY_MDM6600_STATUS0,
+	PHY_MDM6600_STATUS1,
+	PHY_MDM6600_STATUS2,
+	PHY_MDM6600_NR_STATUS_LINES,
+};
+
+/*
+ * MDM6600 command codes. These are based on Motorola Mapphone Linux
+ * kernel tree.
+ */
+enum phy_mdm6600_cmd {
+	PHY_MDM6600_CMD_BP_PANIC_ACK,
+	PHY_MDM6600_CMD_DATA_ONLY_BYPASS,	/* Reroute USB to CPCAP PHY */
+	PHY_MDM6600_CMD_FULL_BYPASS,		/* Reroute USB to CPCAP PHY */
+	PHY_MDM6600_CMD_NO_BYPASS,		/* Request normal USB mode */
+	PHY_MDM6600_CMD_BP_SHUTDOWN_REQ,	/* Request device power off */
+	PHY_MDM6600_CMD_BP_UNKNOWN_5,
+	PHY_MDM6600_CMD_BP_UNKNOWN_6,
+	PHY_MDM6600_CMD_UNDEFINED,
+};
+
+/*
+ * MDM6600 status codes. These are based on Motorola Mapphone Linux
+ * kernel tree.
+ */
+enum phy_mdm6600_status {
+	PHY_MDM6600_STATUS_PANIC,		/* Seems to be really off */
+	PHY_MDM6600_STATUS_PANIC_BUSY_WAIT,
+	PHY_MDM6600_STATUS_QC_DLOAD,
+	PHY_MDM6600_STATUS_RAM_DOWNLOADER,	/* MDM6600 USB flashing mode */
+	PHY_MDM6600_STATUS_PHONE_CODE_AWAKE,	/* MDM6600 normal USB mode */
+	PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP,
+	PHY_MDM6600_STATUS_SHUTDOWN_ACK,
+	PHY_MDM6600_STATUS_UNDEFINED,
+};
+
+static const char * const
+phy_mdm6600_status_name[] = {
+	"off", "busy", "qc_dl", "ram_dl", "awake",
+	"asleep", "shutdown", "undefined",
+};
+
+struct phy_mdm6600 {
+	struct device *dev;
+	struct phy *generic_phy;
+	struct phy_provider *phy_provider;
+	struct gpio_desc *ctrl_gpios[PHY_MDM6600_NR_CTRL_LINES];
+	struct gpio_descs *mode_gpios;
+	struct gpio_descs *status_gpios;
+	struct gpio_descs *cmd_gpios;
+	struct delayed_work bootup_work;
+	struct delayed_work status_work;
+	struct completion ack;
+	bool enabled;				/* mdm6600 phy enabled */
+	bool running;				/* mdm6600 boot done */
+	int status;
+};
+
+static int phy_mdm6600_init(struct phy *x)
+{
+	struct phy_mdm6600 *ddata = phy_get_drvdata(x);
+	struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
+
+	if (!ddata->enabled)
+		return -EPROBE_DEFER;
+
+	gpiod_set_value_cansleep(enable_gpio, 0);
+
+	return 0;
+}
+
+static int phy_mdm6600_power_on(struct phy *x)
+{
+	struct phy_mdm6600 *ddata = phy_get_drvdata(x);
+	struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
+
+	if (!ddata->enabled)
+		return -ENODEV;
+
+	gpiod_set_value_cansleep(enable_gpio, 1);
+
+	return 0;
+}
+
+static int phy_mdm6600_power_off(struct phy *x)
+{
+	struct phy_mdm6600 *ddata = phy_get_drvdata(x);
+	struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
+
+	if (!ddata->enabled)
+		return -ENODEV;
+
+	gpiod_set_value_cansleep(enable_gpio, 0);
+
+	return 0;
+}
+
+static const struct phy_ops gpio_usb_ops = {
+	.init = phy_mdm6600_init,
+	.power_on = phy_mdm6600_power_on,
+	.power_off = phy_mdm6600_power_off,
+	.owner = THIS_MODULE,
+};
+
+/**
+ * phy_mdm6600_cmd() - send a command request to mdm6600
+ * @ddata: device driver data
+ *
+ * Configures the three command request GPIOs to the specified value.
+ */
+static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
+{
+	int values[PHY_MDM6600_NR_CMD_LINES];
+	int i;
+
+	val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
+	for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
+		values[i] = (val & BIT(i)) >> i;
+
+	gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
+				       ddata->cmd_gpios->desc, values);
+}
+
+/**
+ * phy_mdm6600_status() - read mdm6600 status lines
+ * @ddata: device driver data
+ */
+static void phy_mdm6600_status(struct work_struct *work)
+{
+	struct phy_mdm6600 *ddata;
+	struct device *dev;
+	int values[PHY_MDM6600_NR_STATUS_LINES];
+	int error, i, val = 0;
+
+	ddata = container_of(work, struct phy_mdm6600, status_work.work);
+	dev = ddata->dev;
+
+	error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
+					       ddata->status_gpios->desc,
+					       values);
+	if (error)
+		return;
+
+	for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) {
+		val |= values[i] << i;
+		dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
+			__func__, i, values[i], val);
+	}
+	ddata->status = val;
+
+	dev_info(dev, "modem status: %i %s\n",
+		 ddata->status,
+		 phy_mdm6600_status_name[ddata->status & 7]);
+	complete(&ddata->ack);
+}
+
+static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data)
+{
+	struct phy_mdm6600 *ddata = data;
+
+	schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10));
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting
+ * @irq: interrupt
+ * @data: interrupt handler data
+ *
+ * GPIO mode1 is used initially as output to configure the USB boot
+ * mode for mdm6600. After booting it is used as input for OOB wake
+ * signal from mdm6600 to the SoC. Just use it for debug info only
+ * for now.
+ */
+static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data)
+{
+	struct phy_mdm6600 *ddata = data;
+	struct gpio_desc *mode_gpio1;
+
+	mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
+	dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n",
+		gpiod_get_value(mode_gpio1));
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines
+ * @ddata: device driver data
+ */
+static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata)
+{
+	struct device *dev = ddata->dev;
+	int i, error, irq;
+
+	for (i = PHY_MDM6600_STATUS0;
+	     i <= PHY_MDM6600_STATUS2; i++) {
+		struct gpio_desc *gpio = ddata->status_gpios->desc[i];
+
+		irq = gpiod_to_irq(gpio);
+		if (irq <= 0)
+			continue;
+
+		error = devm_request_threaded_irq(dev, irq, NULL,
+					phy_mdm6600_irq_thread,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING |
+					IRQF_ONESHOT,
+					"mdm6600",
+					ddata);
+		if (error)
+			dev_warn(dev, "no modem status irq%i: %i\n",
+				 irq, error);
+	}
+}
+
+struct phy_mdm6600_map {
+	const char *name;
+	int direction;
+};
+
+static const struct phy_mdm6600_map
+phy_mdm6600_ctrl_gpio_map[PHY_MDM6600_NR_CTRL_LINES] = {
+	{ "enable", GPIOD_OUT_LOW, },		/* low = phy disabled */
+	{ "power", GPIOD_OUT_LOW, },		/* low = off */
+	{ "reset", GPIOD_OUT_HIGH, },		/* high = reset */
+};
+
+/**
+ * phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines
+ * @ddata: device driver data
+ */
+static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata)
+{
+	struct device *dev = ddata->dev;
+	int i;
+
+	/* MDM6600 control lines */
+	for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) {
+		const struct phy_mdm6600_map *map =
+			&phy_mdm6600_ctrl_gpio_map[i];
+		struct gpio_desc **gpio = &ddata->ctrl_gpios[i];
+
+		*gpio = devm_gpiod_get(dev, map->name, map->direction);
+		if (IS_ERR(*gpio)) {
+			dev_info(dev, "gpio %s error %li\n",
+				 map->name, PTR_ERR(*gpio));
+			return PTR_ERR(*gpio);
+		}
+	}
+
+	/* MDM6600 USB start-up mode output lines */
+	ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode",
+						 GPIOD_OUT_LOW);
+	if (IS_ERR(ddata->mode_gpios))
+		return PTR_ERR(ddata->mode_gpios);
+
+	if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES)
+		return -EINVAL;
+
+	/* MDM6600 status input lines */
+	ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status",
+						   GPIOD_IN);
+	if (IS_ERR(ddata->status_gpios))
+		return PTR_ERR(ddata->status_gpios);
+
+	if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES)
+		return -EINVAL;
+
+	/* MDM6600 cmd output lines */
+	ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd",
+						GPIOD_OUT_LOW);
+	if (IS_ERR(ddata->cmd_gpios))
+		return PTR_ERR(ddata->cmd_gpios);
+
+	if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * phy_mdm6600_device_power_on() - power on mdm6600 device
+ * @ddata: device driver data
+ *
+ * To get the integrated USB phy in MDM6600 takes some hoops. We must ensure
+ * the shared USB bootmode GPIOs are configured, then request modem start-up,
+ * reset and power-up.. And then we need to recycle the shared USB bootmode
+ * GPIOs as they are also used for Out of Band (OOB) wake for the USB and
+ * TS 27.010 serial mux.
+ */
+static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata)
+{
+	struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio;
+	int error = 0, wakeirq;
+
+	mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
+	mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
+	reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
+	power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER];
+
+	/*
+	 * Shared GPIOs must be low for normal USB mode. After booting
+	 * they are used for OOB wake signaling. These can be also used
+	 * to configure USB flashing mode later on based on a module
+	 * parameter.
+	 */
+	gpiod_set_value_cansleep(mode_gpio0, 0);
+	gpiod_set_value_cansleep(mode_gpio1, 0);
+
+	/* Request start-up mode */
+	phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_NO_BYPASS);
+
+	/* Request a reset first */
+	gpiod_set_value_cansleep(reset_gpio, 0);
+	msleep(100);
+
+	/* Toggle power GPIO to request mdm6600 to start */
+	gpiod_set_value_cansleep(power_gpio, 1);
+	msleep(100);
+	gpiod_set_value_cansleep(power_gpio, 0);
+
+	/*
+	 * Looks like the USB PHY needs between 2.2 to 4 seconds.
+	 * If we try to use it before that, we will get L3 errors
+	 * from omap-usb-host trying to access the PHY. See also
+	 * phy_mdm6600_init() for -EPROBE_DEFER.
+	 */
+	msleep(PHY_MDM6600_PHY_DELAY_MS);
+	ddata->enabled = true;
+
+	/* Booting up the rest of MDM6600 will take total about 8 seconds */
+	dev_info(ddata->dev, "Waiting for power up request to complete..\n");
+	if (wait_for_completion_timeout(&ddata->ack,
+			msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS))) {
+		if (ddata->status > PHY_MDM6600_STATUS_PANIC &&
+		    ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK)
+			dev_info(ddata->dev, "Powered up OK\n");
+	} else {
+		ddata->enabled = false;
+		error = -ETIMEDOUT;
+		dev_err(ddata->dev, "Timed out powering up\n");
+	}
+
+	/* Reconfigure mode1 GPIO as input for OOB wake */
+	gpiod_direction_input(mode_gpio1);
+
+	wakeirq = gpiod_to_irq(mode_gpio1);
+	if (wakeirq <= 0)
+		return wakeirq;
+
+	error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL,
+					  phy_mdm6600_wakeirq_thread,
+					  IRQF_TRIGGER_RISING |
+					  IRQF_TRIGGER_FALLING |
+					  IRQF_ONESHOT,
+					  "mdm6600-wake",
+					  ddata);
+	if (error)
+		dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n",
+			 wakeirq, error);
+
+	ddata->running = true;
+
+	return error;
+}
+
+/**
+ * phy_mdm6600_device_power_off() - power off mdm6600 device
+ * @ddata: device driver data
+ */
+static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata)
+{
+	struct gpio_desc *reset_gpio =
+		ddata->ctrl_gpios[PHY_MDM6600_RESET];
+
+	ddata->enabled = false;
+	phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_BP_SHUTDOWN_REQ);
+	msleep(100);
+
+	gpiod_set_value_cansleep(reset_gpio, 1);
+
+	dev_info(ddata->dev, "Waiting for power down request to complete.. ");
+	if (wait_for_completion_timeout(&ddata->ack,
+					msecs_to_jiffies(5000))) {
+		if (ddata->status == PHY_MDM6600_STATUS_PANIC)
+			dev_info(ddata->dev, "Powered down OK\n");
+	} else {
+		dev_err(ddata->dev, "Timed out powering down\n");
+	}
+}
+
+static void phy_mdm6600_deferred_power_on(struct work_struct *work)
+{
+	struct phy_mdm6600 *ddata;
+	int error;
+
+	ddata = container_of(work, struct phy_mdm6600, bootup_work.work);
+
+	error = phy_mdm6600_device_power_on(ddata);
+	if (error)
+		dev_err(ddata->dev, "Device not functional\n");
+}
+
+static const struct of_device_id phy_mdm6600_id_table[] = {
+	{ .compatible = "motorola,mapphone-mdm6600", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table);
+
+static int phy_mdm6600_probe(struct platform_device *pdev)
+{
+	struct phy_mdm6600 *ddata;
+	int error;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&ddata->bootup_work,
+			  phy_mdm6600_deferred_power_on);
+	INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status);
+	init_completion(&ddata->ack);
+
+	ddata->dev = &pdev->dev;
+	platform_set_drvdata(pdev, ddata);
+
+	error = phy_mdm6600_init_lines(ddata);
+	if (error)
+		return error;
+
+	phy_mdm6600_init_irq(ddata);
+
+	ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
+	if (IS_ERR(ddata->generic_phy)) {
+		error = PTR_ERR(ddata->generic_phy);
+		goto cleanup;
+	}
+
+	phy_set_drvdata(ddata->generic_phy, ddata);
+
+	ddata->phy_provider =
+		devm_of_phy_provider_register(ddata->dev,
+					      of_phy_simple_xlate);
+	if (IS_ERR(ddata->phy_provider)) {
+		error = PTR_ERR(ddata->phy_provider);
+		goto cleanup;
+	}
+
+	schedule_delayed_work(&ddata->bootup_work, 0);
+
+	/*
+	 * See phy_mdm6600_device_power_on(). We should be able
+	 * to remove this eventually when ohci-platform can deal
+	 * with -EPROBE_DEFER.
+	 */
+	msleep(PHY_MDM6600_PHY_DELAY_MS + 500);
+
+	return 0;
+
+cleanup:
+	phy_mdm6600_device_power_off(ddata);
+	return error;
+}
+
+static int phy_mdm6600_remove(struct platform_device *pdev)
+{
+	struct phy_mdm6600 *ddata = platform_get_drvdata(pdev);
+	struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
+
+	if (!ddata->running)
+		wait_for_completion_timeout(&ddata->ack,
+			msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS));
+
+	gpiod_set_value_cansleep(reset_gpio, 1);
+	phy_mdm6600_device_power_off(ddata);
+
+	cancel_delayed_work_sync(&ddata->bootup_work);
+	cancel_delayed_work_sync(&ddata->status_work);
+
+	return 0;
+}
+
+static struct platform_driver phy_mdm6600_driver = {
+	.probe = phy_mdm6600_probe,
+	.remove = phy_mdm6600_remove,
+	.driver = {
+		.name = "phy-mapphone-mdm6600",
+		.of_match_table = of_match_ptr(phy_mdm6600_id_table),
+	},
+};
+
+module_platform_driver(phy_mdm6600_driver);
+
+MODULE_ALIAS("platform:gpio_usb");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("mdm6600 gpio usb phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 8f6e8e28996d6deb853f30c4fae15ebc334a1f59..09ac8afb97acf2e16cae2cf25ed0967031c39143 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -351,6 +351,8 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
 
 	mutex_lock(&phy->mutex);
 	ret = phy->ops->set_mode(phy, mode);
+	if (!ret)
+		phy->attrs.mode = mode;
 	mutex_unlock(&phy->mutex);
 
 	return ret;
diff --git a/drivers/phy/phy-lpc18xx-usb-otg.c b/drivers/phy/phy-lpc18xx-usb-otg.c
index 3b7a71eb5b7e03bc8ba0de65867dd788be957876..7de280a454218b9616afc7d24cfdaab24686eacf 100644
--- a/drivers/phy/phy-lpc18xx-usb-otg.c
+++ b/drivers/phy/phy-lpc18xx-usb-otg.c
@@ -60,8 +60,14 @@ static int lpc18xx_usb_otg_phy_power_on(struct phy *phy)
 		return ret;
 
 	/* The bit in CREG is cleared to enable the PHY */
-	return regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
+	ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
 				  LPC18XX_CREG_CREG0_USB0PHY, 0);
+	if (ret) {
+		clk_disable(lpc->clk);
+		return ret;
+	}
+
+	return 0;
 }
 
 static int lpc18xx_usb_otg_phy_power_off(struct phy *phy)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c
index e17f0351ccc2ec27681fd6aebd02bbb1e2fd74f5..6470c5d61d1c79e3f7b8e38e55aa13893625d426 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
  */
 
 #include <linux/clk.h>
@@ -31,124 +22,7 @@
 
 #include <dt-bindings/phy/phy.h>
 
-/* QMP PHY QSERDES COM registers */
-#define QSERDES_COM_BG_TIMER				0x00c
-#define QSERDES_COM_SSC_EN_CENTER			0x010
-#define QSERDES_COM_SSC_ADJ_PER1			0x014
-#define QSERDES_COM_SSC_ADJ_PER2			0x018
-#define QSERDES_COM_SSC_PER1				0x01c
-#define QSERDES_COM_SSC_PER2				0x020
-#define QSERDES_COM_SSC_STEP_SIZE1			0x024
-#define QSERDES_COM_SSC_STEP_SIZE2			0x028
-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			0x034
-#define QSERDES_COM_CLK_ENABLE1				0x038
-#define QSERDES_COM_SYS_CLK_CTRL			0x03c
-#define QSERDES_COM_SYSCLK_BUF_ENABLE			0x040
-#define QSERDES_COM_PLL_IVCO				0x048
-#define QSERDES_COM_LOCK_CMP1_MODE0			0x04c
-#define QSERDES_COM_LOCK_CMP2_MODE0			0x050
-#define QSERDES_COM_LOCK_CMP3_MODE0			0x054
-#define QSERDES_COM_LOCK_CMP1_MODE1			0x058
-#define QSERDES_COM_LOCK_CMP2_MODE1			0x05c
-#define QSERDES_COM_LOCK_CMP3_MODE1			0x060
-#define QSERDES_COM_BG_TRIM				0x070
-#define QSERDES_COM_CLK_EP_DIV				0x074
-#define QSERDES_COM_CP_CTRL_MODE0			0x078
-#define QSERDES_COM_CP_CTRL_MODE1			0x07c
-#define QSERDES_COM_PLL_RCTRL_MODE0			0x084
-#define QSERDES_COM_PLL_RCTRL_MODE1			0x088
-#define QSERDES_COM_PLL_CCTRL_MODE0			0x090
-#define QSERDES_COM_PLL_CCTRL_MODE1			0x094
-#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM			0x0a8
-#define QSERDES_COM_SYSCLK_EN_SEL			0x0ac
-#define QSERDES_COM_RESETSM_CNTRL			0x0b4
-#define QSERDES_COM_RESTRIM_CTRL			0x0bc
-#define QSERDES_COM_RESCODE_DIV_NUM			0x0c4
-#define QSERDES_COM_LOCK_CMP_EN				0x0c8
-#define QSERDES_COM_LOCK_CMP_CFG			0x0cc
-#define QSERDES_COM_DEC_START_MODE0			0x0d0
-#define QSERDES_COM_DEC_START_MODE1			0x0d4
-#define QSERDES_COM_DIV_FRAC_START1_MODE0		0x0dc
-#define QSERDES_COM_DIV_FRAC_START2_MODE0		0x0e0
-#define QSERDES_COM_DIV_FRAC_START3_MODE0		0x0e4
-#define QSERDES_COM_DIV_FRAC_START1_MODE1		0x0e8
-#define QSERDES_COM_DIV_FRAC_START2_MODE1		0x0ec
-#define QSERDES_COM_DIV_FRAC_START3_MODE1		0x0f0
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		0x108
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		0x10c
-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1		0x110
-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1		0x114
-#define QSERDES_COM_VCO_TUNE_CTRL			0x124
-#define QSERDES_COM_VCO_TUNE_MAP			0x128
-#define QSERDES_COM_VCO_TUNE1_MODE0			0x12c
-#define QSERDES_COM_VCO_TUNE2_MODE0			0x130
-#define QSERDES_COM_VCO_TUNE1_MODE1			0x134
-#define QSERDES_COM_VCO_TUNE2_MODE1			0x138
-#define QSERDES_COM_VCO_TUNE_TIMER1			0x144
-#define QSERDES_COM_VCO_TUNE_TIMER2			0x148
-#define QSERDES_COM_BG_CTRL				0x170
-#define QSERDES_COM_CLK_SELECT				0x174
-#define QSERDES_COM_HSCLK_SEL				0x178
-#define QSERDES_COM_CORECLK_DIV				0x184
-#define QSERDES_COM_CORE_CLK_EN				0x18c
-#define QSERDES_COM_C_READY_STATUS			0x190
-#define QSERDES_COM_CMN_CONFIG				0x194
-#define QSERDES_COM_SVS_MODE_CLK_SEL			0x19c
-#define QSERDES_COM_DEBUG_BUS0				0x1a0
-#define QSERDES_COM_DEBUG_BUS1				0x1a4
-#define QSERDES_COM_DEBUG_BUS2				0x1a8
-#define QSERDES_COM_DEBUG_BUS3				0x1ac
-#define QSERDES_COM_DEBUG_BUS_SEL			0x1b0
-#define QSERDES_COM_CORECLK_DIV_MODE1			0x1bc
-
-/* QMP PHY TX registers */
-#define QSERDES_TX_RES_CODE_LANE_OFFSET			0x054
-#define QSERDES_TX_DEBUG_BUS_SEL			0x064
-#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN	0x068
-#define QSERDES_TX_LANE_MODE				0x094
-#define QSERDES_TX_RCV_DETECT_LVL_2			0x0ac
-
-/* QMP PHY RX registers */
-#define QSERDES_RX_UCDR_SO_GAIN_HALF			0x010
-#define QSERDES_RX_UCDR_SO_GAIN				0x01c
-#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN		0x040
-#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE	0x048
-#define QSERDES_RX_RX_TERM_BW				0x090
-#define QSERDES_RX_RX_EQ_GAIN1_LSB			0x0c4
-#define QSERDES_RX_RX_EQ_GAIN1_MSB			0x0c8
-#define QSERDES_RX_RX_EQ_GAIN2_LSB			0x0cc
-#define QSERDES_RX_RX_EQ_GAIN2_MSB			0x0d0
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d8
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3		0x0dc
-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4		0x0e0
-#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1		0x108
-#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x10c
-#define QSERDES_RX_SIGDET_ENABLES			0x110
-#define QSERDES_RX_SIGDET_CNTRL				0x114
-#define QSERDES_RX_SIGDET_LVL				0x118
-#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL		0x11c
-#define QSERDES_RX_RX_BAND				0x120
-#define QSERDES_RX_RX_INTERFACE_MODE			0x12c
-
-/* QMP PHY PCS registers */
-#define QPHY_POWER_DOWN_CONTROL				0x04
-#define QPHY_TXDEEMPH_M6DB_V0				0x24
-#define QPHY_TXDEEMPH_M3P5DB_V0				0x28
-#define QPHY_ENDPOINT_REFCLK_DRIVE			0x54
-#define QPHY_RX_IDLE_DTCT_CNTRL				0x58
-#define QPHY_POWER_STATE_CONFIG1			0x60
-#define QPHY_POWER_STATE_CONFIG2			0x64
-#define QPHY_POWER_STATE_CONFIG4			0x6c
-#define QPHY_LOCK_DETECT_CONFIG1			0x80
-#define QPHY_LOCK_DETECT_CONFIG2			0x84
-#define QPHY_LOCK_DETECT_CONFIG3			0x88
-#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK		0xa0
-#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK			0xa4
-#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB		0x1A8
-#define QPHY_OSC_DTCT_ACTIONS				0x1AC
-#define QPHY_RX_SIGDET_LVL				0x1D8
-#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB		0x1DC
-#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB		0x1E0
+#include "phy-qcom-qmp.h"
 
 /* QPHY_SW_RESET bit */
 #define SW_RESET				BIT(0)
@@ -164,6 +38,34 @@
 /* QPHY_COM_PCS_READY_STATUS bit */
 #define PCS_READY				BIT(0)
 
+/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
+/* DP PHY soft reset */
+#define SW_DPPHY_RESET				BIT(0)
+/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
+#define SW_DPPHY_RESET_MUX			BIT(1)
+/* USB3 PHY soft reset */
+#define SW_USB3PHY_RESET			BIT(2)
+/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
+#define SW_USB3PHY_RESET_MUX			BIT(3)
+
+/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
+#define USB3_MODE				BIT(0) /* enables USB3 mode */
+#define DP_MODE					BIT(1) /* enables DP mode */
+
+/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */
+#define ARCVR_DTCT_EN				BIT(0)
+#define ALFPS_DTCT_EN				BIT(1)
+#define ARCVR_DTCT_EVENT_SEL			BIT(4)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */
+#define IRQ_CLEAR				BIT(0)
+
+/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */
+#define RCVR_DETECT				BIT(0)
+
+/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
+#define CLAMP_EN				BIT(0) /* enables i/o clamp_n */
+
 #define PHY_INIT_COMPLETE_TIMEOUT		1000
 #define POWER_DOWN_DELAY_US_MIN			10
 #define POWER_DOWN_DELAY_US_MAX			11
@@ -210,6 +112,9 @@ enum qphy_reg_layout {
 	QPHY_SW_RESET,
 	QPHY_START_CTRL,
 	QPHY_PCS_READY_STATUS,
+	QPHY_PCS_AUTONOMOUS_MODE_CTRL,
+	QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
+	QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
 };
 
 static const unsigned int pciephy_regs_layout[] = {
@@ -237,6 +142,18 @@ static const unsigned int usb3phy_regs_layout[] = {
 	[QPHY_SW_RESET]			= 0x00,
 	[QPHY_START_CTRL]		= 0x08,
 	[QPHY_PCS_READY_STATUS]		= 0x17c,
+	[QPHY_PCS_AUTONOMOUS_MODE_CTRL]	= 0x0d4,
+	[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR]  = 0x0d8,
+	[QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178,
+};
+
+static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
+	[QPHY_SW_RESET]			= 0x00,
+	[QPHY_START_CTRL]		= 0x08,
+	[QPHY_PCS_READY_STATUS]		= 0x174,
+	[QPHY_PCS_AUTONOMOUS_MODE_CTRL]	= 0x0d8,
+	[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR]  = 0x0dc,
+	[QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
 };
 
 static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
@@ -467,6 +384,112 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = {
 	QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3),
 };
 
+static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
+	QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+};
+
+static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = {
+	/* FLL settings */
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+	/* Lock Det settings */
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
+
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+};
+
 /* struct qmp_phy_cfg - per-PHY initialization config */
 struct qmp_phy_cfg {
 	/* phy-type - PCIE/UFS/USB */
@@ -511,6 +534,12 @@ struct qmp_phy_cfg {
 	/* power_down delay in usec */
 	int pwrdn_delay_min;
 	int pwrdn_delay_max;
+
+	/* true, if PHY has a separate DP_COM control block */
+	bool has_phy_dp_com_ctrl;
+	/* Register offset of secondary tx/rx lanes for USB DP combo PHY */
+	unsigned int tx_b_lane_offset;
+	unsigned int rx_b_lane_offset;
 };
 
 /**
@@ -520,6 +549,7 @@ struct qmp_phy_cfg {
  * @tx: iomapped memory space for lane's tx
  * @rx: iomapped memory space for lane's rx
  * @pcs: iomapped memory space for lane's pcs
+ * @pcs_misc: iomapped memory space for lane's pcs_misc
  * @pipe_clk: pipe lock
  * @index: lane index
  * @qmp: QMP phy to which this lane belongs
@@ -530,6 +560,7 @@ struct qmp_phy {
 	void __iomem *tx;
 	void __iomem *rx;
 	void __iomem *pcs;
+	void __iomem *pcs_misc;
 	struct clk *pipe_clk;
 	unsigned int index;
 	struct qcom_qmp *qmp;
@@ -541,6 +572,7 @@ struct qmp_phy {
  *
  * @dev: device
  * @serdes: iomapped memory space for phy's serdes
+ * @dp_com: iomapped memory space for phy's dp_com control block
  *
  * @clks: array of clocks required by phy
  * @resets: array of resets required by phy
@@ -550,12 +582,15 @@ struct qmp_phy {
  * @phys: array of per-lane phy descriptors
  * @phy_mutex: mutex lock for PHY common block initialization
  * @init_count: phy common block initialization count
+ * @phy_initialized: indicate if PHY has been initialized
+ * @mode: current PHY mode
  */
 struct qcom_qmp {
 	struct device *dev;
 	void __iomem *serdes;
+	void __iomem *dp_com;
 
-	struct clk **clks;
+	struct clk_bulk_data *clks;
 	struct reset_control **resets;
 	struct regulator_bulk_data *vregs;
 
@@ -564,6 +599,8 @@ struct qcom_qmp {
 
 	struct mutex phy_mutex;
 	int init_count;
+	bool phy_initialized;
+	enum phy_mode mode;
 };
 
 static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@@ -595,6 +632,10 @@ static const char * const msm8996_phy_clk_l[] = {
 	"aux", "cfg_ahb", "ref",
 };
 
+static const char * const qmp_v3_phy_clk_l[] = {
+	"aux", "cfg_ahb", "ref", "com_aux",
+};
+
 /* list of resets */
 static const char * const msm8996_pciephy_reset_l[] = {
 	"phy", "common", "cfg",
@@ -701,6 +742,38 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
 	.pwrdn_delay_max	= 1005,		/* us */
 };
 
+static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
+	.type			= PHY_TYPE_USB3,
+	.nlanes			= 1,
+
+	.serdes_tbl		= qmp_v3_usb3_serdes_tbl,
+	.serdes_tbl_num		= ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+	.tx_tbl			= qmp_v3_usb3_tx_tbl,
+	.tx_tbl_num		= ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+	.rx_tbl			= qmp_v3_usb3_rx_tbl,
+	.rx_tbl_num		= ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+	.pcs_tbl		= qmp_v3_usb3_pcs_tbl,
+	.pcs_tbl_num		= ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+	.clk_list		= qmp_v3_phy_clk_l,
+	.num_clks		= ARRAY_SIZE(qmp_v3_phy_clk_l),
+	.reset_list		= msm8996_usb3phy_reset_l,
+	.num_resets		= ARRAY_SIZE(msm8996_usb3phy_reset_l),
+	.vreg_list		= msm8996_phy_vreg_l,
+	.num_vregs		= ARRAY_SIZE(msm8996_phy_vreg_l),
+	.regs			= qmp_v3_usb3phy_regs_layout,
+
+	.start_ctrl		= SERDES_START | PCS_START,
+	.pwrdn_ctrl		= SW_PWRDN,
+	.mask_pcs_ready		= PHYSTATUS,
+
+	.pwrdn_delay_min	= POWER_DOWN_DELAY_US_MIN,
+	.pwrdn_delay_max	= POWER_DOWN_DELAY_US_MAX,
+
+	.has_phy_dp_com_ctrl	= true,
+	.tx_b_lane_offset	= 0x400,
+	.rx_b_lane_offset	= 0x400,
+};
+
 static void qcom_qmp_phy_configure(void __iomem *base,
 				   const unsigned int *regs,
 				   const struct qmp_phy_init_tbl tbl[],
@@ -724,44 +797,20 @@ static int qcom_qmp_phy_poweron(struct phy *phy)
 {
 	struct qmp_phy *qphy = phy_get_drvdata(phy);
 	struct qcom_qmp *qmp = qphy->qmp;
-	int num = qmp->cfg->num_vregs;
 	int ret;
 
-	dev_vdbg(&phy->dev, "Powering on QMP phy\n");
-
-	/* turn on regulator supplies */
-	ret = regulator_bulk_enable(num, qmp->vregs);
-	if (ret) {
-		dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
-		return ret;
-	}
-
 	ret = clk_prepare_enable(qphy->pipe_clk);
-	if (ret) {
+	if (ret)
 		dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret);
-		regulator_bulk_disable(num, qmp->vregs);
-		return ret;
-	}
 
-	return 0;
-}
-
-static int qcom_qmp_phy_poweroff(struct phy *phy)
-{
-	struct qmp_phy *qphy = phy_get_drvdata(phy);
-	struct qcom_qmp *qmp = qphy->qmp;
-
-	clk_disable_unprepare(qphy->pipe_clk);
-
-	regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs);
-
-	return 0;
+	return ret;
 }
 
 static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
 {
 	const struct qmp_phy_cfg *cfg = qmp->cfg;
 	void __iomem *serdes = qmp->serdes;
+	void __iomem *dp_com = qmp->dp_com;
 	int ret, i;
 
 	mutex_lock(&qmp->phy_mutex);
@@ -770,7 +819,23 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
 		return 0;
 	}
 
+	/* turn on regulator supplies */
+	ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
+	if (ret) {
+		dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+		goto err_reg_enable;
+	}
+
 	for (i = 0; i < cfg->num_resets; i++) {
+		ret = reset_control_assert(qmp->resets[i]);
+		if (ret) {
+			dev_err(qmp->dev, "%s reset assert failed\n",
+				cfg->reset_list[i]);
+			goto err_rst_assert;
+		}
+	}
+
+	for (i = cfg->num_resets - 1; i >= 0; i--) {
 		ret = reset_control_deassert(qmp->resets[i]);
 		if (ret) {
 			dev_err(qmp->dev, "%s reset deassert failed\n",
@@ -779,10 +844,33 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
 		}
 	}
 
+	ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+	if (ret) {
+		dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
+		goto err_rst;
+	}
+
 	if (cfg->has_phy_com_ctrl)
 		qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
 			     SW_PWRDN);
 
+	if (cfg->has_phy_dp_com_ctrl) {
+		qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
+			     SW_PWRDN);
+		/* override hardware control for reset of qmp phy */
+		qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+			     SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+			     SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+
+		qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL,
+			     USB3_MODE | DP_MODE);
+
+		/* bring both QMP USB and QMP DP PHYs PCS block out of reset */
+		qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
+			     SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
+			     SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
+	}
+
 	/* Serdes configuration */
 	qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
 			       cfg->serdes_tbl_num);
@@ -803,7 +891,7 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
 		if (ret) {
 			dev_err(qmp->dev,
 				"phy common block init timed-out\n");
-			goto err_rst;
+			goto err_com_init;
 		}
 	}
 
@@ -811,9 +899,14 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
 
 	return 0;
 
+err_com_init:
+	clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
 err_rst:
-	while (--i >= 0)
+	while (++i < cfg->num_resets)
 		reset_control_assert(qmp->resets[i]);
+err_rst_assert:
+	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+err_reg_enable:
 	mutex_unlock(&qmp->phy_mutex);
 
 	return ret;
@@ -843,6 +936,10 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
 	while (--i >= 0)
 		reset_control_assert(qmp->resets[i]);
 
+	clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
+
 	mutex_unlock(&qmp->phy_mutex);
 
 	return 0;
@@ -857,24 +954,16 @@ static int qcom_qmp_phy_init(struct phy *phy)
 	void __iomem *tx = qphy->tx;
 	void __iomem *rx = qphy->rx;
 	void __iomem *pcs = qphy->pcs;
+	void __iomem *dp_com = qmp->dp_com;
 	void __iomem *status;
 	unsigned int mask, val;
-	int ret, i;
+	int ret;
 
 	dev_vdbg(qmp->dev, "Initializing QMP phy\n");
 
-	for (i = 0; i < qmp->cfg->num_clks; i++) {
-		ret = clk_prepare_enable(qmp->clks[i]);
-		if (ret) {
-			dev_err(qmp->dev, "failed to enable %s clk, err=%d\n",
-				qmp->cfg->clk_list[i], ret);
-			goto err_clk;
-		}
-	}
-
 	ret = qcom_qmp_phy_com_init(qmp);
 	if (ret)
-		goto err_clk;
+		return ret;
 
 	if (cfg->has_lane_rst) {
 		ret = reset_control_deassert(qphy->lane_rst);
@@ -887,7 +976,16 @@ static int qcom_qmp_phy_init(struct phy *phy)
 
 	/* Tx, Rx, and PCS configurations */
 	qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
+	/* Configuration for other LANE for USB-DP combo PHY */
+	if (cfg->has_phy_dp_com_ctrl)
+		qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
+				       cfg->tx_tbl, cfg->tx_tbl_num);
+
 	qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
+	if (cfg->has_phy_dp_com_ctrl)
+		qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
+				       cfg->rx_tbl, cfg->rx_tbl_num);
+
 	qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
 
 	/*
@@ -899,11 +997,13 @@ static int qcom_qmp_phy_init(struct phy *phy)
 	if (cfg->has_pwrdn_delay)
 		usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
 
-	/* start SerDes and Phy-Coding-Sublayer */
-	qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
-
 	/* Pull PHY out of reset state */
 	qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+	if (cfg->has_phy_dp_com_ctrl)
+		qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
+
+	/* start SerDes and Phy-Coding-Sublayer */
+	qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
 
 	status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
 	mask = cfg->mask_pcs_ready;
@@ -914,6 +1014,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
 		dev_err(qmp->dev, "phy initialization timed-out\n");
 		goto err_pcs_ready;
 	}
+	qmp->phy_initialized = true;
 
 	return ret;
 
@@ -922,9 +1023,6 @@ static int qcom_qmp_phy_init(struct phy *phy)
 		reset_control_assert(qphy->lane_rst);
 err_lane_rst:
 	qcom_qmp_phy_com_exit(qmp);
-err_clk:
-	while (--i >= 0)
-		clk_disable_unprepare(qmp->clks[i]);
 
 	return ret;
 }
@@ -934,7 +1032,8 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 	struct qmp_phy *qphy = phy_get_drvdata(phy);
 	struct qcom_qmp *qmp = qphy->qmp;
 	const struct qmp_phy_cfg *cfg = qmp->cfg;
-	int i = cfg->num_clks;
+
+	clk_disable_unprepare(qphy->pipe_clk);
 
 	/* PHY reset */
 	qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
@@ -950,8 +1049,127 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 
 	qcom_qmp_phy_com_exit(qmp);
 
-	while (--i >= 0)
-		clk_disable_unprepare(qmp->clks[i]);
+	qmp->phy_initialized = false;
+
+	return 0;
+}
+
+static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct qmp_phy *qphy = phy_get_drvdata(phy);
+	struct qcom_qmp *qmp = qphy->qmp;
+
+	qmp->mode = mode;
+
+	return 0;
+}
+
+static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy)
+{
+	struct qcom_qmp *qmp = qphy->qmp;
+	const struct qmp_phy_cfg *cfg = qmp->cfg;
+	void __iomem *pcs = qphy->pcs;
+	void __iomem *pcs_misc = qphy->pcs_misc;
+	u32 intr_mask;
+
+	if (qmp->mode == PHY_MODE_USB_HOST_SS ||
+	    qmp->mode == PHY_MODE_USB_DEVICE_SS)
+		intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
+	else
+		intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
+
+	/* Clear any pending interrupts status */
+	qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+	/* Writing 1 followed by 0 clears the interrupt */
+	qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+
+	qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+		     ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL);
+
+	/* Enable required PHY autonomous mode interrupts */
+	qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask);
+
+	/* Enable i/o clamp_n for autonomous mode */
+	if (pcs_misc)
+		qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+}
+
+static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy)
+{
+	struct qcom_qmp *qmp = qphy->qmp;
+	const struct qmp_phy_cfg *cfg = qmp->cfg;
+	void __iomem *pcs = qphy->pcs;
+	void __iomem *pcs_misc = qphy->pcs_misc;
+
+	/* Disable i/o clamp_n on resume for normal mode */
+	if (pcs_misc)
+		qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
+
+	qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
+		     ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN);
+
+	qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+	/* Writing 1 followed by 0 clears the interrupt */
+	qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
+}
+
+static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev)
+{
+	struct qcom_qmp *qmp = dev_get_drvdata(dev);
+	struct qmp_phy *qphy = qmp->phys[0];
+	const struct qmp_phy_cfg *cfg = qmp->cfg;
+
+	dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
+
+	/* Supported only for USB3 PHY */
+	if (cfg->type != PHY_TYPE_USB3)
+		return 0;
+
+	if (!qmp->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	qcom_qmp_phy_enable_autonomous_mode(qphy);
+
+	clk_disable_unprepare(qphy->pipe_clk);
+	clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+
+	return 0;
+}
+
+static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
+{
+	struct qcom_qmp *qmp = dev_get_drvdata(dev);
+	struct qmp_phy *qphy = qmp->phys[0];
+	const struct qmp_phy_cfg *cfg = qmp->cfg;
+	int ret = 0;
+
+	dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
+
+	/* Supported only for USB3 PHY */
+	if (cfg->type != PHY_TYPE_USB3)
+		return 0;
+
+	if (!qmp->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
+	if (ret) {
+		dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(qphy->pipe_clk);
+	if (ret) {
+		dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
+		clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
+		return ret;
+	}
+
+	qcom_qmp_phy_disable_autonomous_mode(qphy);
 
 	return 0;
 }
@@ -1000,29 +1218,17 @@ static int qcom_qmp_phy_reset_init(struct device *dev)
 static int qcom_qmp_phy_clk_init(struct device *dev)
 {
 	struct qcom_qmp *qmp = dev_get_drvdata(dev);
-	int ret, i;
+	int num = qmp->cfg->num_clks;
+	int i;
 
-	qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks,
-				 sizeof(*qmp->clks), GFP_KERNEL);
+	qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
 	if (!qmp->clks)
 		return -ENOMEM;
 
-	for (i = 0; i < qmp->cfg->num_clks; i++) {
-		struct clk *_clk;
-		const char *name = qmp->cfg->clk_list[i];
-
-		_clk = devm_clk_get(dev, name);
-		if (IS_ERR(_clk)) {
-			ret = PTR_ERR(_clk);
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to get %s clk, %d\n",
-					name, ret);
-			return ret;
-		}
-		qmp->clks[i] = _clk;
-	}
+	for (i = 0; i < num; i++)
+		qmp->clks[i].id = qmp->cfg->clk_list[i];
 
-	return 0;
+	return devm_clk_bulk_get(dev, num, qmp->clks);
 }
 
 /*
@@ -1078,7 +1284,7 @@ static const struct phy_ops qcom_qmp_phy_gen_ops = {
 	.init		= qcom_qmp_phy_init,
 	.exit		= qcom_qmp_phy_exit,
 	.power_on	= qcom_qmp_phy_poweron,
-	.power_off	= qcom_qmp_phy_poweroff,
+	.set_mode	= qcom_qmp_phy_set_mode,
 	.owner		= THIS_MODULE,
 };
 
@@ -1097,7 +1303,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
 
 	/*
 	 * Get memory resources for each phy lane:
-	 * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
+	 * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and
+	 * pcs_misc (optional) -> 3.
 	 */
 	qphy->tx = of_iomap(np, 0);
 	if (!qphy->tx)
@@ -1111,6 +1318,10 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
 	if (!qphy->pcs)
 		return -ENOMEM;
 
+	qphy->pcs_misc = of_iomap(np, 3);
+	if (!qphy->pcs_misc)
+		dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
+
 	/*
 	 * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3
 	 * based phys, so they essentially have pipe clock. So,
@@ -1169,11 +1380,19 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
 	}, {
 		.compatible = "qcom,ipq8074-qmp-pcie-phy",
 		.data = &ipq8074_pciephy_cfg,
+	}, {
+		.compatible = "qcom,qmp-v3-usb3-phy",
+		.data = &qmp_v3_usb3phy_cfg,
 	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
 
+static const struct dev_pm_ops qcom_qmp_phy_pm_ops = {
+	SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend,
+			   qcom_qmp_phy_runtime_resume, NULL)
+};
+
 static int qcom_qmp_phy_probe(struct platform_device *pdev)
 {
 	struct qcom_qmp *qmp;
@@ -1192,6 +1411,11 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 	qmp->dev = dev;
 	dev_set_drvdata(dev, qmp);
 
+	/* Get the specific init parameters of QMP phy */
+	qmp->cfg = of_device_get_match_data(dev);
+	if (!qmp->cfg)
+		return -EINVAL;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(base))
@@ -1200,10 +1424,18 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 	/* per PHY serdes; usually located at base address */
 	qmp->serdes = base;
 
-	mutex_init(&qmp->phy_mutex);
+	/* per PHY dp_com; if PHY has dp_com control block */
+	if (qmp->cfg->has_phy_dp_com_ctrl) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   "dp_com");
+		base = devm_ioremap_resource(dev, res);
+		if (IS_ERR(base))
+			return PTR_ERR(base);
 
-	/* Get the specific init parameters of QMP phy */
-	qmp->cfg = of_device_get_match_data(dev);
+		qmp->dp_com = base;
+	}
+
+	mutex_init(&qmp->phy_mutex);
 
 	ret = qcom_qmp_phy_clk_init(dev);
 	if (ret)
@@ -1229,12 +1461,21 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	id = 0;
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	/*
+	 * Prevent runtime pm from being ON by default. Users can enable
+	 * it using power/control in sysfs.
+	 */
+	pm_runtime_forbid(dev);
+
 	for_each_available_child_of_node(dev->of_node, child) {
 		/* Create per-lane phy */
 		ret = qcom_qmp_phy_create(dev, child, id);
 		if (ret) {
 			dev_err(dev, "failed to create lane%d phy, %d\n",
 				id, ret);
+			pm_runtime_disable(dev);
 			return ret;
 		}
 
@@ -1246,6 +1487,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 		if (ret) {
 			dev_err(qmp->dev,
 				"failed to register pipe clock source\n");
+			pm_runtime_disable(dev);
 			return ret;
 		}
 		id++;
@@ -1254,6 +1496,8 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 	if (!IS_ERR(phy_provider))
 		dev_info(dev, "Registered Qcom-QMP phy\n");
+	else
+		pm_runtime_disable(dev);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
@@ -1262,6 +1506,7 @@ static struct platform_driver qcom_qmp_phy_driver = {
 	.probe		= qcom_qmp_phy_probe,
 	.driver = {
 		.name	= "qcom-qmp-phy",
+		.pm	= &qcom_qmp_phy_pm_ops,
 		.of_match_table = qcom_qmp_phy_of_match_table,
 	},
 };
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1c6905d04399835862dc80e04831515b7483845
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.h
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef QCOM_PHY_QMP_H_
+#define QCOM_PHY_QMP_H_
+
+/* Only for QMP V2 PHY - QSERDES COM registers */
+#define QSERDES_COM_BG_TIMER				0x00c
+#define QSERDES_COM_SSC_EN_CENTER			0x010
+#define QSERDES_COM_SSC_ADJ_PER1			0x014
+#define QSERDES_COM_SSC_ADJ_PER2			0x018
+#define QSERDES_COM_SSC_PER1				0x01c
+#define QSERDES_COM_SSC_PER2				0x020
+#define QSERDES_COM_SSC_STEP_SIZE1			0x024
+#define QSERDES_COM_SSC_STEP_SIZE2			0x028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			0x034
+#define QSERDES_COM_CLK_ENABLE1				0x038
+#define QSERDES_COM_SYS_CLK_CTRL			0x03c
+#define QSERDES_COM_SYSCLK_BUF_ENABLE			0x040
+#define QSERDES_COM_PLL_IVCO				0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0			0x04c
+#define QSERDES_COM_LOCK_CMP2_MODE0			0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0			0x054
+#define QSERDES_COM_LOCK_CMP1_MODE1			0x058
+#define QSERDES_COM_LOCK_CMP2_MODE1			0x05c
+#define QSERDES_COM_LOCK_CMP3_MODE1			0x060
+#define QSERDES_COM_BG_TRIM				0x070
+#define QSERDES_COM_CLK_EP_DIV				0x074
+#define QSERDES_COM_CP_CTRL_MODE0			0x078
+#define QSERDES_COM_CP_CTRL_MODE1			0x07c
+#define QSERDES_COM_PLL_RCTRL_MODE0			0x084
+#define QSERDES_COM_PLL_RCTRL_MODE1			0x088
+#define QSERDES_COM_PLL_CCTRL_MODE0			0x090
+#define QSERDES_COM_PLL_CCTRL_MODE1			0x094
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM			0x0a8
+#define QSERDES_COM_SYSCLK_EN_SEL			0x0ac
+#define QSERDES_COM_RESETSM_CNTRL			0x0b4
+#define QSERDES_COM_RESTRIM_CTRL			0x0bc
+#define QSERDES_COM_RESCODE_DIV_NUM			0x0c4
+#define QSERDES_COM_LOCK_CMP_EN				0x0c8
+#define QSERDES_COM_LOCK_CMP_CFG			0x0cc
+#define QSERDES_COM_DEC_START_MODE0			0x0d0
+#define QSERDES_COM_DEC_START_MODE1			0x0d4
+#define QSERDES_COM_DIV_FRAC_START1_MODE0		0x0dc
+#define QSERDES_COM_DIV_FRAC_START2_MODE0		0x0e0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0		0x0e4
+#define QSERDES_COM_DIV_FRAC_START1_MODE1		0x0e8
+#define QSERDES_COM_DIV_FRAC_START2_MODE1		0x0ec
+#define QSERDES_COM_DIV_FRAC_START3_MODE1		0x0f0
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		0x10c
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1		0x110
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1		0x114
+#define QSERDES_COM_VCO_TUNE_CTRL			0x124
+#define QSERDES_COM_VCO_TUNE_MAP			0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0			0x12c
+#define QSERDES_COM_VCO_TUNE2_MODE0			0x130
+#define QSERDES_COM_VCO_TUNE1_MODE1			0x134
+#define QSERDES_COM_VCO_TUNE2_MODE1			0x138
+#define QSERDES_COM_VCO_TUNE_TIMER1			0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2			0x148
+#define QSERDES_COM_BG_CTRL				0x170
+#define QSERDES_COM_CLK_SELECT				0x174
+#define QSERDES_COM_HSCLK_SEL				0x178
+#define QSERDES_COM_CORECLK_DIV				0x184
+#define QSERDES_COM_CORE_CLK_EN				0x18c
+#define QSERDES_COM_C_READY_STATUS			0x190
+#define QSERDES_COM_CMN_CONFIG				0x194
+#define QSERDES_COM_SVS_MODE_CLK_SEL			0x19c
+#define QSERDES_COM_DEBUG_BUS0				0x1a0
+#define QSERDES_COM_DEBUG_BUS1				0x1a4
+#define QSERDES_COM_DEBUG_BUS2				0x1a8
+#define QSERDES_COM_DEBUG_BUS3				0x1ac
+#define QSERDES_COM_DEBUG_BUS_SEL			0x1b0
+#define QSERDES_COM_CORECLK_DIV_MODE1			0x1bc
+
+/* Only for QMP V2 PHY - TX registers */
+#define QSERDES_TX_RES_CODE_LANE_OFFSET			0x054
+#define QSERDES_TX_DEBUG_BUS_SEL			0x064
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN	0x068
+#define QSERDES_TX_LANE_MODE				0x094
+#define QSERDES_TX_RCV_DETECT_LVL_2			0x0ac
+
+/* Only for QMP V2 PHY - RX registers */
+#define QSERDES_RX_UCDR_SO_GAIN_HALF			0x010
+#define QSERDES_RX_UCDR_SO_GAIN				0x01c
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN		0x040
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE	0x048
+#define QSERDES_RX_RX_TERM_BW				0x090
+#define QSERDES_RX_RX_EQ_GAIN1_LSB			0x0c4
+#define QSERDES_RX_RX_EQ_GAIN1_MSB			0x0c8
+#define QSERDES_RX_RX_EQ_GAIN2_LSB			0x0cc
+#define QSERDES_RX_RX_EQ_GAIN2_MSB			0x0d0
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d8
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3		0x0dc
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4		0x0e0
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1		0x108
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x10c
+#define QSERDES_RX_SIGDET_ENABLES			0x110
+#define QSERDES_RX_SIGDET_CNTRL				0x114
+#define QSERDES_RX_SIGDET_LVL				0x118
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL		0x11c
+#define QSERDES_RX_RX_BAND				0x120
+#define QSERDES_RX_RX_INTERFACE_MODE			0x12c
+
+/* Only for QMP V2 PHY - PCS registers */
+#define QPHY_POWER_DOWN_CONTROL				0x04
+#define QPHY_TXDEEMPH_M6DB_V0				0x24
+#define QPHY_TXDEEMPH_M3P5DB_V0				0x28
+#define QPHY_ENDPOINT_REFCLK_DRIVE			0x54
+#define QPHY_RX_IDLE_DTCT_CNTRL				0x58
+#define QPHY_POWER_STATE_CONFIG1			0x60
+#define QPHY_POWER_STATE_CONFIG2			0x64
+#define QPHY_POWER_STATE_CONFIG4			0x6c
+#define QPHY_LOCK_DETECT_CONFIG1			0x80
+#define QPHY_LOCK_DETECT_CONFIG2			0x84
+#define QPHY_LOCK_DETECT_CONFIG3			0x88
+#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK		0xa0
+#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK			0xa4
+#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB		0x1A8
+#define QPHY_OSC_DTCT_ACTIONS				0x1AC
+#define QPHY_RX_SIGDET_LVL				0x1D8
+#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB		0x1DC
+#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB		0x1E0
+
+/* Only for QMP V3 PHY - DP COM registers */
+#define QPHY_V3_DP_COM_PHY_MODE_CTRL			0x00
+#define QPHY_V3_DP_COM_SW_RESET				0x04
+#define QPHY_V3_DP_COM_POWER_DOWN_CTRL			0x08
+#define QPHY_V3_DP_COM_SWI_CTRL				0x0c
+#define QPHY_V3_DP_COM_TYPEC_CTRL			0x10
+#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL			0x14
+#define QPHY_V3_DP_COM_RESET_OVRD_CTRL			0x1c
+
+/* Only for QMP V3 PHY - QSERDES COM registers */
+#define QSERDES_V3_COM_BG_TIMER				0x00c
+#define QSERDES_V3_COM_SSC_EN_CENTER			0x010
+#define QSERDES_V3_COM_SSC_ADJ_PER1			0x014
+#define QSERDES_V3_COM_SSC_ADJ_PER2			0x018
+#define QSERDES_V3_COM_SSC_PER1				0x01c
+#define QSERDES_V3_COM_SSC_PER2				0x020
+#define QSERDES_V3_COM_SSC_STEP_SIZE1			0x024
+#define QSERDES_V3_COM_SSC_STEP_SIZE2			0x028
+#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN		0x034
+#define QSERDES_V3_COM_CLK_ENABLE1			0x038
+#define QSERDES_V3_COM_SYS_CLK_CTRL			0x03c
+#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE		0x040
+#define QSERDES_V3_COM_PLL_IVCO				0x048
+#define QSERDES_V3_COM_LOCK_CMP1_MODE0			0x098
+#define QSERDES_V3_COM_LOCK_CMP2_MODE0			0x09c
+#define QSERDES_V3_COM_LOCK_CMP3_MODE0			0x0a0
+#define QSERDES_V3_COM_LOCK_CMP1_MODE1			0x0a4
+#define QSERDES_V3_COM_LOCK_CMP2_MODE1			0x0a8
+#define QSERDES_V3_COM_LOCK_CMP3_MODE1			0x0ac
+#define QSERDES_V3_COM_CLK_EP_DIV			0x05c
+#define QSERDES_V3_COM_CP_CTRL_MODE0			0x060
+#define QSERDES_V3_COM_CP_CTRL_MODE1			0x064
+#define QSERDES_V3_COM_PLL_RCTRL_MODE0			0x068
+#define QSERDES_V3_COM_PLL_RCTRL_MODE1			0x06c
+#define QSERDES_V3_COM_PLL_CCTRL_MODE0			0x070
+#define QSERDES_V3_COM_PLL_CCTRL_MODE1			0x074
+#define QSERDES_V3_COM_SYSCLK_EN_SEL			0x080
+#define QSERDES_V3_COM_RESETSM_CNTRL			0x088
+#define QSERDES_V3_COM_RESETSM_CNTRL2			0x08c
+#define QSERDES_V3_COM_LOCK_CMP_EN			0x090
+#define QSERDES_V3_COM_LOCK_CMP_CFG			0x094
+#define QSERDES_V3_COM_DEC_START_MODE0			0x0b0
+#define QSERDES_V3_COM_DEC_START_MODE1			0x0b4
+#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0		0x0b8
+#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0		0x0bc
+#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0		0x0c0
+#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1		0x0c4
+#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1		0x0c8
+#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1		0x0cc
+#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0		0x0d8
+#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0		0x0dc
+#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1		0x0e0
+#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1		0x0e4
+#define QSERDES_V3_COM_VCO_TUNE_CTRL			0x0ec
+#define QSERDES_V3_COM_VCO_TUNE_MAP			0x0f0
+#define QSERDES_V3_COM_VCO_TUNE1_MODE0			0x0f4
+#define QSERDES_V3_COM_VCO_TUNE2_MODE0			0x0f8
+#define QSERDES_V3_COM_VCO_TUNE1_MODE1			0x0fc
+#define QSERDES_V3_COM_VCO_TUNE2_MODE1			0x100
+#define QSERDES_V3_COM_VCO_TUNE_TIMER1			0x11c
+#define QSERDES_V3_COM_VCO_TUNE_TIMER2			0x120
+#define QSERDES_V3_COM_CLK_SELECT			0x138
+#define QSERDES_V3_COM_HSCLK_SEL			0x13c
+#define QSERDES_V3_COM_CORECLK_DIV_MODE0		0x148
+#define QSERDES_V3_COM_CORECLK_DIV_MODE1		0x14c
+#define QSERDES_V3_COM_CORE_CLK_EN			0x154
+#define QSERDES_V3_COM_C_READY_STATUS			0x158
+#define QSERDES_V3_COM_CMN_CONFIG			0x15c
+#define QSERDES_V3_COM_SVS_MODE_CLK_SEL			0x164
+#define QSERDES_V3_COM_DEBUG_BUS0			0x168
+#define QSERDES_V3_COM_DEBUG_BUS1			0x16c
+#define QSERDES_V3_COM_DEBUG_BUS2			0x170
+#define QSERDES_V3_COM_DEBUG_BUS3			0x174
+#define QSERDES_V3_COM_DEBUG_BUS_SEL			0x178
+
+/* Only for QMP V3 PHY - TX registers */
+#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX		0x044
+#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX		0x048
+#define QSERDES_V3_TX_DEBUG_BUS_SEL			0x058
+#define QSERDES_V3_TX_HIGHZ_DRVR_EN			0x060
+#define QSERDES_V3_TX_LANE_MODE_1			0x08c
+#define QSERDES_V3_TX_RCV_DETECT_LVL_2			0x0a4
+
+/* Only for QMP V3 PHY - RX registers */
+#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF			0x00c
+#define QSERDES_V3_RX_UCDR_SO_GAIN			0x014
+#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN		0x030
+#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE	0x034
+#define QSERDES_V3_RX_RX_TERM_BW			0x07c
+#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB			0x0c8
+#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB			0x0cc
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2		0x0d4
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3		0x0d8
+#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4		0x0dc
+#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1	0x0f8
+#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x0fc
+#define QSERDES_V3_RX_SIGDET_ENABLES			0x100
+#define QSERDES_V3_RX_SIGDET_CNTRL			0x104
+#define QSERDES_V3_RX_SIGDET_LVL			0x108
+#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL		0x10c
+#define QSERDES_V3_RX_RX_BAND				0x110
+#define QSERDES_V3_RX_RX_INTERFACE_MODE			0x11c
+
+/* Only for QMP V3 PHY - PCS registers */
+#define QPHY_V3_PCS_POWER_DOWN_CONTROL			0x004
+#define QPHY_V3_PCS_TXMGN_V0				0x00c
+#define QPHY_V3_PCS_TXMGN_V1				0x010
+#define QPHY_V3_PCS_TXMGN_V2				0x014
+#define QPHY_V3_PCS_TXMGN_V3				0x018
+#define QPHY_V3_PCS_TXMGN_V4				0x01c
+#define QPHY_V3_PCS_TXMGN_LS				0x020
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0			0x024
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0			0x028
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1			0x02c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1			0x030
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2			0x034
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2			0x038
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3			0x03c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3			0x040
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4			0x044
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4			0x048
+#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS			0x04c
+#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS			0x050
+#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE		0x054
+#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL			0x058
+#define QPHY_V3_PCS_RATE_SLEW_CNTRL			0x05c
+#define QPHY_V3_PCS_POWER_STATE_CONFIG1			0x060
+#define QPHY_V3_PCS_POWER_STATE_CONFIG2			0x064
+#define QPHY_V3_PCS_POWER_STATE_CONFIG4			0x06c
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L		0x070
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H		0x074
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L			0x078
+#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H			0x07c
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1			0x080
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2			0x084
+#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3			0x088
+#define QPHY_V3_PCS_TSYNC_RSYNC_TIME			0x08c
+#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK		0x0a0
+#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK		0x0a4
+#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK		0x0b0
+#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME		0x0b8
+#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME		0x0bc
+#define QPHY_V3_PCS_FLL_CNTRL1				0x0c4
+#define QPHY_V3_PCS_FLL_CNTRL2				0x0c8
+#define QPHY_V3_PCS_FLL_CNT_VAL_L			0x0cc
+#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL			0x0d0
+#define QPHY_V3_PCS_FLL_MAN_CODE			0x0d4
+#define QPHY_V3_PCS_RX_SIGDET_LVL			0x1d8
+
+/* Only for QMP V3 PHY - PCS_MISC registers */
+#define QPHY_V3_PCS_MISC_CLAMP_ENABLE			0x0c
+
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index 6c575244c0fb9b61e6ab185a670494dd4c40f2f0..94afeac1a19ef8b8dc8efa24b9f0049c238121e4 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
  */
 
 #include <linux/clk.h>
@@ -37,28 +29,57 @@
 #define QUSB2PHY_PLL_AUTOPGM_CTL1	0x1c
 #define QUSB2PHY_PLL_PWR_CTRL		0x18
 
-#define QUSB2PHY_PLL_STATUS		0x38
+/* QUSB2PHY_PLL_STATUS register bits */
 #define PLL_LOCKED			BIT(5)
 
-#define QUSB2PHY_PORT_TUNE1		0x80
-#define QUSB2PHY_PORT_TUNE2		0x84
-#define QUSB2PHY_PORT_TUNE3		0x88
-#define QUSB2PHY_PORT_TUNE4		0x8c
-#define QUSB2PHY_PORT_TUNE5		0x90
-#define QUSB2PHY_PORT_TEST2		0x9c
+/* QUSB2PHY_PLL_COMMON_STATUS_ONE register bits */
+#define CORE_READY_STATUS		BIT(0)
 
-#define QUSB2PHY_PORT_POWERDOWN		0xb4
+/* QUSB2PHY_PORT_POWERDOWN register bits */
 #define CLAMP_N_EN			BIT(5)
 #define FREEZIO_N			BIT(1)
 #define POWER_DOWN			BIT(0)
 
+/* QUSB2PHY_PWR_CTRL1 register bits */
+#define PWR_CTRL1_VREF_SUPPLY_TRIM	BIT(5)
+#define PWR_CTRL1_CLAMP_N_EN		BIT(1)
+
 #define QUSB2PHY_REFCLK_ENABLE		BIT(0)
 
 #define PHY_CLK_SCHEME_SEL		BIT(0)
 
+/* QUSB2PHY_INTR_CTRL register bits */
+#define DMSE_INTR_HIGH_SEL			BIT(4)
+#define DPSE_INTR_HIGH_SEL			BIT(3)
+#define CHG_DET_INTR_EN				BIT(2)
+#define DMSE_INTR_EN				BIT(1)
+#define DPSE_INTR_EN				BIT(0)
+
+/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */
+#define CORE_PLL_EN_FROM_RESET			BIT(4)
+#define CORE_RESET				BIT(5)
+#define CORE_RESET_MUX				BIT(6)
+
+#define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO	0x04
+#define QUSB2PHY_PLL_CLOCK_INVERTERS		0x18c
+#define QUSB2PHY_PLL_CMODE			0x2c
+#define QUSB2PHY_PLL_LOCK_DELAY			0x184
+#define QUSB2PHY_PLL_DIGITAL_TIMERS_TWO		0xb4
+#define QUSB2PHY_PLL_BIAS_CONTROL_1		0x194
+#define QUSB2PHY_PLL_BIAS_CONTROL_2		0x198
+#define QUSB2PHY_PWR_CTRL2			0x214
+#define QUSB2PHY_IMP_CTRL1			0x220
+#define QUSB2PHY_IMP_CTRL2			0x224
+#define QUSB2PHY_CHG_CTRL2			0x23c
+
 struct qusb2_phy_init_tbl {
 	unsigned int offset;
 	unsigned int val;
+	/*
+	 * register part of layout ?
+	 * if yes, then offset gives index in the reg-layout
+	 */
+	int in_layout;
 };
 
 #define QUSB2_PHY_INIT_CFG(o, v) \
@@ -67,30 +88,136 @@ struct qusb2_phy_init_tbl {
 		.val = v,	\
 	}
 
+#define QUSB2_PHY_INIT_CFG_L(o, v) \
+	{			\
+		.offset = o,	\
+		.val = v,	\
+		.in_layout = 1,	\
+	}
+
+/* set of registers with offsets different per-PHY */
+enum qusb2phy_reg_layout {
+	QUSB2PHY_PLL_CORE_INPUT_OVERRIDE,
+	QUSB2PHY_PLL_STATUS,
+	QUSB2PHY_PORT_TUNE1,
+	QUSB2PHY_PORT_TUNE2,
+	QUSB2PHY_PORT_TUNE3,
+	QUSB2PHY_PORT_TUNE4,
+	QUSB2PHY_PORT_TUNE5,
+	QUSB2PHY_PORT_TEST1,
+	QUSB2PHY_PORT_TEST2,
+	QUSB2PHY_PORT_POWERDOWN,
+	QUSB2PHY_INTR_CTRL,
+};
+
+static const unsigned int msm8996_regs_layout[] = {
+	[QUSB2PHY_PLL_STATUS]		= 0x38,
+	[QUSB2PHY_PORT_TUNE1]		= 0x80,
+	[QUSB2PHY_PORT_TUNE2]		= 0x84,
+	[QUSB2PHY_PORT_TUNE3]		= 0x88,
+	[QUSB2PHY_PORT_TUNE4]		= 0x8c,
+	[QUSB2PHY_PORT_TUNE5]		= 0x90,
+	[QUSB2PHY_PORT_TEST1]		= 0xb8,
+	[QUSB2PHY_PORT_TEST2]		= 0x9c,
+	[QUSB2PHY_PORT_POWERDOWN]	= 0xb4,
+	[QUSB2PHY_INTR_CTRL]		= 0xbc,
+};
+
 static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
-	QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8),
-	QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3),
-	QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
-	QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0),
+
 	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
 	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
 	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
-	QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
+
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
+
 	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
 	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
 };
 
+static const unsigned int qusb2_v2_regs_layout[] = {
+	[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
+	[QUSB2PHY_PLL_STATUS]		= 0x1a0,
+	[QUSB2PHY_PORT_TUNE1]		= 0x240,
+	[QUSB2PHY_PORT_TUNE2]		= 0x244,
+	[QUSB2PHY_PORT_TUNE3]		= 0x248,
+	[QUSB2PHY_PORT_TUNE4]		= 0x24c,
+	[QUSB2PHY_PORT_TUNE5]		= 0x250,
+	[QUSB2PHY_PORT_TEST1]		= 0x254,
+	[QUSB2PHY_PORT_TEST2]		= 0x258,
+	[QUSB2PHY_PORT_POWERDOWN]	= 0x210,
+	[QUSB2PHY_INTR_CTRL]		= 0x230,
+};
+
+static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_1, 0x40),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_2, 0x20),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_PWR_CTRL2, 0x21),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL1, 0x0),
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL2, 0x58),
+
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0x30),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x29),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0xca),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x04),
+	QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x03),
+
+	QUSB2_PHY_INIT_CFG(QUSB2PHY_CHG_CTRL2, 0x0),
+};
+
 struct qusb2_phy_cfg {
 	const struct qusb2_phy_init_tbl *tbl;
 	/* number of entries in the table */
 	unsigned int tbl_num;
 	/* offset to PHY_CLK_SCHEME register in TCSR map */
 	unsigned int clk_scheme_offset;
+
+	/* array of registers with different offsets */
+	const unsigned int *regs;
+	unsigned int mask_core_ready;
+	unsigned int disable_ctrl;
+	unsigned int autoresume_en;
+
+	/* true if PHY has PLL_TEST register to select clk_scheme */
+	bool has_pll_test;
+
+	/* true if TUNE1 register must be updated by fused value, else TUNE2 */
+	bool update_tune1_with_efuse;
+
+	/* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */
+	bool has_pll_override;
 };
 
 static const struct qusb2_phy_cfg msm8996_phy_cfg = {
-	.tbl = msm8996_init_tbl,
-	.tbl_num = ARRAY_SIZE(msm8996_init_tbl),
+	.tbl		= msm8996_init_tbl,
+	.tbl_num	= ARRAY_SIZE(msm8996_init_tbl),
+	.regs		= msm8996_regs_layout,
+
+	.has_pll_test	= true,
+	.disable_ctrl	= (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
+	.mask_core_ready = PLL_LOCKED,
+	.autoresume_en	 = BIT(3),
+};
+
+static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
+	.tbl		= qusb2_v2_init_tbl,
+	.tbl_num	= ARRAY_SIZE(qusb2_v2_init_tbl),
+	.regs		= qusb2_v2_regs_layout,
+
+	.disable_ctrl	= (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
+			   POWER_DOWN),
+	.mask_core_ready = CORE_READY_STATUS,
+	.has_pll_override = true,
+	.autoresume_en	  = BIT(0),
 };
 
 static const char * const qusb2_phy_vreg_names[] = {
@@ -116,6 +243,8 @@ static const char * const qusb2_phy_vreg_names[] = {
  *
  * @cfg: phy config data
  * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
+ * @phy_initialized: indicate if PHY has been initialized
+ * @mode: current PHY mode
  */
 struct qusb2_phy {
 	struct phy *phy;
@@ -132,6 +261,8 @@ struct qusb2_phy {
 
 	const struct qusb2_phy_cfg *cfg;
 	bool has_se_clk_scheme;
+	bool phy_initialized;
+	enum phy_mode mode;
 };
 
 static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
@@ -160,26 +291,32 @@ static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val)
 
 static inline
 void qcom_qusb2_phy_configure(void __iomem *base,
+			      const unsigned int *regs,
 			      const struct qusb2_phy_init_tbl tbl[], int num)
 {
 	int i;
 
-	for (i = 0; i < num; i++)
-		writel(tbl[i].val, base + tbl[i].offset);
+	for (i = 0; i < num; i++) {
+		if (tbl[i].in_layout)
+			writel(tbl[i].val, base + regs[tbl[i].offset]);
+		else
+			writel(tbl[i].val, base + tbl[i].offset);
+	}
 }
 
 /*
  * Fetches HS Tx tuning value from nvmem and sets the
- * QUSB2PHY_PORT_TUNE2 register.
+ * QUSB2PHY_PORT_TUNE1/2 register.
  * For error case, skip setting the value and use the default value.
  */
 static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
 {
 	struct device *dev = &qphy->phy->dev;
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
 	u8 *val;
 
 	/*
-	 * Read efuse register having TUNE2 parameter's high nibble.
+	 * Read efuse register having TUNE2/1 parameter's high nibble.
 	 * If efuse register shows value as 0x0, or if we fail to find
 	 * a valid efuse register settings, then use default value
 	 * as 0xB for high nibble that we have already set while
@@ -191,58 +328,169 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
 		return;
 	}
 
-	/* Fused TUNE2 value is the higher nibble only */
-	qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4);
+	/* Fused TUNE1/2 value is the higher nibble only */
+	if (cfg->update_tune1_with_efuse)
+		qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
+			      val[0] << 0x4);
+	else
+		qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
+			      val[0] << 0x4);
+
 }
 
-static int qusb2_phy_poweron(struct phy *phy)
+static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
 {
 	struct qusb2_phy *qphy = phy_get_drvdata(phy);
-	int num = ARRAY_SIZE(qphy->vregs);
+
+	qphy->mode = mode;
+
+	return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
+	u32 intr_mask;
+
+	dev_vdbg(dev, "Suspending QUSB2 Phy, mode:%d\n", qphy->mode);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	/*
+	 * Enable DP/DM interrupts to detect line state changes based on current
+	 * speed. In other words, enable the triggers _opposite_ of what the
+	 * current D+/D- levels are e.g. if currently D+ high, D- low
+	 * (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high
+	 */
+	intr_mask = DPSE_INTR_EN | DMSE_INTR_EN;
+	switch (qphy->mode) {
+	case PHY_MODE_USB_HOST_HS:
+	case PHY_MODE_USB_HOST_FS:
+	case PHY_MODE_USB_DEVICE_HS:
+	case PHY_MODE_USB_DEVICE_FS:
+		intr_mask |= DMSE_INTR_HIGH_SEL;
+		break;
+	case PHY_MODE_USB_HOST_LS:
+	case PHY_MODE_USB_DEVICE_LS:
+		intr_mask |= DPSE_INTR_HIGH_SEL;
+		break;
+	default:
+		/* No device connected, enable both DP/DM high interrupt */
+		intr_mask |= DMSE_INTR_HIGH_SEL;
+		intr_mask |= DPSE_INTR_HIGH_SEL;
+		break;
+	}
+
+	writel(intr_mask, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
+
+	/* hold core PLL into reset */
+	if (cfg->has_pll_override) {
+		qusb2_setbits(qphy->base,
+			      cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+			      CORE_PLL_EN_FROM_RESET | CORE_RESET |
+			      CORE_RESET_MUX);
+	}
+
+	/* enable phy auto-resume only if device is connected on bus */
+	if (qphy->mode != PHY_MODE_INVALID) {
+		qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+			      cfg->autoresume_en);
+		/* Autoresume bit has to be toggled in order to enable it */
+		qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+			      cfg->autoresume_en);
+	}
+
+	if (!qphy->has_se_clk_scheme)
+		clk_disable_unprepare(qphy->ref_clk);
+
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+	clk_disable_unprepare(qphy->iface_clk);
+
+	return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
 	int ret;
 
-	dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__);
+	dev_vdbg(dev, "Resuming QUSB2 phy, mode:%d\n", qphy->mode);
 
-	/* turn on regulator supplies */
-	ret = regulator_bulk_enable(num, qphy->vregs);
-	if (ret)
-		return ret;
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
 
 	ret = clk_prepare_enable(qphy->iface_clk);
 	if (ret) {
-		dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
-		regulator_bulk_disable(num, qphy->vregs);
+		dev_err(dev, "failed to enable iface_clk, %d\n", ret);
 		return ret;
 	}
 
-	return 0;
-}
+	ret = clk_prepare_enable(qphy->cfg_ahb_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable cfg ahb clock, %d\n", ret);
+		goto disable_iface_clk;
+	}
 
-static int qusb2_phy_poweroff(struct phy *phy)
-{
-	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+	if (!qphy->has_se_clk_scheme) {
+		clk_prepare_enable(qphy->ref_clk);
+		if (ret) {
+			dev_err(dev, "failed to enable ref clk, %d\n", ret);
+			goto disable_ahb_clk;
+		}
+	}
 
-	clk_disable_unprepare(qphy->iface_clk);
+	writel(0x0, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
 
-	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+	/* bring core PLL out of reset */
+	if (cfg->has_pll_override) {
+		qusb2_clrbits(qphy->base,
+			      cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+			      CORE_RESET | CORE_RESET_MUX);
+	}
 
 	return 0;
+
+disable_ahb_clk:
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+	clk_disable_unprepare(qphy->iface_clk);
+
+	return ret;
 }
 
 static int qusb2_phy_init(struct phy *phy)
 {
 	struct qusb2_phy *qphy = phy_get_drvdata(phy);
-	unsigned int val;
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
+	unsigned int val = 0;
 	unsigned int clk_scheme;
 	int ret;
 
 	dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
 
+	/* turn on regulator supplies */
+	ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(qphy->iface_clk);
+	if (ret) {
+		dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
+		goto poweroff_phy;
+	}
+
 	/* enable ahb interface clock to program phy */
 	ret = clk_prepare_enable(qphy->cfg_ahb_clk);
 	if (ret) {
 		dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
-		return ret;
+		goto disable_iface_clk;
 	}
 
 	/* Perform phy reset */
@@ -262,20 +510,23 @@ static int qusb2_phy_init(struct phy *phy)
 	}
 
 	/* Disable the PHY */
-	qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
-		      CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+	qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
+		      qphy->cfg->disable_ctrl);
 
-	/* save reset value to override reference clock scheme later */
-	val = readl(qphy->base + QUSB2PHY_PLL_TEST);
+	if (cfg->has_pll_test) {
+		/* save reset value to override reference clock scheme later */
+		val = readl(qphy->base + QUSB2PHY_PLL_TEST);
+	}
 
-	qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl,
-				 qphy->cfg->tbl_num);
+	qcom_qusb2_phy_configure(qphy->base, cfg->regs, cfg->tbl,
+				 cfg->tbl_num);
 
 	/* Set efuse value for tuning the PHY */
 	qusb2_phy_set_tune2_param(qphy);
 
 	/* Enable the PHY */
-	qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
+	qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
+		      POWER_DOWN);
 
 	/* Required to get phy pll lock successfully */
 	usleep_range(150, 160);
@@ -308,32 +559,37 @@ static int qusb2_phy_init(struct phy *phy)
 	}
 
 	if (!qphy->has_se_clk_scheme) {
-		val &= ~CLK_REF_SEL;
 		ret = clk_prepare_enable(qphy->ref_clk);
 		if (ret) {
 			dev_err(&phy->dev, "failed to enable ref clk, %d\n",
 				ret);
 			goto assert_phy_reset;
 		}
-	} else {
-		val |= CLK_REF_SEL;
 	}
 
-	writel(val, qphy->base + QUSB2PHY_PLL_TEST);
+	if (cfg->has_pll_test) {
+		if (!qphy->has_se_clk_scheme)
+			val &= ~CLK_REF_SEL;
+		else
+			val |= CLK_REF_SEL;
 
-	/* ensure above write is through */
-	readl(qphy->base + QUSB2PHY_PLL_TEST);
+		writel(val, qphy->base + QUSB2PHY_PLL_TEST);
+
+		/* ensure above write is through */
+		readl(qphy->base + QUSB2PHY_PLL_TEST);
+	}
 
 	/* Required to get phy pll lock successfully */
 	usleep_range(100, 110);
 
-	val = readb(qphy->base + QUSB2PHY_PLL_STATUS);
-	if (!(val & PLL_LOCKED)) {
+	val = readb(qphy->base + cfg->regs[QUSB2PHY_PLL_STATUS]);
+	if (!(val & cfg->mask_core_ready)) {
 		dev_err(&phy->dev,
 			"QUSB2PHY pll lock failed: status reg = %x\n", val);
 		ret = -EBUSY;
 		goto disable_ref_clk;
 	}
+	qphy->phy_initialized = true;
 
 	return 0;
 
@@ -344,6 +600,11 @@ static int qusb2_phy_init(struct phy *phy)
 	reset_control_assert(qphy->phy_reset);
 disable_ahb_clk:
 	clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+	clk_disable_unprepare(qphy->iface_clk);
+poweroff_phy:
+	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+
 	return ret;
 }
 
@@ -352,8 +613,8 @@ static int qusb2_phy_exit(struct phy *phy)
 	struct qusb2_phy *qphy = phy_get_drvdata(phy);
 
 	/* Disable the PHY */
-	qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
-		      CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
+	qusb2_setbits(qphy->base, qphy->cfg->regs[QUSB2PHY_PORT_POWERDOWN],
+		      qphy->cfg->disable_ctrl);
 
 	if (!qphy->has_se_clk_scheme)
 		clk_disable_unprepare(qphy->ref_clk);
@@ -361,6 +622,11 @@ static int qusb2_phy_exit(struct phy *phy)
 	reset_control_assert(qphy->phy_reset);
 
 	clk_disable_unprepare(qphy->cfg_ahb_clk);
+	clk_disable_unprepare(qphy->iface_clk);
+
+	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+
+	qphy->phy_initialized = false;
 
 	return 0;
 }
@@ -368,8 +634,7 @@ static int qusb2_phy_exit(struct phy *phy)
 static const struct phy_ops qusb2_phy_gen_ops = {
 	.init		= qusb2_phy_init,
 	.exit		= qusb2_phy_exit,
-	.power_on	= qusb2_phy_poweron,
-	.power_off	= qusb2_phy_poweroff,
+	.set_mode	= qusb2_phy_set_mode,
 	.owner		= THIS_MODULE,
 };
 
@@ -377,11 +642,19 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
 	{
 		.compatible	= "qcom,msm8996-qusb2-phy",
 		.data		= &msm8996_phy_cfg,
+	}, {
+		.compatible	= "qcom,qusb2-v2-phy",
+		.data		= &qusb2_v2_phy_cfg,
 	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
 
+static const struct dev_pm_ops qusb2_phy_pm_ops = {
+	SET_RUNTIME_PM_OPS(qusb2_phy_runtime_suspend,
+			   qusb2_phy_runtime_resume, NULL)
+};
+
 static int qusb2_phy_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -459,11 +732,19 @@ static int qusb2_phy_probe(struct platform_device *pdev)
 		qphy->cell = NULL;
 		dev_dbg(dev, "failed to lookup tune2 hstx trim value\n");
 	}
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	/*
+	 * Prevent runtime pm from being ON by default. Users can enable
+	 * it using power/control in sysfs.
+	 */
+	pm_runtime_forbid(dev);
 
 	generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
 	if (IS_ERR(generic_phy)) {
 		ret = PTR_ERR(generic_phy);
 		dev_err(dev, "failed to create phy, %d\n", ret);
+		pm_runtime_disable(dev);
 		return ret;
 	}
 	qphy->phy = generic_phy;
@@ -474,6 +755,8 @@ static int qusb2_phy_probe(struct platform_device *pdev)
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 	if (!IS_ERR(phy_provider))
 		dev_info(dev, "Registered Qcom-QUSB2 phy\n");
+	else
+		pm_runtime_disable(dev);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
@@ -482,6 +765,7 @@ static struct platform_driver qusb2_phy_driver = {
 	.probe		= qusb2_phy_probe,
 	.driver = {
 		.name	= "qcom-qusb2-phy",
+		.pm	= &qusb2_phy_pm_ops,
 		.of_match_table = qusb2_phy_of_match_table,
 	},
 };
diff --git a/drivers/phy/ralink/Kconfig b/drivers/phy/ralink/Kconfig
index b17635b407bc1dab9128c2c9d6bd74dd7a603d72..14fd219535efa43c9f784c668361632b32c26e5f 100644
--- a/drivers/phy/ralink/Kconfig
+++ b/drivers/phy/ralink/Kconfig
@@ -4,6 +4,7 @@
 config PHY_RALINK_USB
 	tristate "Ralink USB PHY driver"
 	depends on RALINK || COMPILE_TEST
+	depends on HAS_IOMEM
 	select GENERIC_PHY
 	select MFD_SYSCON
 	help
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index 9c90e7d67e0a5012813eef62249f73c2923c8a21..fb8f05e39cf7f359f5c04c5c12db300517102d92 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -396,6 +396,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
 		.compatible = "renesas,usb2-phy-r8a7796",
 		.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
 	},
+	{
+		.compatible = "renesas,usb2-phy-r8a77965",
+		.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
+	},
 	{
 		.compatible = "renesas,rcar-gen3-usb2-phy",
 	},
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index f5325b2b679ef0a8e03c493b88d16e4a8daa5f2e..0e15119ddfc68d0810abb93f933390fbb1a8cd4b 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -29,6 +29,7 @@ config PHY_ROCKCHIP_INNO_USB2
 config PHY_ROCKCHIP_PCIE
 	tristate "Rockchip PCIe PHY Driver"
 	depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
+	depends on HAS_IOMEM
 	select GENERIC_PHY
 	select MFD_SYSCON
 	help
diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c
index f1b24f18e9b22d1e1071f0f870724e08b0d0c19e..b237360f95f61a4a703d2db7175f9ad952a8b871 100644
--- a/drivers/phy/rockchip/phy-rockchip-emmc.c
+++ b/drivers/phy/rockchip/phy-rockchip-emmc.c
@@ -76,6 +76,13 @@
 #define PHYCTRL_OTAPDLYSEL_MASK		0xf
 #define PHYCTRL_OTAPDLYSEL_SHIFT	0x7
 
+#define PHYCTRL_IS_CALDONE(x) \
+	((((x) >> PHYCTRL_CALDONE_SHIFT) & \
+	  PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)
+#define PHYCTRL_IS_DLLRDY(x) \
+	((((x) >> PHYCTRL_DLLRDY_SHIFT) & \
+	  PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE)
+
 struct rockchip_emmc_phy {
 	unsigned int	reg_offset;
 	struct regmap	*reg_base;
@@ -89,7 +96,7 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 	unsigned int dllrdy;
 	unsigned int freqsel = PHYCTRL_FREQSEL_200M;
 	unsigned long rate;
-	unsigned long timeout;
+	int ret;
 
 	/*
 	 * Keep phyctrl_pdb and phyctrl_endll low to allow
@@ -160,17 +167,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 				   PHYCTRL_PDB_SHIFT));
 
 	/*
-	 * According to the user manual, it asks driver to
-	 * wait 5us for calpad busy trimming
+	 * According to the user manual, it asks driver to wait 5us for
+	 * calpad busy trimming. However it is documented that this value is
+	 * PVT(A.K.A process,voltage and temperature) relevant, so some
+	 * failure cases are found which indicates we should be more tolerant
+	 * to calpad busy trimming.
 	 */
-	udelay(5);
-	regmap_read(rk_phy->reg_base,
-		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
-		    &caldone);
-	caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
-	if (caldone != PHYCTRL_CALDONE_DONE) {
-		pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
-		return -ETIMEDOUT;
+	ret = regmap_read_poll_timeout(rk_phy->reg_base,
+				       rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+				       caldone, PHYCTRL_IS_CALDONE(caldone),
+				       0, 50);
+	if (ret) {
+		pr_err("%s: caldone failed, ret=%d\n", __func__, ret);
+		return ret;
 	}
 
 	/* Set the frequency of the DLL operation */
@@ -210,28 +219,15 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 	 * NOTE: There appear to be corner cases where the DLL seems to take
 	 * extra long to lock for reasons that aren't understood.  In some
 	 * extreme cases we've seen it take up to over 10ms (!).  We'll be
-	 * generous and give it 50ms.  We still busy wait here because:
-	 * - In most cases it should be super fast.
-	 * - This is not called lots during normal operation so it shouldn't
-	 *   be a power or performance problem to busy wait.  We expect it
-	 *   only at boot / resume.  In both cases, eMMC is probably on the
-	 *   critical path so busy waiting a little extra time should be OK.
+	 * generous and give it 50ms.
 	 */
-	timeout = jiffies + msecs_to_jiffies(50);
-	do {
-		udelay(1);
-
-		regmap_read(rk_phy->reg_base,
-			rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
-			&dllrdy);
-		dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
-		if (dllrdy == PHYCTRL_DLLRDY_DONE)
-			break;
-	} while (!time_after(jiffies, timeout));
-
-	if (dllrdy != PHYCTRL_DLLRDY_DONE) {
-		pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
-		return -ETIMEDOUT;
+	ret = regmap_read_poll_timeout(rk_phy->reg_base,
+				       rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+				       dllrdy, PHYCTRL_IS_DLLRDY(dllrdy),
+				       0, 50 * USEC_PER_MSEC);
+	if (ret) {
+		pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret);
+		return ret;
 	}
 
 	return 0;
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 7492c8978217f45e04f591898bfb0ddbaac2a217..76a4b58ec77172873e53e50abc3a6a02efeb1a9c 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -355,11 +355,26 @@ struct usb3phy_reg {
 	u32 write_enable;
 };
 
+/**
+ * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
+ * @reg: the base address for usb3-phy config.
+ * @typec_conn_dir: the register of type-c connector direction.
+ * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
+ * @external_psm: the register of type-c phy external psm clock.
+ * @pipe_status: the register of type-c phy pipe status.
+ * @usb3_host_disable: the register of type-c usb3 host disable.
+ * @usb3_host_port: the register of type-c usb3 host port.
+ * @uphy_dp_sel: the register of type-c phy DP select control.
+ */
 struct rockchip_usb3phy_port_cfg {
+	unsigned int reg;
 	struct usb3phy_reg typec_conn_dir;
 	struct usb3phy_reg usb3tousb2_en;
 	struct usb3phy_reg external_psm;
 	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg usb3_host_disable;
+	struct usb3phy_reg usb3_host_port;
+	struct usb3phy_reg uphy_dp_sel;
 };
 
 struct rockchip_typec_phy {
@@ -372,7 +387,7 @@ struct rockchip_typec_phy {
 	struct reset_control *uphy_rst;
 	struct reset_control *pipe_rst;
 	struct reset_control *tcphy_rst;
-	struct rockchip_usb3phy_port_cfg port_cfgs;
+	const struct rockchip_usb3phy_port_cfg *port_cfgs;
 	/* mutex to protect access to individual PHYs */
 	struct mutex lock;
 
@@ -424,6 +439,30 @@ struct phy_reg dp_pll_cfg[] = {
 	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
 };
 
+static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
+	{
+		.reg = 0xff7c0000,
+		.typec_conn_dir	= { 0xe580, 0, 16 },
+		.usb3tousb2_en	= { 0xe580, 3, 19 },
+		.external_psm	= { 0xe588, 14, 30 },
+		.pipe_status	= { 0xe5c0, 0, 0 },
+		.usb3_host_disable = { 0x2434, 0, 16 },
+		.usb3_host_port = { 0x2434, 12, 28 },
+		.uphy_dp_sel	= { 0x6268, 19, 19 },
+	},
+	{
+		.reg = 0xff800000,
+		.typec_conn_dir	= { 0xe58c, 0, 16 },
+		.usb3tousb2_en	= { 0xe58c, 3, 19 },
+		.external_psm	= { 0xe594, 14, 30 },
+		.pipe_status	= { 0xe5c0, 16, 16 },
+		.usb3_host_disable = { 0x2444, 0, 16 },
+		.usb3_host_port = { 0x2444, 12, 28 },
+		.uphy_dp_sel	= { 0x6268, 3, 19 },
+	},
+	{ /* sentinel */ }
+};
+
 static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 {
 	u32 i, rdata;
@@ -691,7 +730,7 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
 
 static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 {
-	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
 	int ret, i;
 	u32 val;
 
@@ -782,6 +821,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	u8 mode;
 	int ret;
 
+	if (!edev)
+		return MODE_DFP_USB;
+
 	ufp = extcon_get_state(edev, EXTCON_USB);
 	dp = extcon_get_state(edev, EXTCON_DISP_DP);
 
@@ -818,10 +860,22 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	return mode;
 }
 
+static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
+				       bool value)
+{
+	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
+
+	property_enable(tcphy, &cfg->usb3tousb2_en, value);
+	property_enable(tcphy, &cfg->usb3_host_disable, value);
+	property_enable(tcphy, &cfg->usb3_host_port, !value);
+
+	return 0;
+}
+
 static int rockchip_usb3_phy_power_on(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
-	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
 	const struct usb3phy_reg *reg = &cfg->pipe_status;
 	int timeout, new_mode, ret = 0;
 	u32 val;
@@ -835,8 +889,10 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
 	}
 
 	/* DP-only mode; fall back to USB2 */
-	if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB)))
+	if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) {
+		tcphy_cfg_usb3_to_usb2_only(tcphy, true);
 		goto unlock_ret;
+	}
 
 	if (tcphy->mode == new_mode)
 		goto unlock_ret;
@@ -852,6 +908,9 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
 		regmap_read(tcphy->grf_regs, reg->offset, &val);
 		if (!(val & BIT(reg->enable_bit))) {
 			tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB);
+
+			/* enable usb3 host */
+			tcphy_cfg_usb3_to_usb2_only(tcphy, false);
 			goto unlock_ret;
 		}
 		usleep_range(10, 20);
@@ -872,6 +931,7 @@ static int rockchip_usb3_phy_power_off(struct phy *phy)
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
 
 	mutex_lock(&tcphy->lock);
+	tcphy_cfg_usb3_to_usb2_only(tcphy, false);
 
 	if (tcphy->mode == MODE_DISCONNECT)
 		goto unlock;
@@ -894,6 +954,7 @@ static const struct phy_ops rockchip_usb3_phy_ops = {
 static int rockchip_dp_phy_power_on(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
 	int new_mode, ret = 0;
 	u32 val;
 
@@ -926,6 +987,8 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 	if (ret)
 		goto unlock_ret;
 
+	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
+
 	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
 				 val, val & DP_MODE_A2, 1000,
 				 PHY_MODE_SET_TIMEOUT);
@@ -984,51 +1047,9 @@ static const struct phy_ops rockchip_dp_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
-static int tcphy_get_param(struct device *dev,
-			   struct usb3phy_reg *reg,
-			   const char *name)
-{
-	u32 buffer[3];
-	int ret;
-
-	ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
-	if (ret) {
-		dev_err(dev, "Can not parse %s\n", name);
-		return ret;
-	}
-
-	reg->offset = buffer[0];
-	reg->enable_bit = buffer[1];
-	reg->write_enable = buffer[2];
-	return 0;
-}
-
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
 {
-	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
-	int ret;
-
-	ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
-			      "rockchip,typec-conn-dir");
-	if (ret)
-		return ret;
-
-	ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
-			      "rockchip,usb3tousb2-en");
-	if (ret)
-		return ret;
-
-	ret = tcphy_get_param(dev, &cfg->external_psm,
-			      "rockchip,external-psm");
-	if (ret)
-		return ret;
-
-	ret = tcphy_get_param(dev, &cfg->pipe_status,
-			      "rockchip,pipe-status");
-	if (ret)
-		return ret;
-
 	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
 							  "rockchip,grf");
 	if (IS_ERR(tcphy->grf_regs)) {
@@ -1071,7 +1092,7 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 
 static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
 {
-	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
 
 	reset_control_assert(tcphy->tcphy_rst);
 	reset_control_assert(tcphy->uphy_rst);
@@ -1092,17 +1113,43 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 	struct rockchip_typec_phy *tcphy;
 	struct phy_provider *phy_provider;
 	struct resource *res;
-	int ret;
+	const struct rockchip_usb3phy_port_cfg *phy_cfgs;
+	const struct of_device_id *match;
+	int index, ret;
 
 	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
 	if (!tcphy)
 		return -ENOMEM;
 
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "phy configs are not assigned!\n");
+		return -EINVAL;
+	}
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	tcphy->base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(tcphy->base))
 		return PTR_ERR(tcphy->base);
 
+	phy_cfgs = match->data;
+	/* find out a proper config which can be matched with dt. */
+	index = 0;
+	while (phy_cfgs[index].reg) {
+		if (phy_cfgs[index].reg == res->start) {
+			tcphy->port_cfgs = &phy_cfgs[index];
+			break;
+		}
+
+		++index;
+	}
+
+	if (!tcphy->port_cfgs) {
+		dev_err(dev, "no phy-config can be matched with %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
 	ret = tcphy_parse_dt(tcphy, dev);
 	if (ret)
 		return ret;
@@ -1115,9 +1162,13 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
 
 	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
 	if (IS_ERR(tcphy->extcon)) {
-		if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
-			dev_err(dev, "Invalid or missing extcon\n");
-		return PTR_ERR(tcphy->extcon);
+		if (PTR_ERR(tcphy->extcon) == -ENODEV) {
+			tcphy->extcon = NULL;
+		} else {
+			if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+				dev_err(dev, "Invalid or missing extcon\n");
+			return PTR_ERR(tcphy->extcon);
+		}
 	}
 
 	pm_runtime_enable(dev);
@@ -1162,8 +1213,11 @@ static int rockchip_typec_phy_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
-	{ .compatible = "rockchip,rk3399-typec-phy" },
-	{}
+	{
+		.compatible = "rockchip,rk3399-typec-phy",
+		.data = &rk3399_usb3phy_port_cfgs
+	},
+	{ /* sentinel */ }
 };
 
 MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig
index b7e0645a7bd9234e4e08aa09e9686a851c9f607e..2a5d33cb0e7eccef5fe1b8a41682812c15910964 100644
--- a/drivers/phy/samsung/Kconfig
+++ b/drivers/phy/samsung/Kconfig
@@ -49,7 +49,7 @@ config PHY_EXYNOS4210_USB2
 config PHY_EXYNOS4X12_USB2
 	bool
 	depends on PHY_SAMSUNG_USB2
-	default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
+	default SOC_EXYNOS3250 || SOC_EXYNOS4412
 
 config PHY_EXYNOS5250_USB2
 	bool
diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig
index 0814d3f87ec6d22decf7a88db2cfd9e11be2569b..609719bdfa50de4daa2a8b60fbbca4d831379370 100644
--- a/drivers/phy/st/Kconfig
+++ b/drivers/phy/st/Kconfig
@@ -31,3 +31,17 @@ config PHY_STIH407_USB
 	help
 	  Enable this support to enable the picoPHY device used by USB2
 	  and USB3 controllers on STMicroelectronics STiH407 SoC families.
+
+config PHY_STM32_USBPHYC
+	tristate "STMicroelectronics STM32 USB HS PHY Controller driver"
+	depends on ARCH_STM32 || COMPILE_TEST
+	select GENERIC_PHY
+	help
+	  Enable this to support the High-Speed USB transceivers that are part
+	  of some STMicroelectronics STM32 SoCs.
+
+	  This driver controls the entire USB PHY block: the USB PHY controller
+	  (USBPHYC) and the two 8-bit wide UTMI+ interfaces. First interface is
+	  used by an HS USB Host controller, and the second one is shared
+	  between an HS USB OTG controller and an HS USB Host controller,
+	  selected by a USB switch.
diff --git a/drivers/phy/st/Makefile b/drivers/phy/st/Makefile
index e2adfe2166d276c61b8b780d25427f743adb2cf4..c0091ad1fd48bab968df911f65cb6ac5a6816b2a 100644
--- a/drivers/phy/st/Makefile
+++ b/drivers/phy/st/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_MIPHY28LP) 		+= phy-miphy28lp.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_STIH407_USB)		+= phy-stih407-usb.o
+obj-$(CONFIG_PHY_STM32_USBPHYC) 	+= phy-stm32-usbphyc.o
diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c
new file mode 100644
index 0000000000000000000000000000000000000000..bc4e78a19c913dc99a37db355466853443965e12
--- /dev/null
+++ b/drivers/phy/st/phy-stm32-usbphyc.c
@@ -0,0 +1,461 @@
+// SPDX-Licence-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STM32 USB PHY Controller driver
+ *
+ * Copyright (C) 2018 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+
+#define STM32_USBPHYC_PLL	0x0
+#define STM32_USBPHYC_MISC	0x8
+#define STM32_USBPHYC_VERSION	0x3F4
+
+/* STM32_USBPHYC_PLL bit fields */
+#define PLLNDIV			GENMASK(6, 0)
+#define PLLFRACIN		GENMASK(25, 10)
+#define PLLEN			BIT(26)
+#define PLLSTRB			BIT(27)
+#define PLLSTRBYP		BIT(28)
+#define PLLFRACCTL		BIT(29)
+#define PLLDITHEN0		BIT(30)
+#define PLLDITHEN1		BIT(31)
+
+/* STM32_USBPHYC_MISC bit fields */
+#define SWITHOST		BIT(0)
+
+/* STM32_USBPHYC_VERSION bit fields */
+#define MINREV			GENMASK(3, 0)
+#define MAJREV			GENMASK(7, 4)
+
+static const char * const supplies_names[] = {
+	"vdda1v1",	/* 1V1 */
+	"vdda1v8",	/* 1V8 */
+};
+
+#define NUM_SUPPLIES		ARRAY_SIZE(supplies_names)
+
+#define PLL_LOCK_TIME_US	100
+#define PLL_PWR_DOWN_TIME_US	5
+#define PLL_FVCO_MHZ		2880
+#define PLL_INFF_MIN_RATE_HZ	19200000
+#define PLL_INFF_MAX_RATE_HZ	38400000
+#define HZ_PER_MHZ		1000000L
+
+struct pll_params {
+	u8 ndiv;
+	u16 frac;
+};
+
+struct stm32_usbphyc_phy {
+	struct phy *phy;
+	struct stm32_usbphyc *usbphyc;
+	struct regulator_bulk_data supplies[NUM_SUPPLIES];
+	u32 index;
+	bool active;
+};
+
+struct stm32_usbphyc {
+	struct device *dev;
+	void __iomem *base;
+	struct clk *clk;
+	struct reset_control *rst;
+	struct stm32_usbphyc_phy **phys;
+	int nphys;
+	int switch_setup;
+	bool pll_enabled;
+};
+
+static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
+{
+	writel_relaxed(readl_relaxed(reg) | bits, reg);
+}
+
+static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
+{
+	writel_relaxed(readl_relaxed(reg) & ~bits, reg);
+}
+
+static void stm32_usbphyc_get_pll_params(u32 clk_rate, struct pll_params *pll_params)
+{
+	unsigned long long fvco, ndiv, frac;
+
+	/*    _
+	 *   | FVCO = INFF*2*(NDIV + FRACT/2^16) when DITHER_DISABLE[1] = 1
+	 *   | FVCO = 2880MHz
+	 *  <
+	 *   | NDIV = integer part of input bits to set the LDF
+	 *   |_FRACT = fractional part of input bits to set the LDF
+	 *  =>	PLLNDIV = integer part of (FVCO / (INFF*2))
+	 *  =>	PLLFRACIN = fractional part of(FVCO / INFF*2) * 2^16
+	 * <=>  PLLFRACIN = ((FVCO / (INFF*2)) - PLLNDIV) * 2^16
+	 */
+	fvco = (unsigned long long)PLL_FVCO_MHZ * HZ_PER_MHZ;
+
+	ndiv = fvco;
+	do_div(ndiv, (clk_rate * 2));
+	pll_params->ndiv = (u8)ndiv;
+
+	frac = fvco * (1 << 16);
+	do_div(frac, (clk_rate * 2));
+	frac = frac - (ndiv * (1 << 16));
+	pll_params->frac = (u16)frac;
+}
+
+static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
+{
+	struct pll_params pll_params;
+	u32 clk_rate = clk_get_rate(usbphyc->clk);
+	u32 ndiv, frac;
+	u32 usbphyc_pll;
+
+	if ((clk_rate < PLL_INFF_MIN_RATE_HZ) ||
+	    (clk_rate > PLL_INFF_MAX_RATE_HZ)) {
+		dev_err(usbphyc->dev, "input clk freq (%dHz) out of range\n",
+			clk_rate);
+		return -EINVAL;
+	}
+
+	stm32_usbphyc_get_pll_params(clk_rate, &pll_params);
+	ndiv = FIELD_PREP(PLLNDIV, pll_params.ndiv);
+	frac = FIELD_PREP(PLLFRACIN, pll_params.frac);
+
+	usbphyc_pll = PLLDITHEN1 | PLLDITHEN0 | PLLSTRBYP | ndiv;
+
+	if (pll_params.frac)
+		usbphyc_pll |= PLLFRACCTL | frac;
+
+	writel_relaxed(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
+
+	dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
+		clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
+		FIELD_GET(PLLFRACIN, usbphyc_pll));
+
+	return 0;
+}
+
+static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
+{
+	int i;
+
+	for (i = 0; i < usbphyc->nphys; i++)
+		if (usbphyc->phys[i]->active)
+			return true;
+
+	return false;
+}
+
+static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
+{
+	void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+	bool pllen = (readl_relaxed(pll_reg) & PLLEN);
+	int ret;
+
+	/* Check if one phy port has already configured the pll */
+	if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
+		return 0;
+
+	if (pllen) {
+		stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+		/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+		udelay(PLL_PWR_DOWN_TIME_US);
+	}
+
+	ret = stm32_usbphyc_pll_init(usbphyc);
+	if (ret)
+		return ret;
+
+	stm32_usbphyc_set_bits(pll_reg, PLLEN);
+
+	/* Wait for maximum lock time */
+	udelay(PLL_LOCK_TIME_US);
+
+	if (!(readl_relaxed(pll_reg) & PLLEN)) {
+		dev_err(usbphyc->dev, "PLLEN not set\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
+{
+	void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+
+	/* Check if other phy port active */
+	if (stm32_usbphyc_has_one_phy_active(usbphyc))
+		return 0;
+
+	stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+	/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+	udelay(PLL_PWR_DOWN_TIME_US);
+
+	if (readl_relaxed(pll_reg) & PLLEN) {
+		dev_err(usbphyc->dev, "PLL not reset\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int stm32_usbphyc_phy_init(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+	struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+	int ret;
+
+	ret = stm32_usbphyc_pll_enable(usbphyc);
+	if (ret)
+		return ret;
+
+	usbphyc_phy->active = true;
+
+	return 0;
+}
+
+static int stm32_usbphyc_phy_exit(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+	struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+
+	usbphyc_phy->active = false;
+
+	return stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static int stm32_usbphyc_phy_power_on(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+	return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static int stm32_usbphyc_phy_power_off(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+	return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static const struct phy_ops stm32_usbphyc_phy_ops = {
+	.init = stm32_usbphyc_phy_init,
+	.exit = stm32_usbphyc_phy_exit,
+	.power_on = stm32_usbphyc_phy_power_on,
+	.power_off = stm32_usbphyc_phy_power_off,
+	.owner = THIS_MODULE,
+};
+
+static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
+				       u32 utmi_switch)
+{
+	if (!utmi_switch)
+		stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_MISC,
+				       SWITHOST);
+	else
+		stm32_usbphyc_set_bits(usbphyc->base + STM32_USBPHYC_MISC,
+				       SWITHOST);
+	usbphyc->switch_setup = utmi_switch;
+}
+
+static struct phy *stm32_usbphyc_of_xlate(struct device *dev,
+					  struct of_phandle_args *args)
+{
+	struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev);
+	struct stm32_usbphyc_phy *usbphyc_phy = NULL;
+	struct device_node *phynode = args->np;
+
+	int port = 0;
+
+	for (port = 0; port < usbphyc->nphys; port++) {
+		if (phynode == usbphyc->phys[port]->phy->dev.of_node) {
+			usbphyc_phy = usbphyc->phys[port];
+			break;
+		}
+	}
+	if (!usbphyc_phy) {
+		dev_err(dev, "failed to find phy\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (((usbphyc_phy->index == 0) && (args->args_count != 0)) ||
+	    ((usbphyc_phy->index == 1) && (args->args_count != 1))) {
+		dev_err(dev, "invalid number of cells for phy port%d\n",
+			usbphyc_phy->index);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Configure the UTMI switch for PHY port#2 */
+	if (usbphyc_phy->index == 1) {
+		if (usbphyc->switch_setup < 0) {
+			stm32_usbphyc_switch_setup(usbphyc, args->args[0]);
+		} else {
+			if (args->args[0] != usbphyc->switch_setup) {
+				dev_err(dev, "phy port1 already used\n");
+				return ERR_PTR(-EBUSY);
+			}
+		}
+	}
+
+	return usbphyc_phy->phy;
+}
+
+static int stm32_usbphyc_probe(struct platform_device *pdev)
+{
+	struct stm32_usbphyc *usbphyc;
+	struct device *dev = &pdev->dev;
+	struct device_node *child, *np = dev->of_node;
+	struct resource *res;
+	struct phy_provider *phy_provider;
+	u32 version;
+	int ret, port = 0;
+
+	usbphyc = devm_kzalloc(dev, sizeof(*usbphyc), GFP_KERNEL);
+	if (!usbphyc)
+		return -ENOMEM;
+	usbphyc->dev = dev;
+	dev_set_drvdata(dev, usbphyc);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	usbphyc->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(usbphyc->base))
+		return PTR_ERR(usbphyc->base);
+
+	usbphyc->clk = devm_clk_get(dev, 0);
+	if (IS_ERR(usbphyc->clk)) {
+		ret = PTR_ERR(usbphyc->clk);
+		dev_err(dev, "clk get failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(usbphyc->clk);
+	if (ret) {
+		dev_err(dev, "clk enable failed: %d\n", ret);
+		return ret;
+	}
+
+	usbphyc->rst = devm_reset_control_get(dev, 0);
+	if (!IS_ERR(usbphyc->rst)) {
+		reset_control_assert(usbphyc->rst);
+		udelay(2);
+		reset_control_deassert(usbphyc->rst);
+	}
+
+	usbphyc->switch_setup = -EINVAL;
+	usbphyc->nphys = of_get_child_count(np);
+	usbphyc->phys = devm_kcalloc(dev, usbphyc->nphys,
+				     sizeof(*usbphyc->phys), GFP_KERNEL);
+	if (!usbphyc->phys) {
+		ret = -ENOMEM;
+		goto clk_disable;
+	}
+
+	for_each_child_of_node(np, child) {
+		struct stm32_usbphyc_phy *usbphyc_phy;
+		struct phy *phy;
+		u32 index;
+		int i;
+
+		phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops);
+		if (IS_ERR(phy)) {
+			ret = PTR_ERR(phy);
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev,
+					"failed to create phy%d: %d\n", i, ret);
+			goto put_child;
+		}
+
+		usbphyc_phy = devm_kzalloc(dev, sizeof(*usbphyc_phy),
+					   GFP_KERNEL);
+		if (!usbphyc_phy) {
+			ret = -ENOMEM;
+			goto put_child;
+		}
+
+		for (i = 0; i < NUM_SUPPLIES; i++)
+			usbphyc_phy->supplies[i].supply = supplies_names[i];
+
+		ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
+					      usbphyc_phy->supplies);
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(&phy->dev,
+					"failed to get regulators: %d\n", ret);
+			goto put_child;
+		}
+
+		ret = of_property_read_u32(child, "reg", &index);
+		if (ret || index > usbphyc->nphys) {
+			dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+			goto put_child;
+		}
+
+		usbphyc->phys[port] = usbphyc_phy;
+		phy_set_bus_width(phy, 8);
+		phy_set_drvdata(phy, usbphyc_phy);
+
+		usbphyc->phys[port]->phy = phy;
+		usbphyc->phys[port]->usbphyc = usbphyc;
+		usbphyc->phys[port]->index = index;
+		usbphyc->phys[port]->active = false;
+
+		port++;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+						     stm32_usbphyc_of_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "failed to register phy provider: %d\n", ret);
+		goto clk_disable;
+	}
+
+	version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
+	dev_info(dev, "registered rev:%lu.%lu\n",
+		 FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
+
+	return 0;
+
+put_child:
+	of_node_put(child);
+clk_disable:
+	clk_disable_unprepare(usbphyc->clk);
+
+	return ret;
+}
+
+static int stm32_usbphyc_remove(struct platform_device *pdev)
+{
+	struct stm32_usbphyc *usbphyc = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(usbphyc->clk);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_usbphyc_of_match[] = {
+	{ .compatible = "st,stm32mp1-usbphyc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, stm32_usbphyc_of_match);
+
+static struct platform_driver stm32_usbphyc_driver = {
+	.probe = stm32_usbphyc_probe,
+	.remove = stm32_usbphyc_remove,
+	.driver = {
+		.of_match_table = stm32_usbphyc_of_match,
+		.name = "stm32-usbphyc",
+	}
+};
+module_platform_driver(stm32_usbphyc_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 USBPHYC driver");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
index 9d0689ebd28c685d39e718583ead6a2c53959031..05bee32a3a4d636f99e82124bfac8e99f8d2cfc3 100644
--- a/drivers/phy/tegra/xusb-tegra210.c
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -169,6 +169,7 @@
 #define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN (1 << 0)
 
 #define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c
+#define XUSB_PADCTL_UPHY_PLL_CTL4_XDIGCLK_EN (1 << 19)
 #define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN (1 << 15)
 #define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT 12
 #define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK 0x3
@@ -537,11 +538,8 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
 		value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL <<
 			  XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT);
 
-	/* XXX PLL0_XDIGCLK_EN */
-	/*
-	value &= ~(1 << 19);
+	value &= ~XUSB_PADCTL_UPHY_PLL_CTL4_XDIGCLK_EN;
 	padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
-	*/
 
 	value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
 	value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK <<
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index 63e916d4d0696156b244fb2c7e0e5f6de606c8e3..11aa5902a9ac1e39a3bb3a4e3b085dbf7a783d19 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -418,7 +418,7 @@ tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
 {
 	struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV);
 
-	for (map = map; map->type; map++) {
+	for (; map->type; map++) {
 		if (port->index != map->port)
 			continue;
 
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 4f8423a948d5dbf1f0848f139f2d67bf2099bbfb..c9d14eeee7f5ae8156c10967a66db5b53b191879 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -25,7 +25,15 @@ struct phy;
 enum phy_mode {
 	PHY_MODE_INVALID,
 	PHY_MODE_USB_HOST,
+	PHY_MODE_USB_HOST_LS,
+	PHY_MODE_USB_HOST_FS,
+	PHY_MODE_USB_HOST_HS,
+	PHY_MODE_USB_HOST_SS,
 	PHY_MODE_USB_DEVICE,
+	PHY_MODE_USB_DEVICE_LS,
+	PHY_MODE_USB_DEVICE_FS,
+	PHY_MODE_USB_DEVICE_HS,
+	PHY_MODE_USB_DEVICE_SS,
 	PHY_MODE_USB_OTG,
 	PHY_MODE_SGMII,
 	PHY_MODE_10GKR,
@@ -61,6 +69,7 @@ struct phy_ops {
  */
 struct phy_attrs {
 	u32			bus_width;
+	enum phy_mode		mode;
 };
 
 /**
@@ -72,7 +81,8 @@ struct phy_attrs {
  * @mutex: mutex to protect phy_ops
  * @init_count: used to protect when the PHY is used by multiple consumers
  * @power_count: used to protect when the PHY is used by multiple consumers
- * @phy_attrs: used to specify PHY specific attributes
+ * @attrs: used to specify PHY specific attributes
+ * @pwr: power regulator associated with the phy
  */
 struct phy {
 	struct device		dev;
@@ -88,9 +98,10 @@ struct phy {
 /**
  * struct phy_provider - represents the phy provider
  * @dev: phy provider device
+ * @children: can be used to override the default (dev->of_node) child node
  * @owner: the module owner having of_xlate
- * @of_xlate: function pointer to obtain phy instance from phy pointer
  * @list: to maintain a linked list of PHY providers
+ * @of_xlate: function pointer to obtain phy instance from phy pointer
  */
 struct phy_provider {
 	struct device		*dev;
@@ -101,6 +112,13 @@ struct phy_provider {
 		struct of_phandle_args *args);
 };
 
+/**
+ * struct phy_lookup - PHY association in list of phys managed by the phy driver
+ * @node: list node
+ * @dev_id: the device of the association
+ * @con_id: connection ID string on device
+ * @phy: the phy of the association
+ */
 struct phy_lookup {
 	struct list_head node;
 	const char *dev_id;
@@ -144,6 +162,10 @@ int phy_exit(struct phy *phy);
 int phy_power_on(struct phy *phy);
 int phy_power_off(struct phy *phy);
 int phy_set_mode(struct phy *phy, enum phy_mode mode);
+static inline enum phy_mode phy_get_mode(struct phy *phy)
+{
+	return phy->attrs.mode;
+}
 int phy_reset(struct phy *phy);
 int phy_calibrate(struct phy *phy);
 static inline int phy_get_bus_width(struct phy *phy)
@@ -260,6 +282,11 @@ static inline int phy_set_mode(struct phy *phy, enum phy_mode mode)
 	return -ENOSYS;
 }
 
+static inline enum phy_mode phy_get_mode(struct phy *phy)
+{
+	return PHY_MODE_INVALID;
+}
+
 static inline int phy_reset(struct phy *phy)
 {
 	if (!phy)