Linux-PHY Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] phy: cix: add Sky1 USB/DP combo PHY
@ 2026-11-02  8:15 Gary Yang
  2026-11-02  8:15 ` [PATCH 1/3] dt-bindings: phy: add CIX " Gary Yang
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Gary Yang @ 2026-11-02  8:15 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: CIX Linux Kernel Upstream Group, linux-arm-kernel, linux-phy,
	devicetree, linux-kernel, Gary Yang

This series adds support for the USB/DP combo PHY found on the CIX Sky1
SoC. Each PHY instance provides one SuperSpeedPlus USB lane and one
DisplayPort lane of a USB Type-C port, with plug orientation and USB/DP
lane muxing handled through the S5 system controller.

The PHY registers a Type-C switch and mux so that the connector driver
owns orientation and mode decisions, and exposes the USB and DP lanes as
separate child PHYs so that the USB and DP controllers can bind to them
independently.

Patch 1 documents the binding, patch 2 adds the driver, and patch 3 adds
the SoC nodes and enables them on the Radxa Orion O6.

The driver has been built for arm64 with the PHY enabled both built-in
and as a module. dt_binding_check, dtbs_check and checkpatch --strict
pass.

Signed-off-by: Gary Yang <gary.yang@cixtech.com>

Gary Yang (3):
  dt-bindings: phy: add CIX Sky1 USB/DP combo PHY
  phy: cix: add Sky1 USB/DP combo PHY driver
  arm64: dts: cix: add Sky1 USB/DP combo PHY

 .../bindings/phy/cix,sky1-usbdp-phy.yaml      |  150 ++
 MAINTAINERS                                   |    2 +
 arch/arm64/boot/dts/cix/sky1-orion-o6.dts     |   28 +
 arch/arm64/boot/dts/cix/sky1.dtsi             |   96 ++
 drivers/phy/Kconfig                           |    1 +
 drivers/phy/Makefile                          |    1 +
 drivers/phy/cix/Kconfig                       |   22 +
 drivers/phy/cix/Makefile                      |    2 +
 drivers/phy/cix/phy-cix-usbdp.c               | 1477 +++++++++++++++++
 drivers/phy/cix/phy-cix-usbdp.h               |  265 +++
 10 files changed, 2044 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
 create mode 100644 drivers/phy/cix/Kconfig
 create mode 100644 drivers/phy/cix/Makefile
 create mode 100644 drivers/phy/cix/phy-cix-usbdp.c
 create mode 100644 drivers/phy/cix/phy-cix-usbdp.h

-- 
2.50.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/3] dt-bindings: phy: add CIX Sky1 USB/DP combo PHY
  2026-11-02  8:15 [PATCH 0/3] phy: cix: add Sky1 USB/DP combo PHY Gary Yang
@ 2026-11-02  8:15 ` Gary Yang
  2026-11-02  8:27   ` sashiko-bot
  2026-11-02  8:15 ` [PATCH 2/3] phy: cix: add Sky1 USB/DP combo PHY driver Gary Yang
  2026-11-02  8:15 ` [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY Gary Yang
  2 siblings, 1 reply; 9+ messages in thread
From: Gary Yang @ 2026-11-02  8:15 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: CIX Linux Kernel Upstream Group, linux-arm-kernel, linux-phy,
	devicetree, linux-kernel, Gary Yang

Document the USB/DP combo PHY of the CIX Sky1 SoC. Each PHY instance
exposes one SuperSpeedPlus USB lane and one DisplayPort lane as child
nodes, so that the USB and DP consumers can each reference the lane
they use.

The PHY performs Type-C plug orientation switching and USB/DP lane
muxing in hardware and is therefore described with the
"orientation-switch" and "mode-switch" properties. Lane muxing is
programmed through the S5 system controller, identified by the
"cix,usbphy-syscon" phandle followed by the PHY instance index.

Reviewed-by: Fugang Duan <fugang.duan@cixtech.com>
Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 .../bindings/phy/cix,sky1-usbdp-phy.yaml      | 150 ++++++++++++++++++
 MAINTAINERS                                   |   1 +
 2 files changed, 151 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml

diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
new file mode 100644
index 000000000000..a5b6943f84eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
@@ -0,0 +1,150 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/cix,sky1-usbdp-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CIX Sky1 USB/DP Combo PHY
+
+maintainers:
+  - Gary Yang <gary.yang@cixtech.com>
+
+description:
+  The CIX Sky1 USB/DP combo PHY provides one SuperSpeedPlus USB lane
+  and one DisplayPort lane for a USB3C Type-C port. The USB and DP
+  lanes are represented as child nodes of the PHY controller.
+
+properties:
+  compatible:
+    const: cix,sky1-usbdp-phy
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  clocks:
+    maxItems: 1
+    description: PHY APB clock.
+
+  resets:
+    items:
+      - description: PHY core reset
+      - description: PHY APB preset
+
+  reset-names:
+    items:
+      - const: core
+      - const: apb
+
+  orientation-switch:
+    description:
+      The PHY handles Type-C plug orientation switching.
+    type: boolean
+
+  mode-switch:
+    description:
+      The PHY handles multiplexing of the USB and DisplayPort lanes.
+    type: boolean
+
+  cix,usbphy-syscon:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: phandle to the S5 system controller
+          - description: PHY instance index within the system controller
+    description:
+      Phandle to the system controller used for lane muxing and
+      Type-C orientation control, followed by the PHY instance index.
+
+  cix,default-conf:
+    $ref: /schemas/types.yaml#/definitions/uint8
+    enum: [0, 1, 2, 3]
+    default: 3
+    description: |
+      Lane mode the PHY is put in until a consumer changes it through the
+      Type-C mux. The value is a bit mask: 0 = none, 1 = USB, 2 = DP,
+      3 = USB and DP.
+
+patternProperties:
+  "^usb-port@[0-9a-f]+$":
+    type: object
+    description:
+      The USB lane of the combo PHY.
+
+    properties:
+      reg:
+        maxItems: 1
+
+      "#phy-cells":
+        const: 0
+
+    required:
+      - reg
+      - "#phy-cells"
+
+    additionalProperties: false
+
+  "^dp-port@[0-9a-f]+$":
+    type: object
+    description:
+      The DisplayPort lane of the combo PHY.
+
+    properties:
+      reg:
+        maxItems: 1
+
+      "#phy-cells":
+        const: 0
+
+    required:
+      - reg
+      - "#phy-cells"
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - clocks
+  - resets
+  - reset-names
+  - cix,usbphy-syscon
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/cix,sky1.h>
+    #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+
+    usbc_phy0: usb-phy@9030000 {
+        compatible = "cix,sky1-usbdp-phy";
+        reg = <0x09030000 0x40000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        clocks = <&scmi_clk CLK_TREE_USB3C_DRD_PHY3_GATE>;
+        resets = <&s5_syscon SKY1_USB_DP_PHY0_RST_N>,
+                 <&s5_syscon SKY1_USB_DP_PHY0_PRST_N>;
+        reset-names = "core", "apb";
+        cix,usbphy-syscon = <&s5_syscon 0>;
+        cix,default-conf = /bits/ 8 <0x03>;
+        orientation-switch;
+        mode-switch;
+
+        usb-port@0 {
+            reg = <0>;
+            #phy-cells = <0>;
+        };
+
+        dp-port@1 {
+            reg = <1>;
+            #phy-cells = <0>;
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 93abbb315065..8fb940a3ea3b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2903,6 +2903,7 @@ S:	Maintained
 T:	git https://github.com/cixtech/linux-mainline.git
 F:	Documentation/devicetree/bindings/arm/cix.yaml
 F:	Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
+F:	Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
 F:	arch/arm64/boot/dts/cix/
 F:	drivers/mailbox/cix-mailbox.c
 K:	\bcix\b
-- 
2.50.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/3] phy: cix: add Sky1 USB/DP combo PHY driver
  2026-11-02  8:15 [PATCH 0/3] phy: cix: add Sky1 USB/DP combo PHY Gary Yang
  2026-11-02  8:15 ` [PATCH 1/3] dt-bindings: phy: add CIX " Gary Yang
@ 2026-11-02  8:15 ` Gary Yang
  2026-11-02  8:31   ` sashiko-bot
  2026-11-02  8:15 ` [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY Gary Yang
  2 siblings, 1 reply; 9+ messages in thread
From: Gary Yang @ 2026-11-02  8:15 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: CIX Linux Kernel Upstream Group, linux-arm-kernel, linux-phy,
	devicetree, linux-kernel, Gary Yang

Add a driver for the USB/DP combo PHY found on the CIX Sky1 SoC. A
single PHY instance drives one SuperSpeedPlus USB lane and one
DisplayPort lane of a USB Type-C port, and exposes the two lanes as
separate child PHYs so that the USB and DP controllers can bind to
them independently.

The PHY supports Type-C plug orientation switching and lane muxing
between USB and DP, both driven through the S5 system controller. It
registers a Type-C switch and mux with the Type-C class, so the
orientation and mode decisions are taken by the connector driver
instead of the PHY.

The bootloader may hand over the port already initialized; in that
case the first power_on calls skip the re-initialization.

Reviewed-by: Fugang Duan <fugang.duan@cixtech.com>
Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 MAINTAINERS                     |    1 +
 drivers/phy/Kconfig             |    1 +
 drivers/phy/Makefile            |    1 +
 drivers/phy/cix/Kconfig         |   22 +
 drivers/phy/cix/Makefile        |    2 +
 drivers/phy/cix/phy-cix-usbdp.c | 1477 +++++++++++++++++++++++++++++++
 drivers/phy/cix/phy-cix-usbdp.h |  265 ++++++
 7 files changed, 1769 insertions(+)
 create mode 100644 drivers/phy/cix/Kconfig
 create mode 100644 drivers/phy/cix/Makefile
 create mode 100644 drivers/phy/cix/phy-cix-usbdp.c
 create mode 100644 drivers/phy/cix/phy-cix-usbdp.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 8fb940a3ea3b..ed595861174b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2906,6 +2906,7 @@ F:	Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
 F:	Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
 F:	arch/arm64/boot/dts/cix/
 F:	drivers/mailbox/cix-mailbox.c
+F:	drivers/phy/cix/
 K:	\bcix\b
 
 ARM/CLKDEV SUPPORT
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 105fbe408aa2..876e5857634c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -176,6 +176,7 @@ source "drivers/phy/axiado/Kconfig"
 source "drivers/phy/broadcom/Kconfig"
 source "drivers/phy/cadence/Kconfig"
 source "drivers/phy/canaan/Kconfig"
+source "drivers/phy/cix/Kconfig"
 source "drivers/phy/eswin/Kconfig"
 source "drivers/phy/freescale/Kconfig"
 source "drivers/phy/hisilicon/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c6dd02003bbe..5a31c5df1746 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_GENERIC_PHY)		+= allwinner/	\
 					   broadcom/	\
 					   cadence/	\
 					   canaan/	\
+					   cix/		\
 					   eswin/	\
 					   freescale/	\
 					   hisilicon/	\
diff --git a/drivers/phy/cix/Kconfig b/drivers/phy/cix/Kconfig
new file mode 100644
index 000000000000..769ec302657c
--- /dev/null
+++ b/drivers/phy/cix/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Phy drivers for CIX platforms
+#
+
+config PHY_CIX_USBDP
+	tristate "CIX USB3/DP combo PHY driver"
+	depends on ARCH_CIX || COMPILE_TEST
+	depends on OF
+	depends on HAS_IOMEM
+	depends on RESET_CONTROLLER
+	depends on TYPEC
+	select GENERIC_PHY
+	select MFD_SYSCON
+	select REGMAP
+	help
+	  Enable this to support the CIX USB3/DP combo PHY, which provides
+	  one SuperSpeedPlus USB lane and one DisplayPort lane for a USB
+	  Type-C port.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called phy-cix-usbdp.
diff --git a/drivers/phy/cix/Makefile b/drivers/phy/cix/Makefile
new file mode 100644
index 000000000000..68b5d7a029a8
--- /dev/null
+++ b/drivers/phy/cix/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PHY_CIX_USBDP)		+= phy-cix-usbdp.o
diff --git a/drivers/phy/cix/phy-cix-usbdp.c b/drivers/phy/cix/phy-cix-usbdp.c
new file mode 100644
index 000000000000..d596e89c74f7
--- /dev/null
+++ b/drivers/phy/cix/phy-cix-usbdp.c
@@ -0,0 +1,1477 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy driver for cdn_sd0804_t7g_typec
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+
+#include "phy-cix-usbdp.h"
+
+enum {
+	UDPHY_MODE_NONE		= 0,
+	UDPHY_MODE_USB		= BIT(0),
+	UDPHY_MODE_DP		= BIT(1),
+	UDPHY_MODE_DP_USB	= BIT(1) | BIT(0),
+};
+
+#define PHY_LANE_MUX_USB			0
+#define PHY_LANE_MUX_DP				1
+
+struct cix_udphy;
+
+struct cix_udphy_con_dir {
+	u16 offset;
+	u8 bit;
+};
+
+struct cix_udphy_cfg {
+	const struct cix_udphy_con_dir *con_dir;
+	int (*udphy_init)(struct cix_udphy *udphy);
+	int (*udphy_exit)(struct cix_udphy *udphy);
+};
+
+/*
+ * USB 3.0/3.1 Type C Alt Mode
+ *   port 0: USB_TCAM; port 1, DP, 8.1 Gbps by default
+ */
+static const struct reg_sequence sky1_udphy_usbdp_conf_normal[] = {
+	{CMN_SSM_BIAS_TMR,              0x0018},
+	{CMN_PLLSM0_PLLPRE_TMR,         0x0030},
+	{CMN_PLLSM0_PLLLOCK_TMR,        0x00f0},
+	{CMN_PLLSM1_PLLPRE_TMR,         0x0030},
+	{CMN_PLLSM1_PLLLOCK_TMR,        0x00f0},
+	{CMN_BGCAL_INIT_TMR,            0x0078},
+	{CMN_BGCAL_ITER_TMR,            0x0078},
+	{CMN_IBCAL_INIT_TMR,            0x0018},
+	{CMN_TXPUCAL_INIT_TMR,          0x001d},
+	{CMN_TXPDCAL_INIT_TMR,          0x001d},
+	{CMN_RXCAL_INIT_TMR,            0x02d0},
+	{CMN_SD_CAL_PLLCNT_START,       0x0137},
+
+	{RX_SDCAL0_INIT_TMR_LANE0,      0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE1,      0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE2,      0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE3,      0x0018},
+	{RX_SDCAL0_ITER_TMR_LANE0,      0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE1,      0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE2,      0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE3,      0x0078},
+	{RX_SDCAL1_INIT_TMR_LANE0,      0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE1,      0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE2,      0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE3,      0x0018},
+	{RX_SDCAL1_ITER_TMR_LANE0,      0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE1,      0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE2,      0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE3,      0x0078},
+	{TX_RCVDET_ST_TMR_LANE0,        0x0960},
+	{TX_RCVDET_ST_TMR_LANE1,        0x0960},
+	{TX_RCVDET_ST_TMR_LANE2,        0x0960},
+	{TX_RCVDET_ST_TMR_LANE3,        0x0960},
+
+	{CMN_PDIAG_PLL0_CLK_SEL_M0,     0x8600},
+	{CMN_PDIAG_PLL1_CLK_SEL_M0,     0x0601},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE0,   0x0041},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE1,   0x0041},
+	{XCVR_DIAG_HSCLK_SEL_LANE2,     0x0001},
+	{XCVR_DIAG_HSCLK_SEL_LANE3,     0x0001},
+	{XCVR_DIAG_HSCLK_DIV_LANE2,     0x0000},
+	{XCVR_DIAG_HSCLK_DIV_LANE3,     0x0000},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE2,   0x0019},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE3,   0x0019},
+
+	{CMN_PLL0_DSM_DIAG_M0,          0x0004},
+	{CMN_PLL1_DSM_DIAG_M0,          0x8004},
+	{CMN_PDIAG_PLL1_ITRIM_M0,       0x003f},
+	{CMN_PDIAG_PLL0_CP_PADJ_M0,     0x0B17},
+	{CMN_PDIAG_PLL1_CP_PADJ_M0,     0x0B17},
+	{CMN_PDIAG_PLL0_CP_IADJ_M0,     0x0E01},
+	{CMN_PDIAG_PLL1_CP_IADJ_M0,     0x0E01},
+	{CMN_PDIAG_PLL0_FILT_PADJ_M0,   0x0D05},
+	{CMN_PDIAG_PLL1_FILT_PADJ_M0,   0x0D05},
+
+	{CMN_PLL0_INTDIV_M0,            0x01A0},
+	{CMN_PLL1_INTDIV_M0,            0x01C2},
+	{CMN_PLL0_FRACDIVL_M0,          0xAAAB},
+
+	{CMN_PLL1_FRACDIVL_M0,          0x0000}, /* reset value. */
+
+	{CMN_PLL0_FRACDIVH_M0,          0x0002},
+	{CMN_PLL1_FRACDIVH_M0,          0x0002},
+	{CMN_PLL0_HIGH_THR_M0,          0x0116},
+	{CMN_PLL1_HIGH_THR_M0,          0x012C},
+	{CMN_PDIAG_PLL0_CTRL_M0,        0x1002},
+	{CMN_PDIAG_PLL1_CTRL_M0,        0x1002},
+	{CMN_PLL0_SS_CTRL1_M0,          0x0001},
+	{CMN_PLL1_SS_CTRL1_M0,          0x0001},
+	{CMN_PLL0_SS_CTRL2_M0,          0x0416},
+	{CMN_PLL1_SS_CTRL2_M0,          0x044F},
+	{CMN_PLL0_SS_CTRL3_M0,          0x007C},
+	{CMN_PLL1_SS_CTRL3_M0,          0x007F},
+	{CMN_PLL0_SS_CTRL4_M0,          0x0003},
+	{CMN_PLL1_SS_CTRL4_M0,          0x0003},
+	{CMN_PLL0_VCOCAL_INIT_TMR,      0x00F0},
+	{CMN_PLL1_VCOCAL_INIT_TMR,      0x00F0},
+	{CMN_PLL0_VCOCAL_ITER_TMR,      0x0004},
+	{CMN_PLL1_VCOCAL_ITER_TMR,      0x0004},
+	{CMN_PLL0_VCOCAL_REFTIM_START,  0x02F8},
+	{CMN_PLL1_VCOCAL_REFTIM_START,  0x02F8},
+	{CMN_PLL0_VCOCAL_PLLCNT_START,  0x02F6},
+	{CMN_PLL1_VCOCAL_PLLCNT_START,  0x02F6},
+	{CMN_PLL0_VCOCAL_TCTRL,         0x0003},
+	{CMN_PLL1_VCOCAL_TCTRL,         0x0003},
+	{CMN_PLL0_LOCK_REFCNT_START,    0x00BF},
+	{CMN_PLL1_LOCK_REFCNT_START,    0x00BF},
+	{CMN_PLL0_LOCK_PLLCNT_START,    0x00BF},
+	{CMN_PLL1_LOCK_PLLCNT_START,    0x00BF},
+	{CMN_PLL0_LOCK_PLLCNT_THR,      0x0005},
+	{CMN_PLL1_LOCK_PLLCNT_THR,      0x0005},
+
+	/* usb_part */
+	{PHY_PMA_LANE_MAP,              0x5100},
+	{PHY_LANE_OFF_CTRL,             0x0100},
+	{PHY_PIPE_USB3_GEN2_PRE_CFG0,   0x0A0A},
+	{PHY_PIPE_USB3_GEN2_POST_CFG0,  0x1000},
+	{PHY_PIPE_USB3_GEN2_POST_CFG1,  0x0010},
+	{CMN_CDIAG_CDB_PWRI_OVRD,       0x8200},
+	{CMN_CDIAG_XCVRC_PWRI_OVRD,     0x8200},
+	{TX_PSC_A0_LANE0,               0x02FF},
+	{TX_PSC_A1_LANE0,               0x06AF},
+	{TX_PSC_A2_LANE0,               0x06AE},
+	{TX_PSC_A3_LANE0,               0x06AE},
+	{RX_PSC_A0_LANE1,               0x0D1D},
+	{RX_PSC_A1_LANE1,               0x0D1D},
+	{RX_PSC_A2_LANE1,               0x0D00},
+	{RX_PSC_A3_LANE1,               0x0500},
+	{TX_TXCC_CTRL_LANE0,            0x2A82},
+	{TX_TXCC_CPOST_MULT_01_LANE0,   0x0014},
+	{TX_TXCC_MGNFS_MULT_000_LANE0,  0x0007},
+	{RX_SIGDET_HL_FILT_TMR_LANE1,   0x0013},
+	{RX_REE_GCSM1_CTRL_LANE1,       0x0000},
+	{RX_REE_ATTEN_THR_LANE1,        0x0C02},
+	{RX_REE_SMGM_CTRL1_LANE1,       0x0330},
+	{RX_REE_SMGM_CTRL2_LANE1,       0x0300},
+	{XCVR_DIAG_PSC_OVRD_LANE1,      0x0003},
+	{RX_DIAG_SIGDET_TUNE_LANE1,     0x1004},
+	{RX_DIAG_NQST_CTRL_LANE1,       0x00F9},
+	{RX_DIAG_DFE_AMP_TUNE_2_LANE1,  0x0C01},
+	{RX_DIAG_DFE_AMP_TUNE_3_LANE1,  0x0002},
+	{RX_DIAG_PI_CAP_LANE1,          0x0000},
+	{RX_DIAG_PI_RATE_LANE1,         0x0031},
+	{RX_DIAG_ACYA_LANE0,            0x0002},
+	{RX_CDRLF_CNFG_LANE1,           0x018C},
+	{RX_CDRLF_CNFG3_LANE1,          0x0003},
+	/* usb adding to upspeed */
+	{XCVR_DIAG_BIDI_CTRL_LANE0,     0x000f}, /* new add */
+	{XCVR_DIAG_BIDI_CTRL_LANE1,     0x00F0}, /* new add */
+
+	/* dp_part */
+	{PHY_PMA_ISO_PLL_CTRL0,         0x000B},
+	{PHY_PMA_ISO_PLL_CTRL1,         0x2224},
+
+	{TX_PSC_A0_LANE2,              0x00FB},
+	{TX_PSC_A0_LANE3,              0x00FB},
+	{TX_PSC_A2_LANE2,              0x04AA},
+	{TX_PSC_A2_LANE3,              0x04AA},
+	{TX_PSC_A3_LANE2,              0x04AA},
+	{TX_PSC_A3_LANE3,              0x04AA},
+	{RX_PSC_A0_LANE2,              0x0000},
+	{RX_PSC_A0_LANE3,              0x0000},
+	{RX_PSC_A2_LANE2,              0x0000},
+	{RX_PSC_A2_LANE3,              0x0000},
+	{RX_PSC_A3_LANE2,              0x0000},
+	{RX_PSC_A3_LANE3,              0x0000},
+	{RX_PSC_CAL_LANE2,             0x0000},
+	{RX_PSC_CAL_LANE3,             0x0000},
+	{XCVR_DIAG_BIDI_CTRL_LANE2,    0x000F},
+	{XCVR_DIAG_BIDI_CTRL_LANE3,    0x000F},
+	{RX_REE_GCSM1_CTRL_LANE2,      0x0000},
+	{RX_REE_GCSM1_CTRL_LANE3,      0x0000},
+	{RX_REE_GCSM2_CTRL_LANE2,      0x0000},
+	{RX_REE_GCSM2_CTRL_LANE3,      0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE2,    0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE3,    0x0000},
+
+	{CMN_DIAG_GPANA_0,             0x0100}, /* new add */
+};
+
+/*
+ * TYPEC_ALT_MODE_DP
+ *   DP only
+ */
+static const struct reg_sequence sky1_udphy_dp_conf[] = {
+	{CMN_SSM_BIAS_TMR,              0x0018},
+	{CMN_PLLSM0_PLLPRE_TMR,         0x0030},
+	{CMN_PLLSM0_PLLLOCK_TMR,        0x00f0},
+	{CMN_PLLSM1_PLLPRE_TMR,         0x0030},
+	{CMN_PLLSM1_PLLLOCK_TMR,        0x00f0},
+	{CMN_BGCAL_INIT_TMR,            0x0078},
+	{CMN_BGCAL_ITER_TMR,            0x0078},
+	{CMN_IBCAL_INIT_TMR,            0x0018},
+	{CMN_TXPUCAL_INIT_TMR,          0x001d},
+	{CMN_TXPDCAL_INIT_TMR,          0x001d},
+	{CMN_RXCAL_INIT_TMR,            0x02d0},
+	{CMN_SD_CAL_PLLCNT_START,       0x0137},
+	{RX_SDCAL0_INIT_TMR_LANE0,      0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE1,      0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE2,      0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE3,      0x0018},
+	{RX_SDCAL0_ITER_TMR_LANE0,      0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE1,      0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE2,      0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE3,      0x0078},
+	{RX_SDCAL1_INIT_TMR_LANE0,      0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE1,      0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE2,      0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE3,      0x0018},
+	{RX_SDCAL1_ITER_TMR_LANE0,      0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE1,      0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE2,      0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE3,      0x0078},
+	{TX_RCVDET_ST_TMR_LANE0,        0x0960},
+	{TX_RCVDET_ST_TMR_LANE1,        0x0960},
+	{TX_RCVDET_ST_TMR_LANE2,        0x0960},
+	{TX_RCVDET_ST_TMR_LANE3,        0x0960},
+	{CMN_PDIAG_PLL0_CLK_SEL_M0,     0x8600},
+	{CMN_PDIAG_PLL1_CLK_SEL_M0,     0x0701},
+	{XCVR_DIAG_HSCLK_SEL_LANE0,     0x0001},
+	{XCVR_DIAG_HSCLK_SEL_LANE1,     0x0001},
+	{XCVR_DIAG_HSCLK_SEL_LANE2,     0x0001},
+	{XCVR_DIAG_HSCLK_SEL_LANE3,     0x0001},
+	{XCVR_DIAG_HSCLK_DIV_LANE0,     0x0001},
+	{XCVR_DIAG_HSCLK_DIV_LANE1,     0x0001},
+	{XCVR_DIAG_HSCLK_DIV_LANE2,     0x0001},
+	{XCVR_DIAG_HSCLK_DIV_LANE3,     0x0001},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE0,   0x0019},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE1,   0x0019},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE2,   0x0019},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE3,   0x0019},
+	{CMN_PLL0_DSM_DIAG_M0,          0x0004},
+	{CMN_PLL1_DSM_DIAG_M0,          0x8004},
+	{CMN_PDIAG_PLL1_ITRIM_M0,       0x003f},
+
+	{CMN_PDIAG_PLL0_CP_PADJ_M0,     0x0b17},
+	{CMN_PDIAG_PLL1_CP_PADJ_M0,     0x0b17},
+	{CMN_PDIAG_PLL0_CP_IADJ_M0,     0x0e01},
+	{CMN_PDIAG_PLL1_CP_IADJ_M0,     0x0e01},
+	{CMN_PDIAG_PLL0_FILT_PADJ_M0,   0x0d05},
+	{CMN_PDIAG_PLL1_FILT_PADJ_M0,   0x0d05},
+	{CMN_PLL0_INTDIV_M0,            0x01a0},
+	{CMN_PLL1_INTDIV_M0,            0x01c2},
+	{CMN_PLL0_FRACDIVL_M0,          0xaaab},
+	{CMN_PLL1_FRACDIVL_M0,          0x0000},
+	{CMN_PLL0_FRACDIVH_M0,          0x0002},
+	{CMN_PLL1_FRACDIVH_M0,          0x0002},
+	{CMN_PLL0_HIGH_THR_M0,          0x0116},
+	{CMN_PLL1_HIGH_THR_M0,          0x012c},
+	{CMN_PDIAG_PLL0_CTRL_M0,        0x1002},
+	{CMN_PDIAG_PLL1_CTRL_M0,        0x1002},
+
+	{CMN_PLL0_SS_CTRL1_M0,          0x0001},
+	{CMN_PLL1_SS_CTRL1_M0,          0x0001},
+	{CMN_PLL0_SS_CTRL2_M0,          0x0416},
+	{CMN_PLL1_SS_CTRL2_M0,          0x044F},
+	{CMN_PLL0_SS_CTRL3_M0,          0x007C},
+	{CMN_PLL1_SS_CTRL3_M0,          0x007F},
+	{CMN_PLL0_SS_CTRL4_M0,          0x0003},
+	{CMN_PLL1_SS_CTRL4_M0,          0x0003},
+
+	{CMN_PLL0_VCOCAL_INIT_TMR,      0x00f0},
+	{CMN_PLL1_VCOCAL_INIT_TMR,      0x00f0},
+	{CMN_PLL0_VCOCAL_ITER_TMR,      0x0004},
+	{CMN_PLL1_VCOCAL_ITER_TMR,      0x0004},
+	{CMN_PLL0_VCOCAL_REFTIM_START,  0x02f8},
+	{CMN_PLL1_VCOCAL_REFTIM_START,  0x02f8},
+	{CMN_PLL0_VCOCAL_PLLCNT_START,  0x02f6},
+	{CMN_PLL1_VCOCAL_PLLCNT_START,  0x02f6},
+	{CMN_PLL0_VCOCAL_TCTRL,         0x0003},
+	{CMN_PLL1_VCOCAL_TCTRL,         0x0003},
+	{CMN_PLL0_LOCK_REFCNT_START,    0x00bf},
+	{CMN_PLL1_LOCK_REFCNT_START,    0x00bf},
+	{CMN_PLL0_LOCK_PLLCNT_START,    0x00bf},
+	{CMN_PLL1_LOCK_PLLCNT_START,    0x00bf},
+	{CMN_PLL0_LOCK_PLLCNT_THR,      0x0005},
+	{CMN_PLL1_LOCK_PLLCNT_THR,      0x0005},
+	{PHY_PMA_LANE_MAP,              0x51D9},
+	{PHY_LANE_OFF_CTRL,             0x0100},
+	{PHY_PIPE_USB3_GEN2_PRE_CFG0,   0x0a0a},
+	{PHY_PIPE_USB3_GEN2_POST_CFG0,  0x1000},
+	{PHY_PIPE_USB3_GEN2_POST_CFG1,  0x0010},
+	{CMN_CDIAG_CDB_PWRI_OVRD,       0x8200},
+	{CMN_CDIAG_XCVRC_PWRI_OVRD,     0x8200},
+
+	{PHY_PMA_ISO_PLL_CTRL0,         0x000b},
+	{PHY_PMA_ISO_PLL_CTRL1,         0x2224},
+	{TX_PSC_A0_LANE0,               0x00fb},
+	{TX_PSC_A0_LANE1,               0x00fb},
+	{TX_PSC_A0_LANE2,               0x00fb},
+	{TX_PSC_A0_LANE3,               0x00fb},
+	{TX_PSC_A2_LANE0,               0x04aa},
+	{TX_PSC_A2_LANE1,               0x04aa},
+	{TX_PSC_A2_LANE2,               0x04aa},
+	{TX_PSC_A2_LANE3,               0x04aa},
+	{TX_PSC_A3_LANE0,               0x04aa},
+	{TX_PSC_A3_LANE1,               0x04aa},
+	{TX_PSC_A3_LANE2,               0x04aa},
+	{TX_PSC_A3_LANE3,               0x04aa},
+	{RX_PSC_A0_LANE0,               0x0000},
+	{RX_PSC_A0_LANE1,               0x0000},
+	{RX_PSC_A0_LANE2,               0x0000},
+	{RX_PSC_A0_LANE3,               0x0000},
+	{RX_PSC_A2_LANE0,               0x0000},
+	{RX_PSC_A2_LANE1,               0x0000},
+	{RX_PSC_A2_LANE2,               0x0000},
+	{RX_PSC_A2_LANE3,               0x0000},
+	{RX_PSC_A3_LANE0,               0x0000},
+	{RX_PSC_A3_LANE1,               0x0000},
+	{RX_PSC_A3_LANE2,               0x0000},
+	{RX_PSC_A3_LANE3,               0x0000},
+	{RX_PSC_CAL_LANE0,              0x0000},
+	{RX_PSC_CAL_LANE1,              0x0000},
+	{RX_PSC_CAL_LANE2,              0x0000},
+	{RX_PSC_CAL_LANE3,              0x0000},
+	{XCVR_DIAG_BIDI_CTRL_LANE0,     0x000f},
+	{XCVR_DIAG_BIDI_CTRL_LANE1,     0x000f},
+	{XCVR_DIAG_BIDI_CTRL_LANE2,     0x000f},
+	{XCVR_DIAG_BIDI_CTRL_LANE3,     0x000f},
+	{RX_REE_GCSM1_CTRL_LANE0,       0x0000},
+	{RX_REE_GCSM1_CTRL_LANE1,       0x0000},
+	{RX_REE_GCSM1_CTRL_LANE2,       0x0000},
+	{RX_REE_GCSM1_CTRL_LANE3,       0x0000},
+	{RX_REE_GCSM2_CTRL_LANE0,       0x0000},
+	{RX_REE_GCSM2_CTRL_LANE1,       0x0000},
+	{RX_REE_GCSM2_CTRL_LANE2,       0x0000},
+	{RX_REE_GCSM2_CTRL_LANE3,       0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE0,     0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE1,     0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE2,     0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE3,     0x0000},
+	{CMN_DIAG_GPANA_0,              0x0100},
+};
+
+static const struct reg_sequence sky1_udphy_dp_8p1_conf[] = {
+	{CMN_PDIAG_PLL1_ITRIM_M0,       0x007f},
+	{CMN_PLL1_SS_CTRL2_M0,          0x0430},
+	{CMN_PLL1_SS_CTRL3_M0,          0x0062},
+	{CMN_PLL1_SS_CTRL4_M0,          0x0004},
+};
+
+static const struct reg_sequence sky1_udphy_dp_partial_conf[] = {
+	{CMN_PDIAG_PLL1_ITRIM_M0,       0x003f},
+	{CMN_PLL1_SS_CTRL2_M0,          0x044F},
+	{CMN_PLL1_SS_CTRL3_M0,          0x007F},
+	{CMN_PLL1_SS_CTRL4_M0,          0x0003},
+};
+
+static const struct reg_sequence sky1_udphy_usbdp_conf_flip[] = {
+	/* common_part */
+	{CMN_SSM_BIAS_TMR,             0x0018},
+	{CMN_PLLSM0_PLLPRE_TMR,        0x0030},
+	{CMN_PLLSM0_PLLLOCK_TMR,       0x00F0},
+	{CMN_PLLSM1_PLLPRE_TMR,        0x0030},
+	{CMN_PLLSM1_PLLLOCK_TMR,       0x00F0},
+	{CMN_BGCAL_INIT_TMR,           0x0078},
+	{CMN_BGCAL_ITER_TMR,           0x0078},
+	{CMN_IBCAL_INIT_TMR,           0x0018},
+	{CMN_TXPUCAL_INIT_TMR,         0x001D},
+	{CMN_TXPDCAL_INIT_TMR,         0x001D},
+	{CMN_RXCAL_INIT_TMR,           0x02D0},
+	{CMN_SD_CAL_PLLCNT_START,      0x0137},
+	{RX_SDCAL0_INIT_TMR_LANE0,     0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE1,     0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE2,     0x0018},
+	{RX_SDCAL0_INIT_TMR_LANE3,     0x0018},
+	{RX_SDCAL0_ITER_TMR_LANE0,     0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE1,     0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE2,     0x0078},
+	{RX_SDCAL0_ITER_TMR_LANE3,     0x0078},
+	{RX_SDCAL1_INIT_TMR_LANE0,     0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE1,     0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE2,     0x0018},
+	{RX_SDCAL1_INIT_TMR_LANE3,     0x0018},
+	{RX_SDCAL1_ITER_TMR_LANE0,     0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE1,     0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE2,     0x0078},
+	{RX_SDCAL1_ITER_TMR_LANE3,     0x0078},
+	{TX_RCVDET_ST_TMR_LANE0,       0x0960},
+	{TX_RCVDET_ST_TMR_LANE1,       0x0960},
+	{TX_RCVDET_ST_TMR_LANE2,       0x0960},
+	{TX_RCVDET_ST_TMR_LANE3,       0x0960},
+	{CMN_PDIAG_PLL0_CLK_SEL_M0,    0x8600},
+	{CMN_PDIAG_PLL1_CLK_SEL_M0,    0x0601},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE2,  0x0041},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE3,  0x0041},
+	{XCVR_DIAG_HSCLK_SEL_LANE0,    0x0001},
+	{XCVR_DIAG_HSCLK_SEL_LANE1,    0x0001},
+	{XCVR_DIAG_HSCLK_DIV_LANE0,    0x0000},
+	{XCVR_DIAG_HSCLK_DIV_LANE1,    0x0000},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE0,  0x0019},
+	{XCVR_DIAG_PLLDRC_CTRL_LANE1,  0x0019},
+	{CMN_PLL0_DSM_DIAG_M0,         0x0004},
+	{CMN_PLL1_DSM_DIAG_M0,         0x8004},
+	{CMN_PDIAG_PLL1_ITRIM_M0,      0x003f},
+	{CMN_PDIAG_PLL0_CP_PADJ_M0,    0x0B17},
+	{CMN_PDIAG_PLL1_CP_PADJ_M0,    0x0B17},
+	{CMN_PDIAG_PLL0_CP_IADJ_M0,    0x0E01},
+	{CMN_PDIAG_PLL1_CP_IADJ_M0,    0x0E01},
+	{CMN_PDIAG_PLL0_FILT_PADJ_M0,  0x0D05},
+	{CMN_PDIAG_PLL1_FILT_PADJ_M0,  0x0D05},
+
+	{CMN_PLL0_INTDIV_M0,           0x01A0},
+	{CMN_PLL1_INTDIV_M0,           0x01C2},
+	{CMN_PLL0_FRACDIVL_M0,         0xAAAB},
+
+	{CMN_PLL1_FRACDIVL_M0,         0x0000}, /* reset value */
+
+	{CMN_PLL0_FRACDIVH_M0,         0x0002},
+	{CMN_PLL1_FRACDIVH_M0,         0x0002},
+	{CMN_PLL0_HIGH_THR_M0,         0x0116},
+	{CMN_PLL1_HIGH_THR_M0,         0x012C},
+	{CMN_PDIAG_PLL0_CTRL_M0,       0x1002},
+	{CMN_PDIAG_PLL1_CTRL_M0,       0x1002},
+	{CMN_PLL0_SS_CTRL1_M0,         0x0001},
+	{CMN_PLL1_SS_CTRL1_M0,         0x0001},
+	{CMN_PLL0_SS_CTRL2_M0,         0x0416},
+	{CMN_PLL1_SS_CTRL2_M0,         0x044F},
+	{CMN_PLL0_SS_CTRL3_M0,         0x007C},
+	{CMN_PLL1_SS_CTRL3_M0,         0x007F},
+	{CMN_PLL0_SS_CTRL4_M0,         0x0003},
+	{CMN_PLL1_SS_CTRL4_M0,         0x0003},
+	{CMN_PLL0_VCOCAL_INIT_TMR,     0x00F0},
+	{CMN_PLL1_VCOCAL_INIT_TMR,     0x00F0},
+	{CMN_PLL0_VCOCAL_ITER_TMR,     0x0004},
+	{CMN_PLL1_VCOCAL_ITER_TMR,     0x0004},
+	{CMN_PLL0_VCOCAL_REFTIM_START, 0x02F8},
+	{CMN_PLL1_VCOCAL_REFTIM_START, 0x02F8},
+	{CMN_PLL0_VCOCAL_PLLCNT_START, 0x02F6},
+	{CMN_PLL1_VCOCAL_PLLCNT_START, 0x02F6},
+	{CMN_PLL0_VCOCAL_TCTRL,        0x0003},
+	{CMN_PLL1_VCOCAL_TCTRL,        0x0003},
+	{CMN_PLL0_LOCK_REFCNT_START,   0x00BF},
+	{CMN_PLL1_LOCK_REFCNT_START,   0x00BF},
+	{CMN_PLL0_LOCK_PLLCNT_START,   0x00BF},
+	{CMN_PLL1_LOCK_PLLCNT_START,   0x00BF},
+	{CMN_PLL0_LOCK_PLLCNT_THR,     0x0005},
+	{CMN_PLL1_LOCK_PLLCNT_THR,     0x0005},
+	/* usb_part */
+	{PHY_PMA_LANE_MAP,             0x5100},
+	{PHY_LANE_OFF_CTRL,            0x0100},
+	{PHY_PIPE_USB3_GEN2_PRE_CFG0,  0x0A0A},
+	{PHY_PIPE_USB3_GEN2_POST_CFG0, 0x1000},
+	{PHY_PIPE_USB3_GEN2_POST_CFG1, 0x0010},
+	{CMN_CDIAG_CDB_PWRI_OVRD,      0x8200},
+	{CMN_CDIAG_XCVRC_PWRI_OVRD,    0x8200},
+	{TX_PSC_A0_LANE3,              0x02FF},
+	{TX_PSC_A1_LANE3,              0x06AF},
+	{TX_PSC_A2_LANE3,              0x06AE},
+	{TX_PSC_A3_LANE3,              0x06AE},
+	{RX_PSC_A0_LANE2,              0x0D1D},
+	{RX_PSC_A1_LANE2,              0x0D1D},
+	{RX_PSC_A2_LANE2,              0x0D00},
+	{RX_PSC_A3_LANE2,              0x0500},
+	{TX_TXCC_CTRL_LANE3,           0x2A82},
+	{TX_TXCC_CPOST_MULT_01_LANE3,  0x0014},
+	{TX_TXCC_MGNFS_MULT_000_LANE3, 0x0007},
+	{RX_SIGDET_HL_FILT_TMR_LANE2,  0x0013},
+	{RX_REE_GCSM1_CTRL_LANE2,      0x0000},
+	{RX_REE_ATTEN_THR_LANE2,       0x0C02},
+	{RX_REE_SMGM_CTRL1_LANE2,      0x0330},
+	{RX_REE_SMGM_CTRL2_LANE2,      0x0300},
+	{XCVR_DIAG_PSC_OVRD_LANE2,     0x0003},
+	{RX_DIAG_SIGDET_TUNE_LANE2,    0x1004},
+	{RX_DIAG_NQST_CTRL_LANE2,      0x00F9},
+	{RX_DIAG_DFE_AMP_TUNE_2_LANE2, 0x0C01},
+	{RX_DIAG_DFE_AMP_TUNE_3_LANE2, 0x0002},
+	{RX_DIAG_PI_CAP_LANE2,         0x0000},
+	{RX_DIAG_PI_RATE_LANE2,        0x0031},
+	{RX_DIAG_ACYA_LANE3,           0x0002},
+	{RX_CDRLF_CNFG_LANE2,          0x018C},
+	{RX_CDRLF_CNFG3_LANE2,         0x0003},
+	/* usb adding to upspeed */
+	{XCVR_DIAG_BIDI_CTRL_LANE2,    0x00f0},
+	{XCVR_DIAG_BIDI_CTRL_LANE3,    0x000f},
+
+	/* dp_part */
+	{PHY_PMA_ISO_PLL_CTRL0,        0x000B},
+	{PHY_PMA_ISO_PLL_CTRL1,        0x2224},
+
+	/* lane0, lane1 */
+	{TX_PSC_A0_LANE0,              0x00FB},
+	{TX_PSC_A0_LANE1,              0x00FB},
+	{TX_PSC_A2_LANE0,              0x04AA},
+	{TX_PSC_A2_LANE1,              0x04AA},
+	{TX_PSC_A3_LANE0,              0x04AA},
+	{TX_PSC_A3_LANE1,              0x04AA},
+	{RX_PSC_A0_LANE0,              0x0000},
+	{RX_PSC_A0_LANE1,              0x0000},
+	{RX_PSC_A2_LANE0,              0x0000},
+	{RX_PSC_A2_LANE1,              0x0000},
+	{RX_PSC_A3_LANE0,              0x0000},
+	{RX_PSC_A3_LANE1,              0x0000},
+	{RX_PSC_CAL_LANE0,             0x0000},
+	{RX_PSC_CAL_LANE1,             0x0000},
+	{XCVR_DIAG_BIDI_CTRL_LANE0,    0x000F},
+	{XCVR_DIAG_BIDI_CTRL_LANE1,    0x000F},
+	{RX_REE_GCSM1_CTRL_LANE0,      0x0000},
+	{RX_REE_GCSM1_CTRL_LANE1,      0x0000},
+	{RX_REE_GCSM2_CTRL_LANE0,      0x0000},
+	{RX_REE_GCSM2_CTRL_LANE1,      0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE0,    0x0000},
+	{RX_REE_PERGCSM_CTRL_LANE1,    0x0000},
+
+	{CMN_DIAG_GPANA_0,             0x100},
+};
+
+static const struct cix_udphy_con_dir sky1_udphy_con_dir[] = {
+	{0x420, 0},
+	{0x420, 1},
+	{0x420, 2},
+	{0x420, 3}
+};
+
+struct cix_udphy {
+	struct device *dev;
+	void __iomem *base;
+	struct regmap *phy_regmap;
+	struct regmap *usbphy_syscon;
+	struct reset_control *preset;
+	struct reset_control *reset;
+	struct clk *apb_clk;
+	struct typec_switch_dev *sw;
+	struct typec_mux_dev *mux;
+	struct mutex mutex; /* mutex to protect access to individual PHYs */
+	bool flip;
+	u8 cur_mode;
+	u8 next_mode;
+	u32 id;
+	const struct cix_udphy_cfg *cfg;
+};
+
+/* Link rate change */
+struct phy_link_cfg {
+	u32 hsclk_div;
+	u32 clk_sel;
+	u32 pll_intdiv;
+	u32 pll_fracdivl;
+	u32 pll_high_thr;
+};
+
+static const struct phy_link_cfg link_cfg[] = {
+	{ 0x0002, 0x0f01, 0x0195, 0x0000, 0x010e },  /* 1.62gbps */
+	{ 0x0001, 0x0701, 0x01c2, 0x0000, 0x012c },  /* 2.70gbps */
+	{ 0x0000, 0x0301, 0x01c2, 0x0000, 0x012c },  /* 5.40gbps */
+	{ 0x0000, 0x0200, 0x0151, 0x8000, 0x00e2 },  /* 8.10gbps */
+};
+
+struct phy_voltage_conf {
+	u32 tx_txcc_ctrl;
+	u32 drv_diag_tx_drv;
+	u32 tx_txcc_mgnfc_mult_000;
+	u32 tx_txcc_cpost_mult_00;
+};
+
+static const struct phy_voltage_conf volt_cfg[] = {
+	{ 0x08A4, 0x0003, 0x0028, 0x0000 }, /* swing level == 0 */
+	{ 0x08A4, 0x0003, 0x001f, 0x0014 },
+	{ 0x08A4, 0x0003, 0x0012, 0x0020 },
+	{ 0x08A4, 0x0003, 0x0000, 0x0027 },
+	{ 0x08A4, 0x0003, 0x001f, 0x0000 }, /* swing level == 1 */
+	{ 0x08A4, 0x0003, 0x0013, 0x0012 },
+	{ 0x08A4, 0x0003, 0x0000, 0x001f },
+	{ 0x08A4, 0x0003, 0x0000, 0x001f },
+	{ 0x08A4, 0x0003, 0x0013, 0x0000 }, /* swing level == 2 */
+	{ 0x08A4, 0x0003, 0x0000, 0x0013 },
+	{ 0x08A4, 0x0003, 0x0000, 0x0013 },
+	{ 0x08A4, 0x0003, 0x0000, 0x0013 },
+	{ 0x08A4, 0x0003, 0x0000, 0x0000 }, /* swing level == 3 */
+	{ 0x08A4, 0x0003, 0x0000, 0x0000 },
+	{ 0x08A4, 0x0003, 0x0000, 0x0000 },
+	{ 0x08A4, 0x0003, 0x0000, 0x0000 },
+};
+
+static int udphy_dplane_get(struct cix_udphy *udphy)
+{
+	int dp_lanes;
+
+	switch (udphy->next_mode) {
+	case UDPHY_MODE_DP:
+		dp_lanes = 4;
+		break;
+	case UDPHY_MODE_DP_USB:
+		dp_lanes = 2;
+		break;
+	case UDPHY_MODE_USB:
+		fallthrough;
+	default:
+		dp_lanes = 0;
+		break;
+	}
+
+	return dp_lanes;
+}
+
+static int sky1_udphy_clk_enable(struct clk *clk)
+{
+	int i, ret = -EBUSY;
+
+	/*
+	 * If the clock could not enable 10 iterations,
+	 * it considers this clock is broken.
+	 */
+	for (i = 0; i < 10; i++) {
+		ret = clk_prepare_enable(clk);
+		if (!ret)
+			return 0;
+
+		usleep_range(5000, 6000);
+	}
+
+	return ret;
+}
+
+static int sky1_udphy_exit(struct cix_udphy *udphy)
+{
+	reset_control_assert(udphy->reset);
+	reset_control_assert(udphy->preset);
+	clk_disable_unprepare(udphy->apb_clk);
+	return 0;
+}
+
+static int sky1_udphy_init(struct cix_udphy *udphy)
+{
+	int ret;
+	u32 value;
+	const struct cix_udphy_con_dir *con_dir = &udphy->cfg->con_dir[udphy->id];
+
+	regmap_update_bits(udphy->usbphy_syscon, con_dir->offset,
+			   GENMASK(con_dir->bit, con_dir->bit),
+			   udphy->flip << con_dir->bit);
+
+	usleep_range(10, 20);
+
+	regmap_read(udphy->usbphy_syscon, con_dir->offset, &value);
+
+	/* usb rcsu reset is default deassert */
+	reset_control_assert(udphy->reset);
+	reset_control_assert(udphy->preset);
+
+	ret = sky1_udphy_clk_enable(udphy->apb_clk);
+	if (ret) {
+		dev_err(udphy->dev, "Failed to prepare_enable udphy apb clock\n");
+		return ret;
+	}
+
+	reset_control_deassert(udphy->preset);
+
+	if (udphy->next_mode == UDPHY_MODE_DP) {
+		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_conf,
+					     ARRAY_SIZE(sky1_udphy_dp_conf));
+
+		if (ret) {
+			dev_err(udphy->dev, "Failed to write the reg sequence\n");
+			goto assert_apb;
+		}
+	} else {
+		if (!udphy->flip) {
+			ret = regmap_multi_reg_write(udphy->phy_regmap,
+						     sky1_udphy_usbdp_conf_normal,
+						     ARRAY_SIZE(sky1_udphy_usbdp_conf_normal));
+			if (ret) {
+				dev_err(udphy->dev, "Failed to write the reg sequence\n");
+				goto assert_apb;
+			}
+		} else {
+			ret = regmap_multi_reg_write(udphy->phy_regmap,
+						     sky1_udphy_usbdp_conf_flip,
+						     ARRAY_SIZE(sky1_udphy_usbdp_conf_flip));
+			if (ret) {
+				dev_err(udphy->dev, "Failed to write the reg sequence\n");
+				goto assert_apb;
+			}
+		}
+	}
+
+	reset_control_deassert(udphy->reset);
+
+	return 0;
+
+assert_apb:
+	reset_control_assert(udphy->preset);
+	clk_disable_unprepare(udphy->apb_clk);
+	return ret;
+}
+
+static const struct cix_udphy_cfg sky1_udphy_cfg = {
+	.con_dir = sky1_udphy_con_dir,
+	.udphy_init = sky1_udphy_init,
+	.udphy_exit = sky1_udphy_exit
+};
+
+static int udphy_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct cix_udphy *udphy = context;
+	u32 offset = reg << 2;
+
+	writel(val, udphy->base + offset);
+
+	return 0;
+}
+
+static int udphy_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct cix_udphy *udphy = context;
+	u32 offset = reg << 2;
+
+	*val = readl(udphy->base + offset);
+
+	return 0;
+}
+
+static const struct regmap_config cix_udphy_regmap_cfg = {
+	.reg_bits = 32,
+	/* register width, if =2, then only reg 0 2 4 ...2^n can access */
+	.reg_stride = 1,
+	.val_bits = 16,
+	.fast_io = true,
+	.reg_write = udphy_regmap_write,
+	.reg_read = udphy_regmap_read,
+};
+
+static int udphy_orien_sw_set(struct typec_switch_dev *sw,
+			      enum typec_orientation orien)
+{
+	struct cix_udphy *udphy = typec_switch_get_drvdata(sw);
+
+	mutex_lock(&udphy->mutex);
+
+	if (orien == TYPEC_ORIENTATION_NONE)
+		goto unlock_ret;
+
+	udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
+
+unlock_ret:
+	mutex_unlock(&udphy->mutex);
+	return 0;
+}
+
+static int udphy_setup_orien_switch(struct cix_udphy *udphy)
+{
+	struct typec_switch_desc sw_desc = { };
+
+	sw_desc.drvdata = udphy;
+	sw_desc.fwnode = dev_fwnode(udphy->dev);
+	sw_desc.set = udphy_orien_sw_set;
+
+	udphy->sw = typec_switch_register(udphy->dev, &sw_desc);
+	if (IS_ERR(udphy->sw)) {
+		dev_err(udphy->dev, "Error register typec orientation switch: %ld\n",
+			PTR_ERR(udphy->sw));
+		return PTR_ERR(udphy->sw);
+	}
+
+	return 0;
+}
+
+static int usbdp_typec_mux_set(struct typec_mux_dev *mux,
+			       struct typec_mux_state *state)
+{
+	struct cix_udphy *udphy = typec_mux_get_drvdata(mux);
+
+	mutex_lock(&udphy->mutex);
+
+	switch (state->mode) {
+	case TYPEC_DP_STATE_C:
+		fallthrough;
+	case TYPEC_DP_STATE_E:
+		udphy->next_mode = UDPHY_MODE_DP;
+		break;
+	case TYPEC_DP_STATE_D:
+		udphy->next_mode = UDPHY_MODE_DP_USB;
+		break;
+	case TYPEC_STATE_SAFE:
+		udphy->next_mode = UDPHY_MODE_USB;
+		break;
+	default:
+		/* usb only handle as usb+dp mode */
+		udphy->next_mode = UDPHY_MODE_USB;
+		break;
+	}
+
+	/* TODO: check whether the HPD state needs software */
+	mutex_unlock(&udphy->mutex);
+
+	return 0;
+}
+
+static void udphy_orien_switch_unregister(void *data)
+{
+	struct cix_udphy *udphy = data;
+
+	typec_switch_unregister(udphy->sw);
+}
+
+static int udphy_setup_typec_mux(struct cix_udphy *udphy)
+{
+	struct typec_mux_desc mux_desc = {};
+
+	mux_desc.drvdata = udphy;
+	mux_desc.fwnode = dev_fwnode(udphy->dev);
+	mux_desc.set = usbdp_typec_mux_set;
+
+	udphy->mux = typec_mux_register(udphy->dev, &mux_desc);
+	if (IS_ERR(udphy->mux)) {
+		dev_err(udphy->dev, "Error register typec mux: %ld\n",
+			PTR_ERR(udphy->mux));
+		return PTR_ERR(udphy->mux);
+	}
+
+	return 0;
+}
+
+static void udphy_typec_mux_unregister(void *data)
+{
+	struct cix_udphy *udphy = data;
+
+	typec_mux_unregister(udphy->mux);
+}
+
+static int cix_usbdp_phy_power_on(struct cix_udphy *udphy)
+{
+	const struct cix_udphy_cfg *phy_cfgs = udphy->cfg;
+	int ret;
+
+	if (udphy->cur_mode == udphy->next_mode)
+		return 0;
+
+	if ((udphy->cur_mode == UDPHY_MODE_USB || udphy->cur_mode == UDPHY_MODE_DP) &&
+	    udphy->next_mode == UDPHY_MODE_DP_USB)
+		return 0;
+
+	if (phy_cfgs->udphy_init) {
+		ret = phy_cfgs->udphy_init(udphy);
+		if (ret) {
+			dev_err(udphy->dev, "failed to init udphy\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int cix_usbdp_phy_power_off(struct cix_udphy *udphy)
+{
+	const struct cix_udphy_cfg *phy_cfgs = udphy->cfg;
+	int ret;
+
+	if (phy_cfgs->udphy_exit) {
+		ret = phy_cfgs->udphy_exit(udphy);
+		if (ret) {
+			dev_err(udphy->dev, "failed to exit udphy\n");
+			return ret;
+		}
+	}
+
+	udphy->cur_mode = UDPHY_MODE_NONE;
+	return 0;
+}
+
+static int udphy_dp_phy_power_on(struct phy *phy)
+{
+	struct cix_udphy *udphy = phy_get_drvdata(phy);
+	int ret = 0;
+
+	mutex_lock(&udphy->mutex);
+	if (!(udphy->next_mode & UDPHY_MODE_DP))
+		goto unlock;
+
+	ret = cix_usbdp_phy_power_on(udphy);
+	if (ret)
+		goto unlock;
+
+	phy_set_bus_width(phy, udphy_dplane_get(udphy));
+	if (udphy->next_mode == UDPHY_MODE_DP)
+		udphy->cur_mode = UDPHY_MODE_DP;
+	else
+		udphy->cur_mode |= UDPHY_MODE_DP;
+
+unlock:
+	mutex_unlock(&udphy->mutex);
+	return ret;
+}
+
+static int udphy_dp_phy_power_off(struct phy *phy)
+{
+	struct cix_udphy *udphy = phy_get_drvdata(phy);
+	int ret = 0;
+
+	mutex_lock(&udphy->mutex);
+
+	if (!(udphy->cur_mode & UDPHY_MODE_DP))
+		goto unlock;
+	udphy->cur_mode &= ~UDPHY_MODE_DP;
+
+	if (udphy->cur_mode == UDPHY_MODE_NONE)
+		ret = cix_usbdp_phy_power_off(udphy);
+
+	phy_set_bus_width(phy, 0);
+
+unlock:
+	mutex_unlock(&udphy->mutex);
+	return ret;
+}
+
+static int udphy_dp_phy_verify_config(struct cix_udphy *udphy,
+				      struct phy_configure_opts_dp *dp)
+{
+	int i;
+
+	switch (dp->lanes) {
+	case 1:
+	case 2:
+	case 4:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * If changing voltages is required, check swing and pre-emphasis
+	 * levels, per-lane.
+	 */
+	if (dp->set_voltages) {
+		for (i = 0; i < dp->lanes; i++) {
+			if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+				return -EINVAL;
+
+			/*
+			 * Sum of voltage swing and pre-emphasis levels cannot
+			 * exceed 3.
+			 */
+			if (dp->voltage[i] + dp->pre[i] > 3)
+				return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int udphy_dp_phy_set_rate(struct cix_udphy *udphy,
+				 struct phy_configure_opts_dp *dp)
+{
+	int ret;
+	const struct phy_link_cfg *conf = NULL;
+
+	dev_dbg(udphy->dev, "lane %d, rate %x\n", dp->lanes, dp->link_rate);
+
+	regmap_update_bits(udphy->phy_regmap, PHY_PMA_PLL_CTRL, BIT(1), 0x0);
+	usleep_range(10, 20);
+
+	switch (dp->link_rate) {
+	case 0x6:
+		conf = &link_cfg[0];
+		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
+					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
+		break;
+	case 0xa:
+		conf = &link_cfg[1];
+		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
+					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
+		break;
+	case 0x14:
+		conf = &link_cfg[2];
+		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
+					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
+		break;
+	case 0x1e:
+		conf = &link_cfg[3];
+		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_8p1_conf,
+					     ARRAY_SIZE(sky1_udphy_dp_8p1_conf));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	switch (dp->lanes) {
+	case 1:
+		if (!udphy->flip) {
+			regmap_write(udphy->phy_regmap,
+				     XCVR_DIAG_HSCLK_DIV_LANE2,
+				     conf->hsclk_div);
+		} else {
+			regmap_write(udphy->phy_regmap,
+				     XCVR_DIAG_HSCLK_DIV_LANE1,
+				     conf->hsclk_div);
+		}
+		break;
+	case 2:
+		if (!udphy->flip) {
+			regmap_write(udphy->phy_regmap,
+				     XCVR_DIAG_HSCLK_DIV_LANE2,
+				     conf->hsclk_div);
+			regmap_write(udphy->phy_regmap,
+				     XCVR_DIAG_HSCLK_DIV_LANE3,
+				     conf->hsclk_div);
+		} else {
+			regmap_write(udphy->phy_regmap,
+				     XCVR_DIAG_HSCLK_DIV_LANE0,
+				     conf->hsclk_div);
+			regmap_write(udphy->phy_regmap,
+				     XCVR_DIAG_HSCLK_DIV_LANE1,
+				     conf->hsclk_div);
+		}
+		break;
+	case 4:
+		regmap_write(udphy->phy_regmap,
+			     XCVR_DIAG_HSCLK_DIV_LANE0,
+			     conf->hsclk_div);
+		regmap_write(udphy->phy_regmap,
+			     XCVR_DIAG_HSCLK_DIV_LANE1,
+			     conf->hsclk_div);
+		regmap_write(udphy->phy_regmap,
+			     XCVR_DIAG_HSCLK_DIV_LANE2,
+			     conf->hsclk_div);
+		regmap_write(udphy->phy_regmap,
+			     XCVR_DIAG_HSCLK_DIV_LANE3,
+			     conf->hsclk_div);
+		break;
+	default:
+		break;
+	}
+
+	regmap_write(udphy->phy_regmap, CMN_PLL1_INTDIV_M0, conf->pll_intdiv);
+	regmap_write(udphy->phy_regmap, CMN_PLL1_FRACDIVL_M0, conf->pll_fracdivl);
+	regmap_write(udphy->phy_regmap, CMN_PLL1_HIGH_THR_M0, conf->pll_high_thr);
+	regmap_write(udphy->phy_regmap, CMN_PDIAG_PLL1_CLK_SEL_M0, conf->clk_sel);
+
+	regmap_update_bits(udphy->phy_regmap, PHY_PMA_PLL_CTRL, BIT(1), BIT(1));
+	usleep_range(10, 20);
+
+	return 0;
+}
+
+static int udphy_dp_phy_set_voltages(struct cix_udphy *udphy,
+				     struct phy_configure_opts_dp *dp)
+{
+	u32 index = dp->voltage[0] * 4 + dp->pre[0];
+	const struct phy_voltage_conf *cfg = volt_cfg;
+
+	dev_dbg(udphy->dev, "lane %d, vs %d, pe %d\n", dp->lanes, dp->voltage[0],
+		dp->pre[0]);
+
+	switch (dp->lanes) {
+	case 1:
+		if (!udphy->flip) { /* lane 2 */
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
+					   BIT(0), 0x1);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE2,
+				     cfg[index].tx_txcc_ctrl);
+			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE2,
+				     cfg[index].drv_diag_tx_drv);
+			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE2,
+				     cfg[index].tx_txcc_mgnfc_mult_000);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE2,
+				     cfg[index].tx_txcc_cpost_mult_00);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
+					   BIT(0), 0x0);
+		} else { /* lane 1 */
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE1,
+					   BIT(0), 0x1);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE1,
+				     cfg[index].tx_txcc_ctrl);
+			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE1,
+				     cfg[index].drv_diag_tx_drv);
+			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE1,
+				     cfg[index].tx_txcc_mgnfc_mult_000);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE1,
+				     cfg[index].tx_txcc_cpost_mult_00);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE1,
+					   BIT(0), 0x0);
+		}
+		break;
+	case 2:
+		if (!udphy->flip) {
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
+					   BIT(0), 0x1);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE2,
+				     cfg[index].tx_txcc_ctrl);
+			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE2,
+				     cfg[index].drv_diag_tx_drv);
+			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE2,
+				     cfg[index].tx_txcc_mgnfc_mult_000);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE2,
+				     cfg[index].tx_txcc_cpost_mult_00);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
+					   BIT(0), 0x0);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE3,
+					   BIT(0), 0x1);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE3,
+				     cfg[index].tx_txcc_ctrl);
+			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE3,
+				     cfg[index].drv_diag_tx_drv);
+			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE3,
+				     cfg[index].tx_txcc_mgnfc_mult_000);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE3,
+				     cfg[index].tx_txcc_cpost_mult_00);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE3,
+					   BIT(0), 0x0);
+		} else {
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE0,
+					   BIT(0), 0x1);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE0,
+				     cfg[index].tx_txcc_ctrl);
+			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE0,
+				     cfg[index].drv_diag_tx_drv);
+			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE0,
+				     cfg[index].tx_txcc_mgnfc_mult_000);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE0,
+				     cfg[index].tx_txcc_cpost_mult_00);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE0,
+					   BIT(0), 0x0);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE1,
+					   BIT(0), 0x1);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE1,
+				     cfg[index].tx_txcc_ctrl);
+			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE1,
+				     cfg[index].drv_diag_tx_drv);
+			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE1,
+				     cfg[index].tx_txcc_mgnfc_mult_000);
+			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE1,
+				     cfg[index].tx_txcc_cpost_mult_00);
+			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE1,
+					   BIT(0), 0x0);
+		}
+		break;
+	case 4:
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE0,
+				   BIT(0), 0x1);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE0,
+			     cfg[index].tx_txcc_ctrl);
+		regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE0,
+			     cfg[index].drv_diag_tx_drv);
+		regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE0,
+			     cfg[index].tx_txcc_mgnfc_mult_000);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE0,
+			     cfg[index].tx_txcc_cpost_mult_00);
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE0,
+				   BIT(0), 0x0);
+
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE1,
+				   BIT(0), 0x1);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE1,
+			     cfg[index].tx_txcc_ctrl);
+		regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE1,
+			     cfg[index].drv_diag_tx_drv);
+		regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE1,
+			     cfg[index].tx_txcc_mgnfc_mult_000);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE1,
+			     cfg[index].tx_txcc_cpost_mult_00);
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE1,
+				   BIT(0), 0x0);
+
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
+				   BIT(0), 0x1);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE2,
+			     cfg[index].tx_txcc_ctrl);
+		regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE2,
+			     cfg[index].drv_diag_tx_drv);
+		regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE2,
+			     cfg[index].tx_txcc_mgnfc_mult_000);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE2,
+			     cfg[index].tx_txcc_cpost_mult_00);
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
+				   BIT(0), 0x0);
+
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE3,
+				   BIT(0), 0x1);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE3,
+			     cfg[index].tx_txcc_ctrl);
+		regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE3,
+			     cfg[index].drv_diag_tx_drv);
+		regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE3,
+			     cfg[index].tx_txcc_mgnfc_mult_000);
+		regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE3,
+			     cfg[index].tx_txcc_cpost_mult_00);
+		regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE3,
+				   BIT(0), 0x0);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int udphy_dp_phy_configure(struct phy *phy,
+				  union phy_configure_opts *opts)
+{
+	struct cix_udphy *udphy = phy_get_drvdata(phy);
+	int ret;
+
+	mutex_lock(&udphy->mutex);
+	ret = udphy_dp_phy_verify_config(udphy, &opts->dp);
+	if (ret)
+		goto unlock;
+
+	if (opts->dp.set_rate) {
+		ret = udphy_dp_phy_set_rate(udphy, &opts->dp);
+		if (ret) {
+			dev_err(udphy->dev, "failed to set DP link rate\n");
+			goto unlock;
+		}
+	}
+
+	if (opts->dp.set_voltages) {
+		ret = udphy_dp_phy_set_voltages(udphy, &opts->dp);
+		if (ret) {
+			dev_err(udphy->dev, "dp_phy_set_voltages failed\n");
+			goto unlock;
+		}
+	}
+	mutex_unlock(&udphy->mutex);
+	return 0;
+
+unlock:
+	mutex_unlock(&udphy->mutex);
+	return ret;
+}
+
+static const struct phy_ops cix_dp_phy_ops = {
+	.power_on	= udphy_dp_phy_power_on,
+	.power_off	= udphy_dp_phy_power_off,
+	.configure	= udphy_dp_phy_configure,
+	.owner		= THIS_MODULE,
+};
+
+static int cix_u3phy_init(struct phy *phy)
+{
+	struct cix_udphy *udphy = phy_get_drvdata(phy);
+	int ret = 0;
+
+	mutex_lock(&udphy->mutex);
+	if (!(udphy->next_mode & UDPHY_MODE_USB))
+		goto unlock;
+
+	if (udphy->cur_mode & UDPHY_MODE_USB)
+		goto unlock;
+
+	ret = cix_usbdp_phy_power_on(udphy);
+	if (ret)
+		goto unlock;
+
+	udphy->cur_mode |= UDPHY_MODE_USB;
+
+unlock:
+	mutex_unlock(&udphy->mutex);
+	return ret;
+}
+
+static int cix_u3phy_exit(struct phy *phy)
+{
+	struct cix_udphy *udphy = phy_get_drvdata(phy);
+	int ret = 0;
+
+	mutex_lock(&udphy->mutex);
+
+	if (!(udphy->cur_mode & UDPHY_MODE_USB))
+		goto unlock;
+
+	udphy->cur_mode &= ~UDPHY_MODE_USB;
+
+	if (udphy->cur_mode == UDPHY_MODE_NONE)
+		ret = cix_usbdp_phy_power_off(udphy);
+
+unlock:
+	mutex_unlock(&udphy->mutex);
+	return ret;
+}
+
+static int cix_u3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+	if (mode == PHY_MODE_INVALID)
+		return cix_u3phy_exit(phy);
+
+	return cix_u3phy_init(phy);
+}
+
+static const struct phy_ops cix_u3phy_ops = {
+	.set_mode       = cix_u3phy_set_mode,
+	.exit		= cix_u3phy_exit,
+	.owner		= THIS_MODULE,
+};
+
+/*
+ * PHY lane to PMA lane mapping for a normal plug orientation
+ *
+ * PMA LANE     C       D                 E
+ * 0          DP TX2   USB TX0          DP TX2
+ * 1          DP TX3   USB RX0          DP TX3
+ * 2          DP TX0   USB RX1/DP TX0   DP TX0
+ * 3          DP TX1   USB TX1/DP TX1   DP TX1
+ *
+ * PHY lane to PMA lane mapping for a flipped plug orientation
+ *
+ * PMA LANE     C       D                 E
+ * 0          DP TX1   USB TX1/DP TX1   DP TX1
+ * 1          DP TX0   USB RX1/DP TX0   DP TX0
+ * 2          DP TX3   USB RX0          DP TX3
+ * 3          DP TX2   USB TX0          DP TX2
+ */
+static void udphy_set_default_mode(struct cix_udphy *udphy)
+{
+	udphy->cur_mode = UDPHY_MODE_NONE;
+}
+
+static int cix_udphy_probe(struct platform_device *pdev)
+{
+	struct cix_udphy *udphy;
+	struct device *dev = &pdev->dev;
+	struct phy_provider *phy_provider;
+	struct device_node *np = dev->of_node;
+	u32 id;
+	int ret = 0;
+
+	udphy = devm_kzalloc(dev, sizeof(*udphy), GFP_KERNEL);
+	if (!udphy)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, udphy);
+	udphy->dev = dev;
+
+	udphy->usbphy_syscon =
+		syscon_regmap_lookup_by_phandle_args(np, "cix,usbphy-syscon", 1,
+						     &id);
+	if (IS_ERR(udphy->usbphy_syscon))
+		return dev_err_probe(dev, PTR_ERR(udphy->usbphy_syscon),
+				     "failed to get usbphy syscon\n");
+	else if (id >= ARRAY_SIZE(sky1_udphy_con_dir))
+		return dev_err_probe(dev, -EINVAL, "failed to get id\n");
+	udphy->id = id;
+
+	udphy->cfg = device_get_match_data(dev);
+	if (!udphy->cfg) {
+		dev_err(dev, "no OF data can be matched with %pOF node\n", np);
+		return -EINVAL;
+	}
+
+	udphy->reset = devm_reset_control_get_exclusive(dev, "core");
+	if (IS_ERR(udphy->reset))
+		return dev_err_probe(dev, PTR_ERR(udphy->reset), "failed to get reset\n");
+
+	udphy->preset = devm_reset_control_get_exclusive(dev, "apb");
+	if (IS_ERR(udphy->preset))
+		return dev_err_probe(dev, PTR_ERR(udphy->preset), "failed to get preset\n");
+
+	ret = device_property_read_u8(dev, "cix,default-conf", &udphy->next_mode);
+	if (ret)
+		udphy->next_mode = UDPHY_MODE_DP_USB;
+
+	reset_control_assert(udphy->reset);
+	reset_control_assert(udphy->preset);
+
+	udphy->apb_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(udphy->apb_clk))
+		return dev_err_probe(dev, PTR_ERR(udphy->apb_clk), "phy apb clock not found\n");
+
+	udphy->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(udphy->base))
+		return dev_err_probe(dev, PTR_ERR(udphy->base), "fail to get phy register\n");
+
+	udphy->phy_regmap = devm_regmap_init(dev, NULL, udphy, &cix_udphy_regmap_cfg);
+	if (IS_ERR(udphy->phy_regmap))
+		return dev_err_probe(dev, PTR_ERR(udphy->phy_regmap),
+				     "failed to remap phy register\n");
+
+	mutex_init(&udphy->mutex);
+
+	if (device_property_present(dev, "orientation-switch")) {
+		ret = udphy_setup_orien_switch(udphy);
+		if (ret)
+			return ret;
+
+		ret = devm_add_action_or_reset(dev, udphy_orien_switch_unregister, udphy);
+		if (ret)
+			return ret;
+	}
+
+	if (device_property_present(dev, "mode-switch")) {
+		ret = udphy_setup_typec_mux(udphy);
+		if (ret)
+			return ret;
+
+		ret = devm_add_action_or_reset(dev, udphy_typec_mux_unregister, udphy);
+		if (ret)
+			return ret;
+	}
+
+	udphy_set_default_mode(udphy);
+
+	device_for_each_child_node_scoped(dev, child_fn) {
+		const char *name = fwnode_get_name(child_fn);
+		struct phy *phy;
+
+		if (!strncmp(name, "dp-port", 7))
+			phy = devm_phy_create(dev, to_of_node(child_fn),
+					      &cix_dp_phy_ops);
+		else if (!strncmp(name, "usb-port", 8))
+			phy = devm_phy_create(dev, to_of_node(child_fn),
+					      &cix_u3phy_ops);
+		else
+			continue;
+
+		if (IS_ERR(phy)) {
+			ret = PTR_ERR(phy);
+			dev_err(dev, "failed to create phy: %s\n", name);
+			return ret;
+		}
+
+		phy_set_drvdata(phy, udphy);
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "failed to register phy provider\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id cix_udphy_dt_match[] = {
+	{
+		.compatible = "cix,sky1-usbdp-phy",
+		.data = &sky1_udphy_cfg
+	},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, cix_udphy_dt_match);
+
+static struct platform_driver cix_udphy_driver = {
+	.probe		= cix_udphy_probe,
+	.driver		= {
+		.name	= "cix-usbdp-phy",
+		.of_match_table = cix_udphy_dt_match,
+	},
+};
+
+module_platform_driver(cix_udphy_driver);
+
+MODULE_AUTHOR("Chao Zeng <chao.zeng@cixtech.com>");
+MODULE_DESCRIPTION("Cix USBDP Combo PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/cix/phy-cix-usbdp.h b/drivers/phy/cix/phy-cix-usbdp.h
new file mode 100644
index 000000000000..561d02e70e5a
--- /dev/null
+++ b/drivers/phy/cix/phy-cix-usbdp.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PHY_CIX_USBDP_H_
+#define _PHY_CIX_USBDP_H_
+
+#define CMN_SSM_BIAS_TMR             0x0022
+#define CMN_PLLSM0_PLLPRE_TMR        0x002a
+#define CMN_PLLSM0_PLLLOCK_TMR       0x002c
+#define CMN_PLLSM1_PLLPRE_TMR        0x0032
+#define CMN_PLLSM1_PLLLOCK_TMR       0x0034
+#define CMN_BGCAL_INIT_TMR           0x0064
+#define CMN_BGCAL_ITER_TMR           0x0065
+#define CMN_IBCAL_INIT_TMR           0x0074
+#define CMN_PLL0_SS_CTRL1_M0         0x0098
+#define CMN_PLL0_SS_CTRL2_M0         0x0099
+#define CMN_PLL0_SS_CTRL3_M0         0x009a
+#define CMN_PLL0_SS_CTRL4_M0         0x009b
+#define CMN_PLL1_SS_CTRL1_M0         0x00d8
+#define CMN_PLL1_SS_CTRL2_M0         0x00d9
+#define CMN_PLL1_SS_CTRL3_M0         0x00da
+#define CMN_PLL1_SS_CTRL4_M0         0x00db
+#define CMN_TXPUCAL_INIT_TMR         0x0104
+#define CMN_TXPDCAL_INIT_TMR         0x010c
+#define CMN_RXCAL_INIT_TMR           0x0114
+#define CMN_SD_CAL_PLLCNT_START      0x0128
+#define CMN_PDIAG_PLL1_ITRIM_M0      0x01c3
+#define CMN_DIAG_GPANA_0             0x01f0
+
+#define RX_SDCAL0_INIT_TMR_LANE0     0x8044
+#define RX_SDCAL0_INIT_TMR_LANE1     0x8244
+#define RX_SDCAL0_INIT_TMR_LANE2     0x8444
+#define RX_SDCAL0_INIT_TMR_LANE3     0x8644
+
+#define RX_SDCAL0_ITER_TMR_LANE0     0x8045
+#define RX_SDCAL0_ITER_TMR_LANE1     0x8245
+#define RX_SDCAL0_ITER_TMR_LANE2     0x8445
+#define RX_SDCAL0_ITER_TMR_LANE3     0x8645
+
+#define RX_SDCAL1_INIT_TMR_LANE0     0x804c
+#define RX_SDCAL1_INIT_TMR_LANE1     0x824c
+#define RX_SDCAL1_INIT_TMR_LANE2     0x844c
+#define RX_SDCAL1_INIT_TMR_LANE3     0x864c
+
+#define RX_SDCAL1_ITER_TMR_LANE0     0x804d
+#define RX_SDCAL1_ITER_TMR_LANE1     0x824d
+#define RX_SDCAL1_ITER_TMR_LANE2     0x844d
+#define RX_SDCAL1_ITER_TMR_LANE3     0x864d
+
+#define TX_RCVDET_ST_TMR_LANE0       0x4123
+#define TX_RCVDET_ST_TMR_LANE1       0x4323
+#define TX_RCVDET_ST_TMR_LANE2       0x4523
+#define TX_RCVDET_ST_TMR_LANE3       0x4723
+
+#define CMN_PDIAG_PLL0_CLK_SEL_M0    0x01a1
+#define CMN_PDIAG_PLL1_CLK_SEL_M0    0x01c1
+
+#define XCVR_DIAG_HSCLK_SEL_LANE0    0x40e6
+#define XCVR_DIAG_HSCLK_SEL_LANE1    0x42e6
+#define XCVR_DIAG_HSCLK_SEL_LANE2    0x44e6
+#define XCVR_DIAG_HSCLK_SEL_LANE3    0x46e6
+
+#define XCVR_DIAG_HSCLK_DIV_LANE0    0x40e7
+#define XCVR_DIAG_HSCLK_DIV_LANE1    0x42e7
+#define XCVR_DIAG_HSCLK_DIV_LANE2    0x44e7
+#define XCVR_DIAG_HSCLK_DIV_LANE3    0x46e7
+
+#define XCVR_DIAG_PLLDRC_CTRL_LANE0  0x40e5
+#define XCVR_DIAG_PLLDRC_CTRL_LANE1  0x42e5
+#define XCVR_DIAG_PLLDRC_CTRL_LANE2  0x44e5
+#define XCVR_DIAG_PLLDRC_CTRL_LANE3  0x46e5
+
+#define XCVR_DIAG_BIDI_CTRL_LANE0    0x40ea
+#define XCVR_DIAG_BIDI_CTRL_LANE1    0x42ea
+#define XCVR_DIAG_BIDI_CTRL_LANE2    0x44ea
+#define XCVR_DIAG_BIDI_CTRL_LANE3    0x46ea
+
+#define CMN_PLL0_DSM_DIAG_M0         0x0094
+#define CMN_PLL1_DSM_DIAG_M0         0x00d4
+#define CMN_PDIAG_PLL0_CP_PADJ_M0    0x01a4
+#define CMN_PDIAG_PLL1_CP_PADJ_M0    0x01c4
+
+#define CMN_PDIAG_PLL0_CP_IADJ_M0    0x01a5
+#define CMN_PDIAG_PLL1_CP_IADJ_M0    0x01c5
+#define CMN_PDIAG_PLL0_FILT_PADJ_M0  0x01a6
+#define CMN_PDIAG_PLL1_FILT_PADJ_M0  0x01c6
+
+#define CMN_PLL0_INTDIV_M0           0x0090
+#define CMN_PLL1_INTDIV_M0           0x00d0
+#define CMN_PLL0_FRACDIVL_M0         0x0091
+#define CMN_PLL1_FRACDIVL_M0         0x00d1
+
+#define CMN_PLL0_FRACDIVH_M0         0x0092
+#define CMN_PLL1_FRACDIVH_M0         0x00d2
+#define CMN_PLL0_HIGH_THR_M0         0x0093
+#define CMN_PLL1_HIGH_THR_M0         0x00d3
+
+#define CMN_PDIAG_PLL0_CTRL_M0       0x01a0
+#define CMN_PDIAG_PLL1_CTRL_M0       0x01c0
+
+#define CMN_PLL0_VCOCAL_TCTRL        0x0082
+#define CMN_PLL1_VCOCAL_TCTRL        0x00c2
+
+#define CMN_PLL0_VCOCAL_INIT_TMR     0x0084
+#define CMN_PLL1_VCOCAL_INIT_TMR     0x00c4
+
+#define CMN_PLL0_VCOCAL_ITER_TMR     0x0085
+#define CMN_PLL1_VCOCAL_ITER_TMR     0x00c5
+
+#define CMN_PLL0_VCOCAL_REFTIM_START 0x0086
+#define CMN_PLL1_VCOCAL_REFTIM_START 0x00c6
+
+#define CMN_PLL0_VCOCAL_PLLCNT_START 0x0088
+#define CMN_PLL1_VCOCAL_PLLCNT_START 0x00c8
+
+#define CMN_PLL0_LOCK_REFCNT_START   0x009c
+#define CMN_PLL1_LOCK_REFCNT_START   0x00dc
+
+#define CMN_PLL0_LOCK_PLLCNT_START   0x009e
+#define CMN_PLL1_LOCK_PLLCNT_START   0x00de
+
+#define CMN_PLL0_LOCK_PLLCNT_THR     0x009f
+#define CMN_PLL1_LOCK_PLLCNT_THR     0x00df
+
+#define PHY_PMA_LANE_MAP             0xc010
+#define PHY_LANE_OFF_CTRL            0xc011
+
+#define PHY_PIPE_USB3_GEN2_PRE_CFG0  0xc018
+#define PHY_PIPE_USB3_GEN2_POST_CFG0 0xc01a
+#define PHY_PIPE_USB3_GEN2_POST_CFG1 0xc01b
+
+#define CMN_CDIAG_CDB_PWRI_OVRD      0x0041
+#define CMN_CDIAG_XCVRC_PWRI_OVRD    0x0047
+
+#define TX_PSC_A0_LANE0              0x4100
+#define TX_PSC_A0_LANE1              0x4300
+#define TX_PSC_A0_LANE2              0x4500
+#define TX_PSC_A0_LANE3              0x4700
+
+#define TX_PSC_A1_LANE0              0x4101
+#define TX_PSC_A1_LANE3              0x4701
+
+#define TX_PSC_A2_LANE0              0x4102
+#define TX_PSC_A2_LANE1              0x4302
+#define TX_PSC_A2_LANE2              0x4502
+#define TX_PSC_A2_LANE3              0x4702
+
+#define TX_PSC_A3_LANE0              0x4103
+#define TX_PSC_A3_LANE1              0x4303
+#define TX_PSC_A3_LANE2              0x4503
+#define TX_PSC_A3_LANE3              0x4703
+
+#define RX_PSC_A0_LANE0              0x8000
+#define RX_PSC_A0_LANE1              0x8200
+#define RX_PSC_A0_LANE2              0x8400
+#define RX_PSC_A0_LANE3              0x8600
+
+#define RX_PSC_A1_LANE1              0x8201
+#define RX_PSC_A1_LANE2              0x8401
+
+#define RX_PSC_A2_LANE0              0x8002
+#define RX_PSC_A2_LANE1              0x8202
+#define RX_PSC_A2_LANE2              0x8402
+#define RX_PSC_A2_LANE3              0x8602
+
+#define RX_PSC_A3_LANE0              0x8003
+#define RX_PSC_A3_LANE1              0x8203
+#define RX_PSC_A3_LANE2              0x8403
+#define RX_PSC_A3_LANE3              0x8603
+
+#define RX_PSC_CAL_LANE0             0x8006
+#define RX_PSC_CAL_LANE1             0x8206
+#define RX_PSC_CAL_LANE2             0x8406
+#define RX_PSC_CAL_LANE3             0x8606
+
+#define TX_TXCC_CTRL_LANE0           0x4040
+#define TX_TXCC_CTRL_LANE1           0x4240
+#define TX_TXCC_CTRL_LANE2           0x4440
+#define TX_TXCC_CTRL_LANE3           0x4640
+
+#define TX_TXCC_CPOST_MULT_00_LANE0  0x404c
+#define TX_TXCC_CPOST_MULT_00_LANE1  0x424c
+#define TX_TXCC_CPOST_MULT_00_LANE2  0x444c
+#define TX_TXCC_CPOST_MULT_00_LANE3  0x464c
+
+#define TX_TXCC_CPOST_MULT_01_LANE0  0x404d
+#define TX_TXCC_CPOST_MULT_01_LANE3  0x464d
+
+#define TX_TXCC_MGNFS_MULT_000_LANE0 0x4050
+#define TX_TXCC_MGNFS_MULT_000_LANE1 0x4250
+#define TX_TXCC_MGNFS_MULT_000_LANE2 0x4450
+#define TX_TXCC_MGNFS_MULT_000_LANE3 0x4650
+
+#define TX_DIAG_ACYA_LANE0           0x41E7
+#define TX_DIAG_ACYA_LANE1           0x43E7
+#define TX_DIAG_ACYA_LANE2           0x45E7
+#define TX_DIAG_ACYA_LANE3           0x47E7
+
+#define DRV_DIAG_TX_DRV_LANE0        0x40c6
+#define DRV_DIAG_TX_DRV_LANE1        0x42c6
+#define DRV_DIAG_TX_DRV_LANE2        0x44c6
+#define DRV_DIAG_TX_DRV_LANE3        0x46c6
+
+#define RX_SIGDET_HL_FILT_TMR_LANE1  0x8290
+#define RX_SIGDET_HL_FILT_TMR_LANE2  0x8490
+
+#define RX_REE_GCSM1_CTRL_LANE0      0x8108
+#define RX_REE_GCSM1_CTRL_LANE1      0x8308
+#define RX_REE_GCSM1_CTRL_LANE2      0x8508
+#define RX_REE_GCSM1_CTRL_LANE3      0x8708
+
+#define RX_REE_GCSM2_CTRL_LANE0      0x8110
+#define RX_REE_GCSM2_CTRL_LANE1      0x8310
+#define RX_REE_GCSM2_CTRL_LANE2      0x8510
+#define RX_REE_GCSM2_CTRL_LANE3      0x8710
+
+#define RX_REE_PERGCSM_CTRL_LANE0    0x8118
+#define RX_REE_PERGCSM_CTRL_LANE1    0x8318
+#define RX_REE_PERGCSM_CTRL_LANE2    0x8518
+#define RX_REE_PERGCSM_CTRL_LANE3    0x8718
+
+#define RX_REE_ATTEN_THR_LANE1       0x8349
+#define RX_REE_ATTEN_THR_LANE2       0x8549
+
+#define RX_REE_SMGM_CTRL1_LANE1      0x8377
+#define RX_REE_SMGM_CTRL1_LANE2      0x8577
+
+#define RX_REE_SMGM_CTRL2_LANE1      0x8378
+#define RX_REE_SMGM_CTRL2_LANE2      0x8578
+
+#define XCVR_DIAG_PSC_OVRD_LANE1     0x42eb
+#define XCVR_DIAG_PSC_OVRD_LANE2     0x44eb
+
+#define RX_DIAG_SIGDET_TUNE_LANE1    0x83e8
+#define RX_DIAG_SIGDET_TUNE_LANE2    0x85e8
+
+#define RX_DIAG_NQST_CTRL_LANE1      0x83e5
+#define RX_DIAG_NQST_CTRL_LANE2      0x85e5
+
+#define RX_DIAG_DFE_AMP_TUNE_2_LANE1 0x83e2
+#define RX_DIAG_DFE_AMP_TUNE_2_LANE2 0x85e2
+
+#define RX_DIAG_DFE_AMP_TUNE_3_LANE1 0x83e3
+#define RX_DIAG_DFE_AMP_TUNE_3_LANE2 0x85e3
+
+#define RX_DIAG_PI_CAP_LANE1         0x83f5
+#define RX_DIAG_PI_CAP_LANE2         0x85f5
+
+#define RX_DIAG_PI_RATE_LANE1        0x83f4
+#define RX_DIAG_PI_RATE_LANE2        0x85f4
+
+#define RX_DIAG_ACYA_LANE0           0x81ff
+#define RX_DIAG_ACYA_LANE3           0x87ff
+
+#define RX_CDRLF_CNFG_LANE1          0x8280
+#define RX_CDRLF_CNFG_LANE2          0x8480
+
+#define RX_CDRLF_CNFG3_LANE1         0x8282
+#define RX_CDRLF_CNFG3_LANE2         0x8482
+
+#define PHY_PMA_PLL_CTRL             0xe003
+
+#define PHY_PMA_ISO_PLL_CTRL0        0xe005
+#define PHY_PMA_ISO_PLL_CTRL1        0xe006
+
+#endif
-- 
2.50.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY
  2026-11-02  8:15 [PATCH 0/3] phy: cix: add Sky1 USB/DP combo PHY Gary Yang
  2026-11-02  8:15 ` [PATCH 1/3] dt-bindings: phy: add CIX " Gary Yang
  2026-11-02  8:15 ` [PATCH 2/3] phy: cix: add Sky1 USB/DP combo PHY driver Gary Yang
@ 2026-11-02  8:15 ` Gary Yang
  2026-11-02  8:41   ` sashiko-bot
  2 siblings, 1 reply; 9+ messages in thread
From: Gary Yang @ 2026-11-02  8:15 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: CIX Linux Kernel Upstream Group, linux-arm-kernel, linux-phy,
	devicetree, linux-kernel, Gary Yang

Add the four USB/DP combo PHY nodes to the Sky1 SoC device tree and
enable them on the Radxa Orion O6.

Each PHY node describes its USB and DisplayPort lanes as child nodes,
points at the S5 system controller instance that controls lane muxing
and plug orientation, and is disabled by default so that boards that do
not route the port stay unaffected.

Reviewed-by: Fugang Duan <fugang.duan@cixtech.com>
Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 28 +++++++
 arch/arm64/boot/dts/cix/sky1.dtsi         | 96 +++++++++++++++++++++++
 2 files changed, 124 insertions(+)

diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
index e39c87774c12..76f75185b6a8 100644
--- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
+++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
@@ -117,3 +117,31 @@ &s5_gpio2 {
 &uart2 {
 	status = "okay";
 };
+
+&usbc_phy0 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
+
+&usbc_phy1 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
+
+&usbc_phy2 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
+
+&usbc_phy3 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index 0a820f45feb9..672aade42d6f 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -606,6 +606,102 @@ mbox_ap2sfh: mailbox@80a0000 {
 			cix,mbox-dir = "tx";
 		};
 
+		usbc_phy0: usb-phy@9030000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x9030000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_DRD_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY0_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY0_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 0>;
+
+			status = "disabled";
+
+			usb3_phy0: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc0_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
+		usbc_phy1: usb-phy@90a0000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x90a0000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_0_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY1_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY1_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 1>;
+
+			status = "disabled";
+
+			usb3_phy1: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc1_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
+		usbc_phy2: usb-phy@9110000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x9110000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_1_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY2_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY2_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 2>;
+
+			status = "disabled";
+
+			usb3_phy2: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc2_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
+		usbc_phy3: usb-phy@9180000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x9180000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_2_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY3_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY3_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 3>;
+
+			status = "disabled";
+
+			usb3_phy3: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc3_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
 		pcie_x8_rc: pcie@a010000 {
 			compatible = "cix,sky1-pcie-host";
 			reg = <0x00 0x0a010000 0x00 0x10000>,
-- 
2.50.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY
  2026-11-02  8:21 [PATCH 0/3] phy: " Gary Yang
@ 2026-11-02  8:21 ` Gary Yang
  2026-11-02  8:52   ` sashiko-bot
  0 siblings, 1 reply; 9+ messages in thread
From: Gary Yang @ 2026-11-02  8:21 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Fugang Duan
  Cc: CIX Linux Kernel Upstream Group, linux-arm-kernel, linux-phy,
	devicetree, linux-kernel, Gary Yang

Add the four USB/DP combo PHY nodes to the Sky1 SoC device tree and
enable them on the Radxa Orion O6.

Each PHY node describes its USB and DisplayPort lanes as child nodes,
points at the S5 system controller instance that controls lane muxing
and plug orientation, and is disabled by default so that boards that do
not route the port stay unaffected.

Reviewed-by: Fugang Duan <fugang.duan@cixtech.com>
Signed-off-by: Gary Yang <gary.yang@cixtech.com>
---
 arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 28 +++++++
 arch/arm64/boot/dts/cix/sky1.dtsi         | 96 +++++++++++++++++++++++
 2 files changed, 124 insertions(+)

diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
index e39c87774c12..76f75185b6a8 100644
--- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
+++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
@@ -117,3 +117,31 @@ &s5_gpio2 {
 &uart2 {
 	status = "okay";
 };
+
+&usbc_phy0 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
+
+&usbc_phy1 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
+
+&usbc_phy2 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
+
+&usbc_phy3 {
+	orientation-switch;
+	mode-switch;
+	cix,default-conf = /bits/ 8 <0x03>;
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index 0a820f45feb9..672aade42d6f 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -606,6 +606,102 @@ mbox_ap2sfh: mailbox@80a0000 {
 			cix,mbox-dir = "tx";
 		};
 
+		usbc_phy0: usb-phy@9030000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x9030000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_DRD_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY0_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY0_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 0>;
+
+			status = "disabled";
+
+			usb3_phy0: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc0_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
+		usbc_phy1: usb-phy@90a0000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x90a0000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_0_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY1_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY1_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 1>;
+
+			status = "disabled";
+
+			usb3_phy1: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc1_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
+		usbc_phy2: usb-phy@9110000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x9110000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_1_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY2_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY2_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 2>;
+
+			status = "disabled";
+
+			usb3_phy2: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc2_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
+		usbc_phy3: usb-phy@9180000 {
+			compatible = "cix,sky1-usbdp-phy";
+			reg = <0x00 0x9180000 0x00 0x40000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&scmi_clk CLK_TREE_USB3C_2_PHY3_GATE>;
+			resets = <&s5_syscon SKY1_USB_DP_PHY3_RST_N>,
+				 <&s5_syscon SKY1_USB_DP_PHY3_PRST_N>;
+			reset-names = "core", "apb";
+			cix,usbphy-syscon = <&s5_syscon 3>;
+
+			status = "disabled";
+
+			usb3_phy3: usb-port@0 {
+				reg = <0x0>;
+				#phy-cells = <0>;
+			};
+
+			usbc3_dp_phy: dp-port@1 {
+				reg = <0x1>;
+				#phy-cells = <0>;
+			};
+		};
+
 		pcie_x8_rc: pcie@a010000 {
 			compatible = "cix,sky1-pcie-host";
 			reg = <0x00 0x0a010000 0x00 0x10000>,
-- 
2.50.1


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] dt-bindings: phy: add CIX Sky1 USB/DP combo PHY
  2026-11-02  8:15 ` [PATCH 1/3] dt-bindings: phy: add CIX " Gary Yang
@ 2026-11-02  8:27   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-02  8:27 UTC (permalink / raw)
  To: Gary Yang
  Cc: linux-phy, devicetree, robh, conor+dt, vkoul, neil.armstrong,
	olteanv

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] dt-bindings: phy: cix,sky1-usbdp-phy: missing OF graph port/ports for Type-C
- [Low] dt-bindings: phy: cix,sky1-usbdp-phy: dummy child nodes and policy property
--

commit 861d429c69ca8c486ffb6fda39bbf7f07f828ee7
Author: Gary Yang <gary.yang@cixtech.com>

dt-bindings: phy: add CIX Sky1 USB/DP combo PHY

This commit adds the Device Tree binding schema for the CIX Sky1 SoC
USB/DP combo PHY, describing its clocks, resets, system controller phandle,
Type-C switch properties, and child nodes for the USB and DisplayPort lanes.

> diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
> new file mode 100644
> index 0000000000000..a5b6943f84ebd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
[ ... ]
> +  orientation-switch:
> +    description:
> +      The PHY handles Type-C plug orientation switching.
> +    type: boolean
> +
> +  mode-switch:
> +    description:
> +      The PHY handles multiplexing of the USB and DisplayPort lanes.
> +    type: boolean
[ ... ]
> +  cix,default-conf:
> +    $ref: /schemas/types.yaml#/definitions/uint8
> +    enum: [0, 1, 2, 3]
> +    default: 3
> +    description: |
> +      Lane mode the PHY is put in until a consumer changes it through the
> +      Type-C mux. The value is a bit mask: 0 = none, 1 = USB, 2 = DP,
> +      3 = USB and DP.
> +
> +patternProperties:
> +  "^usb-port@[0-9a-f]+$":
> +    type: object
> +    description:
> +      The USB lane of the combo PHY.
> +
> +    properties:
> +      reg:
> +        maxItems: 1
> +
> +      "#phy-cells":
> +        const: 0
> +
> +    required:
> +      - reg
> +      - "#phy-cells"
> +
> +    additionalProperties: false
> +
> +  "^dp-port@[0-9a-f]+$":
> +    type: object
> +    description:
> +      The DisplayPort lane of the combo PHY.
> +
> +    properties:
> +      reg:
> +        maxItems: 1
> +
> +      "#phy-cells":
> +        const: 0
> +
> +    required:
> +      - reg
> +      - "#phy-cells"
> +
> +    additionalProperties: false

[Severity: Low]
Why are child nodes and cix,default-conf needed in the binding here?

The usb-port and dp-port child nodes only contain reg and #phy-cells = <0>
with no per-lane hardware resources (such as separate clocks, resets,
regulators, or OF graph ports), and the driver in cix_udphy_probe() does not
read the child reg property at all. When combo PHY sub-lanes have no
independent DT resources, could the parent PHY node expose both lanes
directly using #phy-cells = <1> (for example taking PHY_TYPE_USB3 and
PHY_TYPE_DP) instead of requiring #address-cells, #size-cells, and dummy
child nodes with arbitrary unit addresses?

Similarly, cix,default-conf describes the initial software lane mode prior
to Type-C mux updates rather than a hardware property of the PHY, and both
the binding and driver already default to 3. Could cix,default-conf be
dropped from the binding so the driver initializes its default state
directly?

> +
> +required:
> +  - compatible
> +  - reg
> +  - "#address-cells"
> +  - "#size-cells"
> +  - clocks
> +  - resets
> +  - reset-names
> +  - cix,usbphy-syscon
> +
> +additionalProperties: false

[Severity: Medium]
How can a Type-C connector or controller link to this PHY when
orientation-switch and mode-switch are used?

In the Type-C mux and switch core (fwnode_typec_switch_get() ->
fwnode_connection_find_matches() -> fwnode_graph_devcon_matches() ->
typec_switch_match() and fwnode_typec_mux_get() -> typec_mux_match() in
drivers/usb/typec/mux.c), the boolean orientation-switch and mode-switch
properties on a provider node are matched by traversing OF graph endpoints
(port or ports) from the Type-C connector node to the provider node:

drivers/usb/typec/mux.c:typec_switch_match() {
	if (id && !fwnode_property_present(fwnode, id))
		return NULL;
	...
}

However, Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
defines orientation-switch and mode-switch inline and sets
additionalProperties: false without defining port or ports properties or
referencing /schemas/usb/usb-switch.yaml# and
/schemas/usb/usb-switch-ports.yaml#.

As a result, any board Device Tree that connects a Type-C connector to this
PHY via OF graph will fail dtbs_check validation, while omitting the OF
graph links prevents fwnode_typec_switch_get() and fwnode_typec_mux_get()
from discovering the PHY's switch and mux at runtime. Should this schema
reference usb-switch.yaml and define the required OF graph port/ports nodes?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102081532.2305142-1-gary.yang@cixtech.com?part=1

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] phy: cix: add Sky1 USB/DP combo PHY driver
  2026-11-02  8:15 ` [PATCH 2/3] phy: cix: add Sky1 USB/DP combo PHY driver Gary Yang
@ 2026-11-02  8:31   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-02  8:31 UTC (permalink / raw)
  To: Gary Yang
  Cc: linux-phy, devicetree, robh, conor+dt, vkoul, neil.armstrong,
	olteanv

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] phy: cix: wrong link_rate units and early PLL disable in udphy_dp_phy_set_rate()
- [High] phy: cix: lane 0 voltage and pre-emphasis applied to all lanes in udphy_dp_phy_set_voltages()
- [High] phy: cix: missing .init callback in cix_u3phy_ops alongside .exit
- [High] phy: cix: clock enable leak and broken mode/orientation state in cix_usbdp_phy_power_on()
- [High] phy: cix: unclocked MMIO access and missing lane bound in udphy_dp_phy_configure()
- [Medium] phy: cix: missing SVID check for DisplayPort states in usbdp_typec_mux_set()
- [Medium] phy: cix: probe resets PHY despite bootloader handover claim and dead macros
--

commit ba3650ed160ebad8a4d82ef366b4d21f8c1a0ce7
Author: Gary Yang <gary.yang@cixtech.com>

phy: cix: add Sky1 USB/DP combo PHY driver

This commit adds a Generic PHY driver for the CIX Sky1 USB3/DisplayPort combo
PHY, exposing separate USB and DP child PHYs and registering a Type-C
orientation switch and mode mux.

> diff --git a/drivers/phy/cix/phy-cix-usbdp.c b/drivers/phy/cix/phy-cix-usbdp.c
> new file mode 100644
> index 0000000000000..d596e89c74f76
> --- /dev/null
> +++ b/drivers/phy/cix/phy-cix-usbdp.c
> @@ -0,0 +1,1477 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * phy driver for cdn_sd0804_t7g_typec
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/usb/typec_dp.h>
> +#include <linux/usb/typec_mux.h>
> +
> +#include "phy-cix-usbdp.h"
> +
> +enum {
> +	UDPHY_MODE_NONE		= 0,
> +	UDPHY_MODE_USB		= BIT(0),
> +	UDPHY_MODE_DP		= BIT(1),
> +	UDPHY_MODE_DP_USB	= BIT(1) | BIT(0),
> +};
> +
> +#define PHY_LANE_MUX_USB			0
> +#define PHY_LANE_MUX_DP				1

[ ... ]

> +static int sky1_udphy_exit(struct cix_udphy *udphy)
> +{
> +	reset_control_assert(udphy->reset);
> +	reset_control_assert(udphy->preset);
> +	clk_disable_unprepare(udphy->apb_clk);
> +	return 0;
> +}
> +
> +static int sky1_udphy_init(struct cix_udphy *udphy)
> +{
> +	int ret;
> +	u32 value;
> +	const struct cix_udphy_con_dir *con_dir = &udphy->cfg->con_dir[udphy->id];
> +
> +	regmap_update_bits(udphy->usbphy_syscon, con_dir->offset,
> +			   GENMASK(con_dir->bit, con_dir->bit),
> +			   udphy->flip << con_dir->bit);
> +
> +	usleep_range(10, 20);
> +
> +	regmap_read(udphy->usbphy_syscon, con_dir->offset, &value);
> +
> +	/* usb rcsu reset is default deassert */
> +	reset_control_assert(udphy->reset);
> +	reset_control_assert(udphy->preset);
> +
> +	ret = sky1_udphy_clk_enable(udphy->apb_clk);
> +	if (ret) {
> +		dev_err(udphy->dev, "Failed to prepare_enable udphy apb clock\n");
> +		return ret;
> +	}

[ ... ]

> +static int udphy_orien_sw_set(struct typec_switch_dev *sw,
> +			      enum typec_orientation orien)
> +{
> +	struct cix_udphy *udphy = typec_switch_get_drvdata(sw);
> +
> +	mutex_lock(&udphy->mutex);
> +
> +	if (orien == TYPEC_ORIENTATION_NONE)
> +		goto unlock_ret;
> +
> +	udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
> +
> +unlock_ret:
> +	mutex_unlock(&udphy->mutex);
> +	return 0;
> +}

[ ... ]

> +static int usbdp_typec_mux_set(struct typec_mux_dev *mux,
> +			       struct typec_mux_state *state)
> +{
> +	struct cix_udphy *udphy = typec_mux_get_drvdata(mux);
> +
> +	mutex_lock(&udphy->mutex);
> +
> +	switch (state->mode) {
> +	case TYPEC_DP_STATE_C:
> +		fallthrough;
> +	case TYPEC_DP_STATE_E:
> +		udphy->next_mode = UDPHY_MODE_DP;
> +		break;
> +	case TYPEC_DP_STATE_D:
> +		udphy->next_mode = UDPHY_MODE_DP_USB;
> +		break;
> +	case TYPEC_STATE_SAFE:
> +		udphy->next_mode = UDPHY_MODE_USB;
> +		break;
> +	default:
> +		/* usb only handle as usb+dp mode */
> +		udphy->next_mode = UDPHY_MODE_USB;
> +		break;
> +	}
> +
> +	/* TODO: check whether the HPD state needs software */
> +	mutex_unlock(&udphy->mutex);
> +
> +	return 0;
> +}

[Severity: Medium]
In drivers/phy/cix/phy-cix-usbdp.c, usbdp_typec_mux_set() switches directly
on state->mode without checking state->alt.

In include/linux/usb/typec_altmode.h and include/linux/usb/typec_dp.h, both
connector modal states (TYPEC_MODE_USB2 = 2, TYPEC_MODE_USB3 = 3,
TYPEC_MODE_USB4 = 4, TYPEC_MODE_AUDIO = 5, TYPEC_MODE_DEBUG = 6) and
DisplayPort pin assignment states (TYPEC_DP_STATE_A = 2 through
TYPEC_DP_STATE_F = 7) start at TYPEC_STATE_MODAL.

If the Type-C mux is called with a non-DisplayPort modal state such as
TYPEC_MODE_USB4, TYPEC_MODE_AUDIO, TYPEC_MODE_DEBUG, or another alternate
mode SVID, won't usbdp_typec_mux_set() match TYPEC_DP_STATE_C,
TYPEC_DP_STATE_D, or TYPEC_DP_STATE_E and misconfigure udphy->next_mode for
DisplayPort unless it first verifies state->alt && state->alt->svid ==
USB_TYPEC_DP_SID?

[ ... ]

> +static int cix_usbdp_phy_power_on(struct cix_udphy *udphy)
> +{
> +	const struct cix_udphy_cfg *phy_cfgs = udphy->cfg;
> +	int ret;
> +
> +	if (udphy->cur_mode == udphy->next_mode)
> +		return 0;
> +
> +	if ((udphy->cur_mode == UDPHY_MODE_USB || udphy->cur_mode == UDPHY_MODE_DP) &&
> +	    udphy->next_mode == UDPHY_MODE_DP_USB)
> +		return 0;
> +
> +	if (phy_cfgs->udphy_init) {
> +		ret = phy_cfgs->udphy_init(udphy);
> +		if (ret) {
> +			dev_err(udphy->dev, "failed to init udphy\n");
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cix_usbdp_phy_power_off(struct cix_udphy *udphy)
> +{
> +	const struct cix_udphy_cfg *phy_cfgs = udphy->cfg;
> +	int ret;
> +
> +	if (phy_cfgs->udphy_exit) {
> +		ret = phy_cfgs->udphy_exit(udphy);
> +		if (ret) {
> +			dev_err(udphy->dev, "failed to exit udphy\n");
> +			return ret;
> +		}
> +	}
> +
> +	udphy->cur_mode = UDPHY_MODE_NONE;
> +	return 0;
> +}
> +
> +static int udphy_dp_phy_power_on(struct phy *phy)
> +{
> +	struct cix_udphy *udphy = phy_get_drvdata(phy);
> +	int ret = 0;
> +
> +	mutex_lock(&udphy->mutex);
> +	if (!(udphy->next_mode & UDPHY_MODE_DP))
> +		goto unlock;
> +
> +	ret = cix_usbdp_phy_power_on(udphy);
> +	if (ret)
> +		goto unlock;
> +
> +	phy_set_bus_width(phy, udphy_dplane_get(udphy));
> +	if (udphy->next_mode == UDPHY_MODE_DP)
> +		udphy->cur_mode = UDPHY_MODE_DP;
> +	else
> +		udphy->cur_mode |= UDPHY_MODE_DP;
> +
> +unlock:
> +	mutex_unlock(&udphy->mutex);
> +	return ret;
> +}

[Severity: High]
How does udphy->cur_mode track both the active sub-PHYs and the programmed
hardware configuration in drivers/phy/cix/phy-cix-usbdp.c?

First, sky1_udphy_init() unconditionally calls
sky1_udphy_clk_enable(udphy->apb_clk), which calls clk_prepare_enable(), while
sky1_udphy_exit() calls clk_disable_unprepare(udphy->apb_clk) only once when
udphy->cur_mode reaches UDPHY_MODE_NONE. If one child PHY is already active
(for example, cix_u3phy_init() set udphy->cur_mode = UDPHY_MODE_USB) and the
mux switches to udphy->next_mode = UDPHY_MODE_DP before
udphy_dp_phy_power_on() runs, cix_usbdp_phy_power_on() calls sky1_udphy_init()
a second time and leaks an enable reference on udphy->apb_clk. In that same
path, udphy_dp_phy_power_on() then assigns udphy->cur_mode = UDPHY_MODE_DP,
clearing UDPHY_MODE_USB so that a later cix_u3phy_exit() becomes a no-op.

Second, if udphy_dp_phy_power_on() initializes the PHY with udphy->next_mode
== UDPHY_MODE_DP, sky1_udphy_init() programs sky1_udphy_dp_conf (4-lane
DP-only mode) and sets udphy->cur_mode = UDPHY_MODE_DP. If the mux later
switches udphy->next_mode to UDPHY_MODE_DP_USB and cix_u3phy_init() calls
cix_usbdp_phy_power_on(), the check:

	if ((udphy->cur_mode == UDPHY_MODE_USB || udphy->cur_mode == UDPHY_MODE_DP) &&
	    udphy->next_mode == UDPHY_MODE_DP_USB)
		return 0;

returns early without calling sky1_udphy_init(), leaving the hardware in
4-lane DP-only mode with the USB3 lanes unconfigured.

Third, udphy_orien_sw_set() updates udphy->flip without marking the PHY as
needing re-initialization. If udphy->flip changes after one sub-PHY has
already initialized in UDPHY_MODE_DP_USB, won't cix_usbdp_phy_power_on() skip
sky1_udphy_init(), leaving the syscon orientation bit and lane configuration
tables in the old orientation while udphy_dp_phy_set_rate() and
udphy_dp_phy_set_voltages() program the flipped PMA lanes?

[ ... ]

> +static int udphy_dp_phy_verify_config(struct cix_udphy *udphy,
> +				      struct phy_configure_opts_dp *dp)
> +{
> +	int i;
> +
> +	switch (dp->lanes) {
> +	case 1:
> +	case 2:
> +	case 4:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * If changing voltages is required, check swing and pre-emphasis
> +	 * levels, per-lane.
> +	 */
> +	if (dp->set_voltages) {
> +		for (i = 0; i < dp->lanes; i++) {
> +			if (dp->voltage[i] > 3 || dp->pre[i] > 3)
> +				return -EINVAL;
> +
> +			/*
> +			 * Sum of voltage swing and pre-emphasis levels cannot
> +			 * exceed 3.
> +			 */
> +			if (dp->voltage[i] + dp->pre[i] > 3)
> +				return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int udphy_dp_phy_set_rate(struct cix_udphy *udphy,
> +				 struct phy_configure_opts_dp *dp)
> +{
> +	int ret;
> +	const struct phy_link_cfg *conf = NULL;
> +
> +	dev_dbg(udphy->dev, "lane %d, rate %x\n", dp->lanes, dp->link_rate);
> +
> +	regmap_update_bits(udphy->phy_regmap, PHY_PMA_PLL_CTRL, BIT(1), 0x0);
> +	usleep_range(10, 20);
> +
> +	switch (dp->link_rate) {
> +	case 0x6:
> +		conf = &link_cfg[0];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
> +		break;
> +	case 0xa:
> +		conf = &link_cfg[1];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
> +		break;
> +	case 0x14:
> +		conf = &link_cfg[2];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
> +		break;
> +	case 0x1e:
> +		conf = &link_cfg[3];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_8p1_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_8p1_conf));
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

[Severity: High]
In drivers/phy/cix/phy-cix-usbdp.c, udphy_dp_phy_set_rate() switches on
dp->link_rate using DPCD bandwidth code values (0x6, 0xa, 0x14, 0x1e).

According to include/linux/phy/phy-dp.h, struct phy_configure_opts_dp
defines link_rate in Mb/s (1620, 2700, 5400, 8100). When a standard
DisplayPort bridge or controller driver calls phy_configure() ->
udphy_dp_phy_configure() -> udphy_dp_phy_set_rate() with dp->set_rate = 1 and
dp->link_rate in Mb/s, won't it always hit the default case and fail with
-EINVAL?

Also, because udphy_dp_phy_verify_config() does not validate dp->link_rate and
udphy_dp_phy_set_rate() clears BIT(1) of PHY_PMA_PLL_CTRL before the switch
statement, returning -EINVAL on an unsupported rate leaves PLL1 disabled.

[ ... ]

> +static int udphy_dp_phy_set_voltages(struct cix_udphy *udphy,
> +				     struct phy_configure_opts_dp *dp)
> +{
> +	u32 index = dp->voltage[0] * 4 + dp->pre[0];
> +	const struct phy_voltage_conf *cfg = volt_cfg;
> +
> +	dev_dbg(udphy->dev, "lane %d, vs %d, pe %d\n", dp->lanes, dp->voltage[0],
> +		dp->pre[0]);
> +
> +	switch (dp->lanes) {
> +	case 1:
> +		if (!udphy->flip) { /* lane 2 */
> +			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
> +					   BIT(0), 0x1);
> +			regmap_write(udphy->phy_regmap, TX_TXCC_CTRL_LANE2,
> +				     cfg[index].tx_txcc_ctrl);
> +			regmap_write(udphy->phy_regmap, DRV_DIAG_TX_DRV_LANE2,
> +				     cfg[index].drv_diag_tx_drv);
> +			regmap_write(udphy->phy_regmap, TX_TXCC_MGNFS_MULT_000_LANE2,
> +				     cfg[index].tx_txcc_mgnfc_mult_000);
> +			regmap_write(udphy->phy_regmap, TX_TXCC_CPOST_MULT_00_LANE2,
> +				     cfg[index].tx_txcc_cpost_mult_00);
> +			regmap_update_bits(udphy->phy_regmap, TX_DIAG_ACYA_LANE2,
> +					   BIT(0), 0x0);

[Severity: High]
In drivers/phy/cix/phy-cix-usbdp.c, udphy_dp_phy_verify_config() validates
dp->voltage[i] and dp->pre[i] for each lane up to dp->lanes, but
udphy_dp_phy_set_voltages() computes index solely from dp->voltage[0] and
dp->pre[0] and writes cfg[index] to all active lanes in the 2-lane and 4-lane
cases.

During DisplayPort link training on a 2-lane or 4-lane link, if the sink
requests different voltage swing or pre-emphasis levels across lanes, won't
lanes 1..3 be programmed with lane 0's settings instead of dp->voltage[i] and
dp->pre[i]?

Could this also use a per-lane helper instead of duplicating the 6-register
programming sequence across all branches?

[ ... ]

> +static int udphy_dp_phy_configure(struct phy *phy,
> +				  union phy_configure_opts *opts)
> +{
> +	struct cix_udphy *udphy = phy_get_drvdata(phy);
> +	int ret;
> +
> +	mutex_lock(&udphy->mutex);
> +	ret = udphy_dp_phy_verify_config(udphy, &opts->dp);
> +	if (ret)
> +		goto unlock;
> +
> +	if (opts->dp.set_rate) {
> +		ret = udphy_dp_phy_set_rate(udphy, &opts->dp);
> +		if (ret) {
> +			dev_err(udphy->dev, "failed to set DP link rate\n");
> +			goto unlock;
> +		}
> +	}
> +
> +	if (opts->dp.set_voltages) {
> +		ret = udphy_dp_phy_set_voltages(udphy, &opts->dp);
> +		if (ret) {
> +			dev_err(udphy->dev, "dp_phy_set_voltages failed\n");
> +			goto unlock;
> +		}
> +	}
> +	mutex_unlock(&udphy->mutex);
> +	return 0;
> +
> +unlock:
> +	mutex_unlock(&udphy->mutex);
> +	return ret;
> +}

[Severity: High]
What happens if udphy_dp_phy_configure() in drivers/phy/cix/phy-cix-usbdp.c is
called before udphy_dp_phy_power_on(), or after udphy_dp_phy_power_on()
returned 0 early because !(udphy->next_mode & UDPHY_MODE_DP)?

In both cases, udphy->apb_clk is disabled and udphy->preset is asserted (from
cix_udphy_probe() or sky1_udphy_exit()), yet udphy_dp_phy_configure() proceeds
to call udphy_dp_phy_set_rate() and udphy_dp_phy_set_voltages(), performing
MMIO reads and writes on udphy->base without checking (udphy->cur_mode &
UDPHY_MODE_DP). Won't accessing those registers with the APB clock gated and
reset asserted cause a bus abort or hang?

Additionally, udphy_dp_phy_verify_config() allows dp->lanes == 4 even when
udphy->next_mode is UDPHY_MODE_DP_USB (where udphy_dplane_get(udphy) is 2),
which causes udphy_dp_phy_set_rate() and udphy_dp_phy_set_voltages() to
overwrite the PMA registers of the active USB3 lanes.

[ ... ]

> +static int cix_u3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
> +{
> +	if (mode == PHY_MODE_INVALID)
> +		return cix_u3phy_exit(phy);
> +
> +	return cix_u3phy_init(phy);
> +}
> +
> +static const struct phy_ops cix_u3phy_ops = {
> +	.set_mode       = cix_u3phy_set_mode,
> +	.exit		= cix_u3phy_exit,
> +	.owner		= THIS_MODULE,
> +};

[Severity: High]
In drivers/phy/cix/phy-cix-usbdp.c, cix_u3phy_ops defines .exit =
cix_u3phy_exit, but omits .init = cix_u3phy_init and only invokes
cix_u3phy_init() from cix_u3phy_set_mode().

When a USB controller driver calls phy_init() during probe before
phy_set_mode(), or calls phy_exit() followed by phy_init() across suspend and
resume without calling phy_set_mode(), won't phy_init() be a no-op that leaves
the USB3 PHY uninitialized and in reset?

[ ... ]

> +static void udphy_set_default_mode(struct cix_udphy *udphy)
> +{
> +	udphy->cur_mode = UDPHY_MODE_NONE;
> +}
> +
> +static int cix_udphy_probe(struct platform_device *pdev)
> +{
> +	struct cix_udphy *udphy;
> +	struct device *dev = &pdev->dev;
> +	struct phy_provider *phy_provider;
> +	struct device_node *np = dev->of_node;
> +	u32 id;
> +	int ret = 0;

[ ... ]

> +	ret = device_property_read_u8(dev, "cix,default-conf", &udphy->next_mode);
> +	if (ret)
> +		udphy->next_mode = UDPHY_MODE_DP_USB;
> +
> +	reset_control_assert(udphy->reset);
> +	reset_control_assert(udphy->preset);

[ ... ]

> +	udphy_set_default_mode(udphy);

[Severity: Medium]
The commit message states:

  The bootloader may hand over the port already initialized; in that
  case the first power_on calls skip the re-initialization.

However, in drivers/phy/cix/phy-cix-usbdp.c, cix_udphy_probe() unconditionally
calls reset_control_assert() on udphy->reset and udphy->preset, and
udphy_set_default_mode() unconditionally sets udphy->cur_mode =
UDPHY_MODE_NONE. As a result, any bootloader initialization is wiped out in
probe and the first power_on call never skips re-initialization. Should
cix_udphy_probe() and udphy_set_default_mode() be updated to match the commit
description, or should the commit message be updated to match the code?

Also, PHY_LANE_MUX_USB and PHY_LANE_MUX_DP at the top of
drivers/phy/cix/phy-cix-usbdp.c are unused and can be removed.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102081532.2305142-1-gary.yang@cixtech.com?part=2

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY
  2026-11-02  8:15 ` [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY Gary Yang
@ 2026-11-02  8:41   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-02  8:41 UTC (permalink / raw)
  To: Gary Yang
  Cc: linux-phy, devicetree, robh, conor+dt, vkoul, neil.armstrong,
	olteanv

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] arm64: dts: cix: enabling usbc_phy0..3 asserts PHY resets at boot
- [High] phy: cix: broken mode transitions, clock leak, and phy_ops in cix_udphy
- [Medium] arm64: dts: cix: missing OF graph ports for usbc_phy Type-C switches
--

commit d16228024f6482f816de21fc474d6f5027baff41
Author: Gary Yang <gary.yang@cixtech.com>

arm64: dts: cix: add Sky1 USB/DP combo PHY

This commit adds the four USB/DP combo PHY device tree nodes to the CIX Sky1
SoC dtsi and enables them with Type-C orientation-switch and mode-switch
properties on the Radxa Orion O6 board.

> diff --git a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
> index e39c87774c128..76f75185b6a8e 100644
> --- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
> +++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
> @@ -117,3 +117,31 @@ &s5_gpio2 {
>  &uart2 {
>  	status = "okay";
>  };
> +
> +&usbc_phy0 {
> +	orientation-switch;
> +	mode-switch;

[Severity: Medium]
How are Type-C connector nodes expected to link to the orientation-switch
and mode-switch registered by usbc_phy0 through usbc_phy3?

In drivers/phy/cix/phy-cix-usbdp.c, cix_udphy_probe() checks for these
boolean properties and registers a Type-C switch and mux on the PHY device
fwnode via udphy_setup_orien_switch() and udphy_setup_typec_mux():

drivers/phy/cix/phy-cix-usbdp.c:cix_udphy_probe() {
	...
	if (device_property_present(dev, "orientation-switch")) {
		ret = udphy_setup_orien_switch(udphy);
		...
	}

	if (device_property_present(dev, "mode-switch")) {
		ret = udphy_setup_typec_mux(udphy);
		...
	}
}

When a Type-C connector driver looks up its switch or mux through
fwnode_typec_switch_get() or fwnode_typec_mux_get(),
fwnode_connection_find_matches() -> fwnode_graph_devcon_matches() resolves
boolean orientation-switch and mode-switch providers by walking OF graph
endpoints (port or ports with remote-endpoint) from the connector node to
the provider node.

However, neither sky1.dtsi nor sky1-orion-o6.dts defines any port or ports
nodes on usbc_phy0..3, and
Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml sets
additionalProperties: false while only allowing usb-port@[0-9a-f]+ and
dp-port@[0-9a-f]+ child nodes:

  patternProperties:
    "^usb-port@[0-9a-f]+$":
      ...
    "^dp-port@[0-9a-f]+$":
      ...
  additionalProperties: false

Without OF graph port definitions in the binding and device tree nodes,
won't any usb-c-connector node be unable to bind to these switches or fail
DT schema validation?

> +	cix,default-conf = /bits/ 8 <0x03>;
> +	status = "okay";
> +};

[Severity: High]
Does enabling usbc_phy0 through usbc_phy3 here before any USB or DisplayPort
consumer nodes are present in the device tree break bootloader-initialized
USB or DisplayPort connections at boot?

Neither arch/arm64/boot/dts/cix/sky1.dtsi nor
arch/arm64/boot/dts/cix/sky1-orion-o6.dts currently has any consumer nodes
referencing usb3_phy0..3 or usbc0..3_dp_phy. When cix_udphy_probe() runs for
each enabled PHY node, it unconditionally asserts both the core and APB
resets and sets cur_mode to UDPHY_MODE_NONE:

drivers/phy/cix/phy-cix-usbdp.c:cix_udphy_probe() {
	...
	reset_control_assert(udphy->reset);
	reset_control_assert(udphy->preset);
	...
	udphy_set_default_mode(udphy);
}

drivers/phy/cix/phy-cix-usbdp.c:udphy_set_default_mode() {
	udphy->cur_mode = UDPHY_MODE_NONE;
}

Although commit ba3650ed160e ("phy: cix: add USB/DP combo PHY driver for
Sky1") states that the bootloader may hand over the port already initialized,
asserting reset and preset in cix_udphy_probe() while no consumers exist to
power the PHYs back on leaves all four PHYs permanently held in reset after
probe. Wouldn't this tear down any active bootloader-initialized DisplayPort
(such as an EFI framebuffer output) or USB link on Radxa Orion O6?

[ ... ]

> diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
> index 0a820f45feb96..672aade42d6f8 100644
> --- a/arch/arm64/boot/dts/cix/sky1.dtsi
> +++ b/arch/arm64/boot/dts/cix/sky1.dtsi
> @@ -606,6 +606,102 @@ mbox_ap2sfh: mailbox@80a0000 {
>  			cix,mbox-dir = "tx";
>  		};
>  
> +		usbc_phy0: usb-phy@9030000 {
> +			compatible = "cix,sky1-usbdp-phy";
> +			reg = <0x00 0x9030000 0x00 0x40000>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			clocks = <&scmi_clk CLK_TREE_USB3C_DRD_PHY3_GATE>;
> +			resets = <&s5_syscon SKY1_USB_DP_PHY0_RST_N>,
> +				 <&s5_syscon SKY1_USB_DP_PHY0_PRST_N>;
> +			reset-names = "core", "apb";
> +			cix,usbphy-syscon = <&s5_syscon 0>;
> +
> +			status = "disabled";
> +
> +			usb3_phy0: usb-port@0 {
> +				reg = <0x0>;
> +				#phy-cells = <0>;
> +			};
> +
> +			usbc0_dp_phy: dp-port@1 {
> +				reg = <0x1>;
> +				#phy-cells = <0>;
> +			};
> +		};

[Severity: High]
When these PHY nodes are enabled on Radxa Orion O6, can the cix,sky1-usbdp-phy
driver handle USB3 PHY initialization and Type-C mode transitions without
skipping lane reconfiguration or leaking udphy->apb_clk enable references?

First, in drivers/phy/cix/phy-cix-usbdp.c, cix_usbdp_phy_power_on() checks
udphy->cur_mode against udphy->next_mode:

drivers/phy/cix/phy-cix-usbdp.c:cix_usbdp_phy_power_on() {
	if (udphy->cur_mode == udphy->next_mode)
		return 0;

	if ((udphy->cur_mode == UDPHY_MODE_USB || udphy->cur_mode == UDPHY_MODE_DP) &&
	    udphy->next_mode == UDPHY_MODE_DP_USB)
		return 0;

	if (phy_cfgs->udphy_init) {
		ret = phy_cfgs->udphy_init(udphy);
		...
	}
}

If the PHY is brought up in 4-lane DP-only mode (udphy->cur_mode ==
UDPHY_MODE_DP, which programs sky1_udphy_dp_conf in sky1_udphy_init()) and
usbdp_typec_mux_set() later switches udphy->next_mode to UDPHY_MODE_DP_USB, a
subsequent call to cix_u3phy_init() -> cix_usbdp_phy_power_on() hits the early
return above and skips sky1_udphy_init(). Doesn't that leave the hardware in
4-lane DP-only configuration with USB3 non-functional?

Second, if USB is active (udphy->cur_mode == UDPHY_MODE_USB, with
udphy->apb_clk already enabled by sky1_udphy_init()) and udphy->next_mode
changes to UDPHY_MODE_DP, udphy_dp_phy_power_on() calls
cix_usbdp_phy_power_on() -> sky1_udphy_init(), which calls
sky1_udphy_clk_enable() -> clk_prepare_enable(udphy->apb_clk) a second time
without a preceding disable, and then overwrites udphy->cur_mode:

drivers/phy/cix/phy-cix-usbdp.c:udphy_dp_phy_power_on() {
	...
	ret = cix_usbdp_phy_power_on(udphy);
	if (ret)
		goto unlock;

	phy_set_bus_width(phy, udphy_dplane_get(udphy));
	if (udphy->next_mode == UDPHY_MODE_DP)
		udphy->cur_mode = UDPHY_MODE_DP;
	else
		udphy->cur_mode |= UDPHY_MODE_DP;
}

Because overwriting udphy->cur_mode clears the UDPHY_MODE_USB bit,
cix_u3phy_exit() becomes a no-op and cix_usbdp_phy_power_off() ->
sky1_udphy_exit() only calls clk_disable_unprepare(udphy->apb_clk) once. Does
this leak a prepare/enable reference on udphy->apb_clk?

Third, cix_u3phy_ops only wires up .set_mode and .exit, omitting .init and
.power_on:

drivers/phy/cix/phy-cix-usbdp.c:cix_u3phy_ops {
	.set_mode       = cix_u3phy_set_mode,
	.exit		= cix_u3phy_exit,
	.owner		= THIS_MODULE,
};

Won't standard USB controller drivers that call phy_init() and phy_power_on()
before accessing controller registers or before calling phy_set_mode()
operate while usb3_phy0..3 are still held in reset?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102081532.2305142-1-gary.yang@cixtech.com?part=3

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY
  2026-11-02  8:21 ` [PATCH 3/3] arm64: dts: " Gary Yang
@ 2026-11-02  8:52   ` sashiko-bot
  0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-02  8:52 UTC (permalink / raw)
  To: Gary Yang
  Cc: linux-phy, devicetree, robh, conor+dt, vkoul, neil.armstrong,
	olteanv

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] phy: cix: link rate unit mismatch and PLL disable in udphy_dp_phy_set_rate()
- [High] phy: cix: unclocked MMIO and lane check in udphy_dp_phy_configure()
- [High] phy: cix: clock leak and skipped re-init in cix_usbdp_phy_power_on()
- [Medium] phy: cix: per-lane voltages ignored in udphy_dp_phy_set_voltages()
- [Medium] dt-bindings: phy: cix,sky1-usbdp-phy: missing OF graph port properties
--

commit ffe7df98ad72d8c1a7362fd9f434e85648138040
Author: Gary Yang <gary.yang@cixtech.com>

arm64: dts: cix: add Sky1 USB/DP combo PHY

This series adds the device tree binding, driver, and device tree nodes for
the CIX Sky1 USB/DP combo PHY, supporting SuperSpeedPlus USB and DisplayPort
lane muxing and Type-C orientation switching via the S5 system controller.

> diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
> new file mode 100644
> index 0000000000000..a5b6943f84ebd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/cix,sky1-usbdp-phy.yaml
> @@ -0,0 +1,150 @@

[ ... ]

> +  orientation-switch:
> +    description:
> +      The PHY handles Type-C plug orientation switching.
> +    type: boolean
> +
> +  mode-switch:
> +    description:
> +      The PHY handles multiplexing of the USB and DisplayPort lanes.
> +    type: boolean

[Severity: Medium]
When a board device tree connects a usb-c-connector node to this PHY for
Type-C orientation and mode switching, the connector links to the provider
via OF graph port or ports endpoints (matched by fwnode_graph_devcon_matches()
in typec_switch_match() and typec_mux_match()).

Because this schema specifies additionalProperties: false without defining
port or ports properties, won't board device trees that wire a Type-C
connector to a cix,sky1-usbdp-phy node fail dtbs_check validation?

[ ... ]

> diff --git a/drivers/phy/cix/phy-cix-usbdp.c b/drivers/phy/cix/phy-cix-usbdp.c
> new file mode 100644
> index 0000000000000..d596e89c74f76
> --- /dev/null
> +++ b/drivers/phy/cix/phy-cix-usbdp.c
> @@ -0,0 +1,1477 @@

[ ... ]

> +static int sky1_udphy_init(struct cix_udphy *udphy)
> +{
> +	int ret;
> +	u32 value;
> +	const struct cix_udphy_con_dir *con_dir = &udphy->cfg->con_dir[udphy->id];
> +
> +	regmap_update_bits(udphy->usbphy_syscon, con_dir->offset,
> +			   GENMASK(con_dir->bit, con_dir->bit),
> +			   udphy->flip << con_dir->bit);
> +
> +	usleep_range(10, 20);
> +
> +	regmap_read(udphy->usbphy_syscon, con_dir->offset, &value);
> +
> +	/* usb rcsu reset is default deassert */
> +	reset_control_assert(udphy->reset);
> +	reset_control_assert(udphy->preset);
> +
> +	ret = sky1_udphy_clk_enable(udphy->apb_clk);
> +	if (ret) {
> +		dev_err(udphy->dev, "Failed to prepare_enable udphy apb clock\n");
> +		return ret;
> +	}

[ ... ]

> +static int udphy_orien_sw_set(struct typec_switch_dev *sw,
> +			      enum typec_orientation orien)
> +{
> +	struct cix_udphy *udphy = typec_switch_get_drvdata(sw);
> +
> +	mutex_lock(&udphy->mutex);
> +
> +	if (orien == TYPEC_ORIENTATION_NONE)
> +		goto unlock_ret;
> +
> +	udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
> +
> +unlock_ret:
> +	mutex_unlock(&udphy->mutex);
> +	return 0;
> +}

[ ... ]

> +static int cix_usbdp_phy_power_on(struct cix_udphy *udphy)
> +{
> +	const struct cix_udphy_cfg *phy_cfgs = udphy->cfg;
> +	int ret;
> +
> +	if (udphy->cur_mode == udphy->next_mode)
> +		return 0;
> +
> +	if ((udphy->cur_mode == UDPHY_MODE_USB || udphy->cur_mode == UDPHY_MODE_DP) &&
> +	    udphy->next_mode == UDPHY_MODE_DP_USB)
> +		return 0;
> +
> +	if (phy_cfgs->udphy_init) {
> +		ret = phy_cfgs->udphy_init(udphy);
> +		if (ret) {
> +			dev_err(udphy->dev, "failed to init udphy\n");
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}

[ ... ]

> +static int udphy_dp_phy_power_on(struct phy *phy)
> +{
> +	struct cix_udphy *udphy = phy_get_drvdata(phy);
> +	int ret = 0;
> +
> +	mutex_lock(&udphy->mutex);
> +	if (!(udphy->next_mode & UDPHY_MODE_DP))
> +		goto unlock;
> +
> +	ret = cix_usbdp_phy_power_on(udphy);
> +	if (ret)
> +		goto unlock;
> +
> +	phy_set_bus_width(phy, udphy_dplane_get(udphy));
> +	if (udphy->next_mode == UDPHY_MODE_DP)
> +		udphy->cur_mode = UDPHY_MODE_DP;
> +	else
> +		udphy->cur_mode |= UDPHY_MODE_DP;
> +
> +unlock:
> +	mutex_unlock(&udphy->mutex);
> +	return ret;
> +}

[Severity: High]
How are udphy->apb_clk and the hardware lane configuration kept in sync when
transitioning between Type-C modes and orientations?

First, if USB powers on first (setting udphy->cur_mode = UDPHY_MODE_USB and
enabling udphy->apb_clk once in sky1_udphy_init()), and the Type-C mux then
selects 4-lane DP (udphy->next_mode = UDPHY_MODE_DP) followed by
udphy_dp_phy_power_on():

  udphy_dp_phy_power_on()
    cix_usbdp_phy_power_on()
      sky1_udphy_init()
        sky1_udphy_clk_enable(udphy->apb_clk);
    udphy->cur_mode = UDPHY_MODE_DP;

sky1_udphy_init() calls clk_prepare_enable(udphy->apb_clk) a second time, and
udphy_dp_phy_power_on() overwrites udphy->cur_mode = UDPHY_MODE_DP, dropping
UDPHY_MODE_USB. When the PHYs later power off, sky1_udphy_exit() runs at most
once, leaking a prepare/enable reference on udphy->apb_clk.

Second, if DP powers on first in 4-lane mode (udphy->cur_mode = UDPHY_MODE_DP,
programmed with sky1_udphy_dp_conf) and udphy->next_mode later changes to
UDPHY_MODE_DP_USB, cix_usbdp_phy_power_on() hits the early return:

	if ((udphy->cur_mode == UDPHY_MODE_USB || udphy->cur_mode == UDPHY_MODE_DP) &&
	    udphy->next_mode == UDPHY_MODE_DP_USB)
		return 0;

Doesn't that skip reprogramming the PHY from 4-lane DP to the USB+DP combo
configuration when cix_u3phy_init() is called?

Third, if udphy_orien_sw_set() updates udphy->flip while one sub-PHY is
already active in udphy->cur_mode, cix_usbdp_phy_power_on() also returns 0
without updating udphy->usbphy_syscon or loading the flipped register table
(sky1_udphy_usbdp_conf_flip), while subsequent calls to
udphy_dp_phy_set_rate() and udphy_dp_phy_set_voltages() program the flipped
lanes.

[ ... ]

> +static int udphy_dp_phy_verify_config(struct cix_udphy *udphy,
> +				      struct phy_configure_opts_dp *dp)
> +{
> +	int i;
> +
> +	switch (dp->lanes) {
> +	case 1:
> +	case 2:
> +	case 4:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

[ ... ]

> +static int udphy_dp_phy_set_rate(struct cix_udphy *udphy,
> +				 struct phy_configure_opts_dp *dp)
> +{
> +	int ret;
> +	const struct phy_link_cfg *conf = NULL;
> +
> +	dev_dbg(udphy->dev, "lane %d, rate %x\n", dp->lanes, dp->link_rate);
> +
> +	regmap_update_bits(udphy->phy_regmap, PHY_PMA_PLL_CTRL, BIT(1), 0x0);
> +	usleep_range(10, 20);
> +
> +	switch (dp->link_rate) {
> +	case 0x6:
> +		conf = &link_cfg[0];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
> +		break;
> +	case 0xa:
> +		conf = &link_cfg[1];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
> +		break;
> +	case 0x14:
> +		conf = &link_cfg[2];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_partial_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_partial_conf));
> +		break;
> +	case 0x1e:
> +		conf = &link_cfg[3];
> +		ret = regmap_multi_reg_write(udphy->phy_regmap, sky1_udphy_dp_8p1_conf,
> +					     ARRAY_SIZE(sky1_udphy_dp_8p1_conf));
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

[Severity: High]
In include/linux/phy/phy-dp.h, struct phy_configure_opts_dp defines link_rate
in Mb/s (1620, 2700, 5400, 8100), whereas udphy_dp_phy_set_rate() matches
dp->link_rate against raw DPCD bandwidth codes (0x6, 0xa, 0x14, 0x1e).

Won't standard DisplayPort controller drivers passing link_rate in Mb/s to
phy_configure() -> udphy_dp_phy_configure() -> udphy_dp_phy_set_rate() always
hit the default case and fail with -EINVAL, while also leaving BIT(1) of
PHY_PMA_PLL_CTRL cleared?

[ ... ]

> +static int udphy_dp_phy_set_voltages(struct cix_udphy *udphy,
> +				     struct phy_configure_opts_dp *dp)
> +{
> +	u32 index = dp->voltage[0] * 4 + dp->pre[0];
> +	const struct phy_voltage_conf *cfg = volt_cfg;
> +
> +	dev_dbg(udphy->dev, "lane %d, vs %d, pe %d\n", dp->lanes, dp->voltage[0],
> +		dp->pre[0]);

[Severity: Medium]
udphy_dp_phy_verify_config() validates dp->voltage[i] and dp->pre[i] for each
lane i < dp->lanes, but udphy_dp_phy_set_voltages() computes index solely from
dp->voltage[0] and dp->pre[0] and writes that configuration to all active
lanes.

Should udphy_dp_phy_set_voltages() compute the volt_cfg index per lane using
each lane's own dp->voltage[i] and dp->pre[i] values so per-lane link training
adjustments are applied?

[ ... ]

> +static int udphy_dp_phy_configure(struct phy *phy,
> +				  union phy_configure_opts *opts)
> +{
> +	struct cix_udphy *udphy = phy_get_drvdata(phy);
> +	int ret;
> +
> +	mutex_lock(&udphy->mutex);
> +	ret = udphy_dp_phy_verify_config(udphy, &opts->dp);
> +	if (ret)
> +		goto unlock;
> +
> +	if (opts->dp.set_rate) {
> +		ret = udphy_dp_phy_set_rate(udphy, &opts->dp);
> +		if (ret) {
> +			dev_err(udphy->dev, "failed to set DP link rate\n");
> +			goto unlock;
> +		}
> +	}

[Severity: High]
What happens if udphy_dp_phy_configure() is called when UDPHY_MODE_DP is not
active in udphy->cur_mode, or with more lanes than allocated to DP?

In udphy_dp_phy_power_on(), if !(udphy->next_mode & UDPHY_MODE_DP) (for
example when Type-C is in TYPEC_STATE_SAFE or USB-only mode, or when
cix,default-conf is 0 or 1), udphy_dp_phy_power_on() returns 0 without
enabling udphy->apb_clk or deasserting udphy->preset. A subsequent call to
udphy_dp_phy_configure() will then perform MMIO reads and writes through
udphy->phy_regmap while the APB clock is disabled and APB reset is asserted.

In addition, udphy_dp_phy_verify_config() accepts dp->lanes == 4 without
checking against udphy_dplane_get(udphy), so a 4-lane DP configuration in
2-lane UDPHY_MODE_DP_USB mode will overwrite the active USB lane registers in
udphy_dp_phy_set_rate() and udphy_dp_phy_set_voltages().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261102082112.2306326-1-gary.yang@cixtech.com?part=3

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-11-02  8:53 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02  8:15 [PATCH 0/3] phy: cix: add Sky1 USB/DP combo PHY Gary Yang
2026-11-02  8:15 ` [PATCH 1/3] dt-bindings: phy: add CIX " Gary Yang
2026-11-02  8:27   ` sashiko-bot
2026-11-02  8:15 ` [PATCH 2/3] phy: cix: add Sky1 USB/DP combo PHY driver Gary Yang
2026-11-02  8:31   ` sashiko-bot
2026-11-02  8:15 ` [PATCH 3/3] arm64: dts: cix: add Sky1 USB/DP combo PHY Gary Yang
2026-11-02  8:41   ` sashiko-bot
  -- strict thread matches above, loose matches on Subject: below --
2026-11-02  8:21 [PATCH 0/3] phy: " Gary Yang
2026-11-02  8:21 ` [PATCH 3/3] arm64: dts: " Gary Yang
2026-11-02  8:52   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox