* [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support
@ 2026-08-31 8:23 Hongliang Yang
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
` (9 more replies)
0 siblings, 10 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-08-31 8:23 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add USB3 PHY driver, Cadence USBSSP glue driver, and USB4/USB5
device tree nodes for the CIX Sky1 SoC.
This series adds support for the USB4 and USB5 controllers (based on
Cadence USBSSP DRD) found on the CIX Sky1 SoC, along with their
associated multi-port USB3 PHY.
Hongliang Yang (4):
dt-bindings: Add CIX Sky1 USB bindings
phy: cix: Add CIX Sky1 USB3 PHY driver
usb: cdns3: Add CIX Sky1 glue driver
arm64: dts: cix: Add USB4/USB5 nodes for Sky1
.../bindings/phy/cix,sky1-usb3-phy.yaml | 109 +++
.../bindings/usb/cix,sky1-usbssp.yaml | 153 ++++
MAINTAINERS | 3 +
arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 36 +
arch/arm64/boot/dts/cix/sky1.dtsi | 133 ++++
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/cix/Kconfig | 20 +
drivers/phy/cix/Makefile | 6 +
drivers/phy/cix/phy-cix-usb3.c | 410 +++++++++++
drivers/phy/cix/phy-cix-usbdp.h | 310 ++++++++
drivers/usb/cdns3/Kconfig | 13 +
drivers/usb/cdns3/Makefile | 1 +
drivers/usb/cdns3/cdnsp-sky1.c | 682 ++++++++++++++++++
drivers/usb/cdns3/cdnsp-sky1.h | 127 ++++
15 files changed, 2005 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
create mode 100644 Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
create mode 100644 drivers/phy/cix/Kconfig
create mode 100644 drivers/phy/cix/Makefile
create mode 100644 drivers/phy/cix/phy-cix-usb3.c
create mode 100644 drivers/phy/cix/phy-cix-usbdp.h
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.c
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.h
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
--
2.54.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
@ 2026-08-31 8:23 ` Hongliang Yang
2026-08-31 8:32 ` sashiko-bot
` (2 more replies)
2026-08-31 8:23 ` [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
` (8 subsequent siblings)
9 siblings, 3 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-08-31 8:23 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add device tree bindings for CIX Sky1 USB3 PHY and Cadence USBSSP
controller glue layer.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
.../bindings/phy/cix,sky1-usb3-phy.yaml | 109 +++++++++++++
.../bindings/usb/cix,sky1-usbssp.yaml | 153 ++++++++++++++++++
MAINTAINERS | 3 +
3 files changed, 265 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
create mode 100644 Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
new file mode 100644
index 000000000000..76d020df72dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/cix,sky1-usb3-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CIX Sky1 USB3 PHY
+
+maintainers:
+ - Hongliang Yang <hongliang.yang@cixtech.com>
+
+description:
+ The CIX Sky1 USB3 PHY is a multi-port SuperSpeedPlus PHY that
+ supports up to two USB3 lanes. Each port is represented as a
+ child node of the PHY controller.
+
+properties:
+ compatible:
+ const: cix,sky1-usb3-phy
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: APB register interface clock
+ - description: PHY reference clock
+
+ clock-names:
+ items:
+ - const: apb_clk
+ - const: ref_clk
+
+ resets:
+ items:
+ - description: PHY reset
+ - description: PHY APB preset
+
+ reset-names:
+ items:
+ - const: reset
+ - const: preset
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^usb-port@[0-9a-f]+$":
+ type: object
+ description:
+ A single USB3 PHY port/lane.
+
+ properties:
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 0
+
+ required:
+ - reg
+ - "#phy-cells"
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - "#address-cells"
+ - "#size-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/cix,sky1.h>
+ #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+
+ usb-phy@9210000 {
+ compatible = "cix,sky1-usb3-phy";
+ reg = <0x09210000 0x40000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
+ <&s5_syscon SKY1_USBPHY_SS_PST_N>;
+ reset-names = "reset", "preset";
+
+ clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
+ clock-names = "apb_clk", "ref_clk";
+
+ usb-port@0 {
+ reg = <0>;
+ #phy-cells = <0>;
+ };
+
+ usb-port@1 {
+ reg = <1>;
+ #phy-cells = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
new file mode 100644
index 000000000000..47202ff5b9e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
@@ -0,0 +1,153 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/cix,sky1-usbssp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CIX Sky1 Cadence USBSSP Controller
+
+maintainers:
+ - Hongliang Yang <hongliang.yang@cixtech.com>
+
+description:
+ The CIX Sky1 USBSSP controller is based on the Cadence USBSSP DRD
+ controller. The integration adds glue registers and mode strap controls
+ in the Sky1 S5 system controller. The CIX glue node wraps a Cadence
+ USBSSP core as a child node.
+
+properties:
+ compatible:
+ const: cix,sky1-usbssp
+
+ reg:
+ items:
+ - description: AXI property configuration register
+ - description: Controller status register
+
+ reg-names:
+ items:
+ - const: axi_property
+ - const: controller_status
+
+ clocks:
+ items:
+ - description: Start-of-frame clock
+ - description: AXI bus clock
+ - description: Low-power mode clock
+ - description: APB register interface clock
+
+ clock-names:
+ items:
+ - const: sof_clk
+ - const: usb_aclk
+ - const: lpm_clk
+ - const: usb_pclk
+
+ resets:
+ items:
+ - description: APB register reset
+ - description: Controller reset
+
+ reset-names:
+ items:
+ - const: usb_preset
+ - const: usb_reset
+
+ cix,usb_syscon:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the Sky1 S5 system controller used to program USB mode
+ strap controls.
+
+ axi_bmax_value:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 0x7
+ description:
+ AXI bus maximum burst length configuration value.
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 2
+
+ ranges: true
+
+patternProperties:
+ "^usb-controller@[0-9a-f]+$":
+ $ref: cdns,usb3.yaml#
+ description:
+ The Cadence USBSSP core controller node. See cdns,usb3.yaml
+ for properties such as dr_mode, maximum-speed, phys, and interrupts.
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - cix,usb_syscon
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/cix,sky1.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ usb@91c0300 {
+ compatible = "cix,sky1-usbssp";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ reg = <0x00 0x091c0314 0x00 0x4>,
+ <0x00 0x091c0400 0x00 0x4>;
+ reg-names = "axi_property", "controller_status";
+
+ resets = <&src SKY1_USBC_SS2_PRST_N>,
+ <&src SKY1_USBC_SS2_RST_N>;
+ reset-names = "usb_preset", "usb_reset";
+
+ clocks = <&scmi_clk CLK_TREE_USB3A_H0_CLK_SOF>,
+ <&scmi_clk CLK_TREE_USB3A_0_AXI_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_H0_CLK_LPM>,
+ <&scmi_clk CLK_TREE_USB3A_0_APB_GATE>;
+ clock-names = "sof_clk", "usb_aclk", "lpm_clk", "usb_pclk";
+
+ cix,usb_syscon = <&src>;
+ axi_bmax_value = <0x7>;
+
+ usb-controller@91d0000 {
+ compatible = "cdns,usb3";
+ reg = <0x00 0x91d0000 0x00 0x4000>,
+ <0x00 0x91d8000 0x00 0x8000>,
+ <0x00 0x91d4000 0x00 0x4000>;
+ reg-names = "otg", "xhci", "dev";
+
+ interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host", "peripheral", "otg", "wakeup";
+
+ /* Cadence USBSSP core properties - see cdns,usb3.yaml */
+ maximum-speed = "super-speed-plus";
+ dr_mode = "otg";
+
+ phys = <&usb3_phy4_0>, <&usb2_phy6>;
+ phy-names = "cdns3,usb3-phy", "cdns3,usb2-phy";
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..4c5f6cbe9124 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2775,8 +2775,11 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git
F: Documentation/devicetree/bindings/arm/cix.yaml
F: Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
+F: Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
+F: Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
F: arch/arm64/boot/dts/cix/
F: drivers/mailbox/cix-mailbox.c
+F: drivers/phy/cix/
K: \bcix\b
ARM/CLKDEV SUPPORT
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
@ 2026-08-31 8:23 ` Hongliang Yang
2026-08-31 8:33 ` sashiko-bot
2026-08-31 8:23 ` [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
` (7 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Hongliang Yang @ 2026-08-31 8:23 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang, Gary Yang
Add PHY driver for the USB3 PHY found on CIX Sky1 SoCs.
This driver handles PHY initialization, reset, and clock
management for the multi-port USB3 PHY.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
Reviewed-by: Gary Yang <gary.yang@cixtech.com>
---
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/cix/Kconfig | 20 ++
drivers/phy/cix/Makefile | 6 +
drivers/phy/cix/phy-cix-usb3.c | 410 ++++++++++++++++++++++++++++++++
drivers/phy/cix/phy-cix-usbdp.h | 310 ++++++++++++++++++++++++
6 files changed, 748 insertions(+)
create mode 100644 drivers/phy/cix/Kconfig
create mode 100644 drivers/phy/cix/Makefile
create mode 100644 drivers/phy/cix/phy-cix-usb3.c
create mode 100644 drivers/phy/cix/phy-cix-usbdp.h
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19f3b7d12b7d..cb0d4be47628 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -175,6 +175,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 d7aa516bcc49..6fd0f3910018 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -27,6 +27,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..aaa2eb9d4552
--- /dev/null
+++ b/drivers/phy/cix/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Phy drivers for Cix PHYs
+#
+
+if ARCH_CIX || COMPILE_TEST
+
+config PHY_CIX_USB3
+ tristate "Cix USB3 PHY Driver"
+ depends on OF && HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Enable this to support the CIX USB3 PHY for TYPEA Connection.
+
+ This driver provides initialization and power management for
+ the USB3 PHY found on CIX Sky1 SoCs.
+
+ If unsure, say N.
+
+endif # ARCH_CIX || COMPILE_TEST
diff --git a/drivers/phy/cix/Makefile b/drivers/phy/cix/Makefile
new file mode 100644
index 000000000000..b46b1059b2ab
--- /dev/null
+++ b/drivers/phy/cix/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Cix phy drivers.
+#
+
+obj-$(CONFIG_PHY_CIX_USB3) += phy-cix-usb3.o
diff --git a/drivers/phy/cix/phy-cix-usb3.c b/drivers/phy/cix/phy-cix-usb3.c
new file mode 100644
index 000000000000..3b6e2669b09d
--- /dev/null
+++ b/drivers/phy/cix/phy-cix-usb3.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy driver for cdn_sd0803_t7g_typea
+ *
+ * Author: Hongliang Yang <hongliang.yang@cixtech.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include "phy-cix-usbdp.h"
+
+struct cix_u3phy;
+
+struct cix_u3phy_cfg {
+ int (*u3phy_init)(struct cix_u3phy *u3phy);
+ int (*u3phy_exit)(struct cix_u3phy *u3phy);
+};
+
+static const struct reg_sequence sky1_u3phy_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_ITER_TMR_LANE0, 0x0078},
+ {RX_SDCAL0_ITER_TMR_LANE1, 0x0078},
+ {RX_SDCAL1_INIT_TMR_LANE0, 0x0018},
+ {RX_SDCAL1_INIT_TMR_LANE1, 0x0018},
+ {RX_SDCAL1_ITER_TMR_LANE0, 0x0078},
+ {RX_SDCAL1_ITER_TMR_LANE1, 0x0078},
+ {TX_RCVDET_ST_TMR_LANE0, 0x0960},
+ {TX_RCVDET_ST_TMR_LANE1, 0x0960},
+
+ {PHY_PLL_CFG_0803, 0x0000},
+ {CMN_PDIAG_PLL0_CLK_SEL_M0, 0x8600},
+ {XCVR_DIAG_HSCLK_SEL_LANE0, 0x0000},
+ {XCVR_DIAG_HSCLK_SEL_LANE1, 0x0000},
+ {XCVR_DIAG_HSCLK_DIV_LANE0, 0x0001},
+ {XCVR_DIAG_HSCLK_DIV_LANE1, 0x0001},
+ {XCVR_DIAG_PLLDRC_CTRL_LANE0, 0x0041},
+ {XCVR_DIAG_PLLDRC_CTRL_LANE1, 0x0041},
+ {CMN_PLL0_DSM_DIAG_M0, 0x0004},
+ {CMN_PLL1_DSM_DIAG_M0, 0x0004},
+ {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, 0x01a0},
+ {CMN_PLL0_FRACDIVL_M0, 0xaaab},
+ {CMN_PLL1_FRACDIVL_M0, 0xaaab},
+ {CMN_PLL0_FRACDIVH_M0, 0x0002},
+ {CMN_PLL1_FRACDIVH_M0, 0x0002},
+ {CMN_PLL0_HIGH_THR_M0, 0x0116},
+ {CMN_PLL1_HIGH_THR_M0, 0x0116},
+ {CMN_PDIAG_PLL0_CTRL_M0, 0x1002},
+ {CMN_PDIAG_PLL1_CTRL_M0, 0x1002},
+ {CMN_PLL0_SS_CTRL1_M0, 0x0001}, /* SSC enabled */
+ {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, 0x02f8},
+ {CMN_PLL1_VCOCAL_PLLCNT_START, 0x02f8},
+ {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, 0x0003},
+ {CMN_PLL1_LOCK_PLLCNT_THR, 0x0003},
+ {PHY_PIPE_USB3_GEN2_PRE_CFG0_0803, 0x0a0a},
+ {PHY_PIPE_USB3_GEN2_POST_CFG0_0803, 0x1000},
+ {PHY_PIPE_USB3_GEN2_POST_CFG1_0803, 0x0010},
+ {CMN_CDIAG_CDB_PWRI_OVRD, 0x8200},
+ {CMN_CDIAG_XCVRC_PWRI_OVRD, 0x8200},
+ {TX_PSC_A0_LANE0, 0x02ff},
+ {TX_PSC_A0_LANE1, 0x02ff},
+ {TX_PSC_A1_LANE0, 0x06af},
+ {TX_PSC_A1_LANE1, 0x06af},
+ {TX_PSC_A2_LANE0, 0x06ae},
+ {TX_PSC_A2_LANE1, 0x06ae},
+ {TX_PSC_A3_LANE0, 0x06ae},
+ {TX_PSC_A3_LANE1, 0x06ae},
+ {RX_PSC_A0_LANE0, 0x0d1d},
+ {RX_PSC_A0_LANE1, 0x0d1d},
+ {RX_PSC_A1_LANE0, 0x0d1d},
+ {RX_PSC_A1_LANE1, 0x0d1d},
+ {RX_PSC_A2_LANE0, 0x0d00},
+ {RX_PSC_A2_LANE1, 0x0d00},
+ {RX_PSC_A3_LANE0, 0x0500},
+ {RX_PSC_A3_LANE1, 0x0500},
+ {TX_TXCC_CTRL_LANE0, 0x2a82},
+ {TX_TXCC_CTRL_LANE1, 0x2a82},
+ {TX_TXCC_CPOST_MULT_01_LANE0, 0x0014},
+ {TX_TXCC_CPOST_MULT_01_LANE1, 0x0014},
+ {TX_TXCC_MGNFS_MULT_000_LANE0, 0x0002},
+ {TX_TXCC_MGNFS_MULT_000_LANE1, 0x0002},
+ {RX_SIGDET_HL_FILT_TMR_LANE0, 0x0013},
+ {RX_SIGDET_HL_FILT_TMR_LANE1, 0x0013},
+ {RX_REE_GCSM1_CTRL_LANE0, 0x0000},
+ {RX_REE_GCSM1_CTRL_LANE1, 0x0000},
+ {RX_REE_ATTEN_THR_LANE0, 0x0c02},
+ {RX_REE_ATTEN_THR_LANE1, 0x0c02},
+ {RX_REE_SMGM_CTRL1_LANE0, 0x0330},
+ {RX_REE_SMGM_CTRL1_LANE1, 0x0330},
+ {RX_REE_SMGM_CTRL2_LANE0, 0x0300},
+ {RX_REE_SMGM_CTRL2_LANE1, 0x0300},
+ {XCVR_DIAG_PSC_OVRD_LANE0, 0x0003},
+ {XCVR_DIAG_PSC_OVRD_LANE1, 0x0003},
+ {RX_DIAG_SIGDET_TUNE_LANE0, 0x1004},
+ {RX_DIAG_SIGDET_TUNE_LANE1, 0x1004},
+ {RX_DIAG_NQST_CTRL_LANE0, 0x00f9},
+ {RX_DIAG_NQST_CTRL_LANE1, 0x00f9},
+ {RX_DIAG_DFE_AMP_TUNE_2_LANE0, 0x0c01},
+ {RX_DIAG_DFE_AMP_TUNE_2_LANE1, 0x0c01},
+ {RX_DIAG_DFE_AMP_TUNE_3_LANE0, 0x0002},
+ {RX_DIAG_DFE_AMP_TUNE_3_LANE1, 0x0002},
+ {RX_DIAG_PI_CAP_LANE0, 0x0000},
+ {RX_DIAG_PI_CAP_LANE1, 0x0000},
+ {RX_DIAG_PI_RATE_LANE0, 0x0031},
+ {RX_DIAG_PI_RATE_LANE1, 0x0031},
+ {RX_CDRLF_CNFG_LANE0, 0x018c},
+ {RX_CDRLF_CNFG_LANE1, 0x018c},
+ {RX_CDRLF_CNFG3_LANE0, 0x0003},
+ {RX_CDRLF_CNFG3_LANE1, 0x0003}
+};
+
+struct cix_u3phy {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *phy_regmap;
+ struct reset_control *preset;
+ struct reset_control *reset;
+ struct clk *apb_clk;
+ struct clk *ref_clk;
+ struct mutex mutex; /* mutex to protect access to individual PHYs */
+ bool init;
+ int init_count;
+ const struct cix_u3phy_cfg *cfg;
+};
+
+static int sky1_u3phy_exit(struct cix_u3phy *u3phy)
+{
+ u3phy->init_count--;
+ if (u3phy->init && u3phy->init_count == 0) {
+ reset_control_assert(u3phy->reset);
+ reset_control_assert(u3phy->preset);
+ clk_disable_unprepare(u3phy->apb_clk);
+ clk_disable_unprepare(u3phy->ref_clk);
+ u3phy->init = false;
+ }
+ return 0;
+}
+
+static int sky1_u3phy_init(struct cix_u3phy *u3phy)
+{
+ int ret;
+
+ if (!u3phy->init) {
+ /* usb rcsu reset is default deassert */
+ reset_control_assert(u3phy->reset);
+ reset_control_assert(u3phy->preset);
+ ret = clk_prepare_enable(u3phy->apb_clk);
+ if (ret) {
+ dev_err(u3phy->dev, "Failed to prepare_enable u3phy apb clock\n");
+ goto assert_reset_preset;
+ }
+ ret = clk_prepare_enable(u3phy->ref_clk);
+ if (ret) {
+ dev_err(u3phy->dev, "Failed to prepare_enable u3phy ref clock\n");
+ goto disable_apb_clk;
+ }
+ reset_control_deassert(u3phy->preset);
+ ret = regmap_multi_reg_write(u3phy->phy_regmap, sky1_u3phy_conf,
+ ARRAY_SIZE(sky1_u3phy_conf));
+ if (ret) {
+ dev_err(u3phy->dev, "Failed to write the reg sequence\n");
+ goto disable_ref_clk;
+ }
+ u3phy->init = true;
+ reset_control_deassert(u3phy->reset);
+ }
+ u3phy->init_count++;
+ return 0;
+
+disable_ref_clk:
+ clk_disable_unprepare(u3phy->ref_clk);
+disable_apb_clk:
+ clk_disable_unprepare(u3phy->apb_clk);
+assert_reset_preset:
+ reset_control_deassert(u3phy->preset);
+ reset_control_deassert(u3phy->reset);
+ u3phy->init = false;
+ return ret;
+}
+
+static const struct cix_u3phy_cfg sky1_u3phy_cfg = {
+ .u3phy_init = sky1_u3phy_init,
+ .u3phy_exit = sky1_u3phy_exit
+};
+
+static int u3phy_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct cix_u3phy *u3phy = context;
+ u32 offset = reg << 2;
+
+ writel(val, u3phy->base + offset);
+ return 0;
+}
+
+static int u3phy_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct cix_u3phy *u3phy = context;
+ u32 offset = reg << 2;
+
+ *val = readl(u3phy->base + offset);
+ return 0;
+}
+
+static const struct regmap_config cix_u3phy_regmap_cfg = {
+ .reg_bits = 32,
+ .reg_stride = 1,
+ .val_bits = 16,
+ .fast_io = true,
+ .reg_write = u3phy_regmap_write,
+ .reg_read = u3phy_regmap_read,
+};
+
+static int cix_usb3_phy_power_on(struct cix_u3phy *u3phy)
+{
+ int ret;
+ const struct cix_u3phy_cfg *phy_cfgs = u3phy->cfg;
+
+ if (phy_cfgs->u3phy_init) {
+ ret = phy_cfgs->u3phy_init(u3phy);
+ if (ret) {
+ dev_err(u3phy->dev, "failed to init udphy\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int cix_usb3_phy_power_off(struct cix_u3phy *u3phy)
+{
+ int ret;
+ const struct cix_u3phy_cfg *phy_cfgs = u3phy->cfg;
+
+ if (phy_cfgs->u3phy_exit) {
+ ret = phy_cfgs->u3phy_exit(u3phy);
+ if (ret) {
+ dev_err(u3phy->dev, "failed to exit udphy\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int cix_u3phy_init(struct phy *phy)
+{
+ struct cix_u3phy *u3phy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ mutex_lock(&u3phy->mutex);
+ ret = cix_usb3_phy_power_on(u3phy);
+ mutex_unlock(&u3phy->mutex);
+ return ret;
+}
+
+static int cix_u3phy_exit(struct phy *phy)
+{
+ struct cix_u3phy *u3phy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ mutex_lock(&u3phy->mutex);
+ ret = cix_usb3_phy_power_off(u3phy);
+ mutex_unlock(&u3phy->mutex);
+ return ret;
+}
+
+static const struct phy_ops cix_u3phy_ops = {
+ .init = cix_u3phy_init,
+ .exit = cix_u3phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int cix_u3phy_probe(struct platform_device *pdev)
+{
+ struct cix_u3phy *u3phy;
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct device_node *np = dev->of_node;
+ struct device_node *child_np;
+ struct fwnode_handle *child_fn;
+ struct phy *phy;
+ int ret = 0;
+
+ u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
+ if (!u3phy)
+ return -ENOMEM;
+ dev_set_drvdata(dev, u3phy);
+ u3phy->dev = dev;
+ u3phy->cfg = device_get_match_data(dev);
+ if (!u3phy->cfg) {
+ dev_err(dev, "no OF data can be matched with %p node\n", np);
+ return -EINVAL;
+ }
+ u3phy->reset = devm_reset_control_get(dev, "reset");
+ if (IS_ERR(u3phy->reset)) {
+ dev_err(dev, "failed to get reset\n");
+ return PTR_ERR(u3phy->reset);
+ }
+ u3phy->preset = devm_reset_control_get(dev, "preset");
+ if (IS_ERR(u3phy->preset)) {
+ dev_err(dev, "failed to get preset\n");
+ return PTR_ERR(u3phy->preset);
+ }
+ u3phy->apb_clk = devm_clk_get(dev, "apb_clk");
+ if (IS_ERR(u3phy->apb_clk)) {
+ dev_err(dev, "phy apb clock not found\n");
+ return PTR_ERR(u3phy->apb_clk);
+ }
+ u3phy->ref_clk = devm_clk_get(dev, "ref_clk");
+ if (IS_ERR(u3phy->ref_clk)) {
+ dev_err(dev, "phy ref clock not found\n");
+ return PTR_ERR(u3phy->ref_clk);
+ }
+ u3phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(u3phy->base))
+ return PTR_ERR(u3phy->base);
+ u3phy->phy_regmap = devm_regmap_init(dev, NULL, u3phy, &cix_u3phy_regmap_cfg);
+ if (IS_ERR(u3phy->phy_regmap)) {
+ dev_err(dev, "failed to remap phy register\n");
+ return PTR_ERR(u3phy->phy_regmap);
+ }
+ mutex_init(&u3phy->mutex);
+ device_for_each_child_node(dev, child_fn) {
+ child_np = to_of_node(child_fn);
+ if (!strncmp(fwnode_get_name(child_fn), "usb-port", 8) ||
+ !strncmp(fwnode_get_name(child_fn), "USB", 3))
+ phy = devm_phy_create(dev, child_np, &cix_u3phy_ops);
+ else
+ continue;
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create phy: %s\n",
+ fwnode_get_name(child_fn));
+ goto put_child;
+ }
+ phy_set_drvdata(phy, u3phy);
+ phy_create_lookup(phy, fwnode_get_name(child_fn), dev_name(dev));
+ }
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(dev, "failed to register phy provider\n");
+ goto put_child;
+ }
+ reset_control_assert(u3phy->reset);
+ reset_control_assert(u3phy->preset);
+ return 0;
+
+put_child:
+ of_node_put(child_np);
+ return ret;
+}
+
+static const struct of_device_id cix_u3phy_dt_match[] = {
+ {
+ .compatible = "cix,sky1-usb3-phy",
+ .data = &sky1_u3phy_cfg
+ },
+ { /* sentinel */ }
+};
+
+static struct platform_driver cix_usb3_phy_driver = {
+ .probe = cix_u3phy_probe,
+ .driver = {
+ .name = "cix-usb3-phy",
+ .of_match_table = cix_u3phy_dt_match,
+ },
+};
+
+module_platform_driver(cix_usb3_phy_driver);
+
+MODULE_AUTHOR("Hongliang Yang <hongliang.yang@cixtech.com>");
+MODULE_DESCRIPTION("Cix USB3 Only 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..6ec7eb5c81aa
--- /dev/null
+++ b/drivers/phy/cix/phy-cix-usbdp.h
@@ -0,0 +1,310 @@
+/* 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_LANE1 0x4301
+#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_LANE0 0x8001
+#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_LANE1 0x424d
+#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_LANE0 0x8090
+#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_LANE0 0x8149
+#define RX_REE_ATTEN_THR_LANE1 0x8349
+#define RX_REE_ATTEN_THR_LANE2 0x8549
+
+#define RX_REE_SMGM_CTRL1_LANE0 0x8177
+#define RX_REE_SMGM_CTRL1_LANE1 0x8377
+#define RX_REE_SMGM_CTRL1_LANE2 0x8577
+
+#define RX_REE_SMGM_CTRL2_LANE0 0x8178
+#define RX_REE_SMGM_CTRL2_LANE1 0x8378
+#define RX_REE_SMGM_CTRL2_LANE2 0x8578
+
+#define XCVR_DIAG_PSC_OVRD_LANE0 0x40eb
+#define XCVR_DIAG_PSC_OVRD_LANE1 0x42eb
+#define XCVR_DIAG_PSC_OVRD_LANE2 0x44eb
+
+#define RX_DIAG_SIGDET_TUNE_LANE0 0x81e8
+#define RX_DIAG_SIGDET_TUNE_LANE1 0x83e8
+#define RX_DIAG_SIGDET_TUNE_LANE2 0x85e8
+
+#define RX_DIAG_NQST_CTRL_LANE0 0x81e5
+#define RX_DIAG_NQST_CTRL_LANE1 0x83e5
+#define RX_DIAG_NQST_CTRL_LANE2 0x85e5
+
+#define RX_DIAG_DFE_AMP_TUNE_2_LANE0 0x81e2
+#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_LANE0 0x81e3
+#define RX_DIAG_DFE_AMP_TUNE_3_LANE1 0x83e3
+#define RX_DIAG_DFE_AMP_TUNE_3_LANE2 0x85e3
+
+#define RX_DIAG_PI_CAP_LANE0 0x81f5
+#define RX_DIAG_PI_CAP_LANE1 0x83f5
+#define RX_DIAG_PI_CAP_LANE2 0x85f5
+
+#define RX_DIAG_PI_RATE_LANE0 0x81f4
+#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_LANE0 0x8080
+#define RX_CDRLF_CNFG_LANE1 0x8280
+#define RX_CDRLF_CNFG_LANE2 0x8480
+
+#define RX_CDRLF_CNFG3_LANE0 0x8082
+#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
+
+/* Bits */
+
+#define PHY_PLL_CFG_0803 0xC00E
+/* 0803 phy register */
+#define PHY_PIPE_USB3_GEN2_PRE_CFG0_0803 0xc01c
+#define PHY_PIPE_USB3_GEN2_POST_CFG0_0803 0xc01e
+#define PHY_PIPE_USB3_GEN2_POST_CFG1_0803 0xc01f
+
+/* gop status address */
+#define GOP_STATUS_ADDRESS 0x83E05000
+#define GOP_STATUS_SIZE 0x04
+
+struct gop_status {
+ unsigned char phy_status[4];
+};
+
+/*
+ * 0: usb
+ * 1: 2 lane usb+ 2 lane dp
+ * 2: usb device
+ * 3: usb2.0 + 4 lane dp
+ */
+enum phy_role {
+ USB_ROLE_NONE,
+ USB_ROLE_HOST,
+ USB_ROLE_DEVICE,
+ USB_ROLE_HOST_20,
+};
+
+#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
2026-08-31 8:23 ` [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
@ 2026-08-31 8:23 ` Hongliang Yang
2026-08-31 8:41 ` sashiko-bot
2026-08-31 8:57 ` Philipp Zabel
2026-08-31 8:23 ` [PATCH v1 4/4] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
` (6 subsequent siblings)
9 siblings, 2 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-08-31 8:23 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang, Gary Yang
Add glue layer for the Cadence USBSSP DRD controller on CIX Sky1
SoCs. The glue driver handles platform-specific initialization
including clocks, resets, and PHY configuration.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
Reviewed-by: Gary Yang <gary.yang@cixtech.com>
---
drivers/usb/cdns3/Kconfig | 13 +
drivers/usb/cdns3/Makefile | 1 +
drivers/usb/cdns3/cdnsp-sky1.c | 682 +++++++++++++++++++++++++++++++++
drivers/usb/cdns3/cdnsp-sky1.h | 127 ++++++
4 files changed, 823 insertions(+)
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.c
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.h
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index 39ad23d1ada8..03d8e170a98a 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -77,6 +77,19 @@ config USB_CDNSP_PCI
If you choose to build this driver as module it will
be dynamically linked and module will be called cdnsp-pci.ko
+config USB_CDNSP_SKY1
+ tristate "Cadence USB3 support on CIX Sky1 SoC platforms"
+ depends on USB_CDNS3
+ depends on ARCH_CIX || COMPILE_TEST
+ default USB_CDNS3
+ help
+ Glue driver for the Cadence USB dual-role controllers on CIX Sky1
+ (device tree compatible cix,sky1-usb3). It enables clocks and resets
+ from the SoC, then uses the shared cdns3 platform core (cdns.ko).
+
+ If built as a module, the module is named cdnsp-sky1.ko and must be
+ loaded after the cdns core module when both are loadable modules.
+
config USB_CDNS3_TI
tristate "Cadence USB3 support on TI platforms"
depends on ARCH_K3 || COMPILE_TEST
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index b2e4ba6a49a3..ab813aaf9940 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-pci.o
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o
+obj-$(CONFIG_USB_CDNSP_SKY1) += cdnsp-sky1.o
diff --git a/drivers/usb/cdns3/cdnsp-sky1.c b/drivers/usb/cdns3/cdnsp-sky1.c
new file mode 100644
index 000000000000..3dd20451569e
--- /dev/null
+++ b/drivers/usb/cdns3/cdnsp-sky1.c
@@ -0,0 +1,682 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cdnsp-sky1.c - CIX Sky1 glue for Cadence USBSSP DRD controller
+ *
+ * Copyright (C) 2026 CIX Technology Group Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "cdnsp-sky1.h"
+#include "core.h"
+
+static const char *cix_usb_clk_names[CIX_USB_CLK_NUM] = {
+ "sof_clk",
+ "usb_aclk",
+ "lpm_clk",
+ "usb_pclk",
+};
+
+struct cdnsp_sky1_strap_signal {
+ unsigned int offset, bit;
+};
+
+static const struct cdnsp_sky1_strap_signal strap_signals[SKY1_USB_S5_NUM] = {
+ /* usb config in s5 domain */
+ [U3_TYPEC_DRD_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_DRD_MODE_STRAP_BIT },
+ [U3_TYPEC_HOST0_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_HOST0_MODE_STRAP_BIT },
+ [U3_TYPEC_HOST1_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_HOST1_MODE_STRAP_BIT },
+ [U3_TYPEC_HOST2_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_HOST2_MODE_STRAP_BIT },
+ [U3_TYPEA_CTRL0_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEA_CTRL0_MODE_STRAP_BIT },
+ [U3_TYPEA_CTRL1_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEA_CTRL1_MODE_STRAP_BIT},
+ [U2_HOST0_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST0_MODE_STRAP_BIT },
+ [U2_HOST1_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST1_MODE_STRAP_BIT },
+ [U2_HOST2_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST2_MODE_STRAP_BIT },
+ [U2_HOST3_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST3_MODE_STRAP_BIT },
+};
+
+static int cdnsp_sky1_set_mode_by_id(struct device *dev, int mode)
+{
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ return regmap_update_bits(data->usb_syscon,
+ strap_signals[data->id].offset,
+ GENMASK(strap_signals[data->id].bit + 1,
+ strap_signals[data->id].bit),
+ mode << strap_signals[data->id].bit);
+}
+
+/**
+ * cdnsp_sky1_clk_enable_all() - enable all clocks for usb controller
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static int cdnsp_sky1_clk_enable_all(struct device *dev)
+{
+ int i, ret = 0;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_NUM; i++) {
+ cix_usb_clks[i] = devm_clk_get(dev, cix_usb_clk_names[i]);
+ if (IS_ERR(cix_usb_clks[i])) {
+ ret = dev_err_probe(dev, PTR_ERR(cix_usb_clks[i]),
+ "could not get %s clock\n",
+ cix_usb_clk_names[i]);
+ goto err_usb_clks;
+ }
+ ret = clk_prepare_enable(cix_usb_clks[i]);
+ if (ret) {
+ dev_err(dev, "%s enable failed:%d\n", cix_usb_clk_names[i], ret);
+ goto err_usb_clks;
+ }
+ }
+ dev_dbg(dev, "enable sky1 USB clock done\n");
+ return ret;
+
+err_usb_clks:
+ cix_usb_clks[i] = NULL;
+ while (--i >= 0) {
+ clk_disable_unprepare(cix_usb_clks[i]);
+ cix_usb_clks[i] = NULL;
+ }
+ return ret;
+};
+
+/**
+ * cdnsp_sky1_clk_disable_all() - disable all clocks for usb controller
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static void cdnsp_sky1_clk_disable_all(struct device *dev)
+{
+ int i;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_NUM; i++)
+ clk_disable_unprepare(cix_usb_clks[i]);
+};
+
+/**
+ * cdnsp_sky1_clk_enable_resume() - enable the clocks that are turned
+ * off while suspend
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static int cdnsp_sky1_clk_enable_resume(struct device *dev)
+{
+ int i, ret;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_OFF_NUM; i++) {
+ ret = clk_prepare_enable(cix_usb_clks[i]);
+ if (ret) {
+ dev_err(dev, "failed to enable clock %s: %d\n",
+ cix_usb_clk_names[i], ret);
+ goto err_usb_clks;
+ }
+ }
+ return 0;
+
+err_usb_clks:
+ cix_usb_clks[i] = NULL;
+ while (--i >= 0) {
+ clk_disable_unprepare(cix_usb_clks[i]);
+ cix_usb_clks[i] = NULL;
+ }
+ return ret;
+};
+
+/**
+ * cdnsp_sky1_clk_disable_suspend() - disable the clocks which are not
+ * needed when suspend
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static void cdnsp_sky1_clk_disable_suspend(struct device *dev)
+{
+ int i;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_OFF_NUM; i++)
+ clk_disable_unprepare(cix_usb_clks[i]);
+};
+
+static int sky1_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us)
+{
+ u32 result;
+ int ret;
+
+ ret = readl_poll_timeout(ptr, result,
+ (result & mask) == done || result == U32_MAX,
+ 10, timeout_us);
+ if (result == U32_MAX) /* card removed */
+ return -ENODEV;
+ return ret;
+}
+
+static int cdns_sky1_platform_suspend(struct device *dev,
+ bool suspend, bool wakeup)
+{
+ struct cdns *cdns = dev_get_drvdata(dev);
+ struct platform_device *xhci_dev = cdns->host_dev;
+ struct usb_hcd *hcd;
+ struct device *parent = cdns->dev->parent;
+ struct cdnsp_sky1 *data = dev_get_drvdata(parent);
+ u32 value;
+ int ret = 0;
+ int count = 3;
+
+ data->wakeup = wakeup;
+
+ if (cdns->role != USB_ROLE_HOST)
+ return 0;
+
+ hcd = dev_get_drvdata(&xhci_dev->dev);
+ if (!hcd) {
+ dev_dbg(dev, "host controller have not registered\n");
+ return 0;
+ }
+
+ if (suspend) {
+ while (count--) {
+ value = readl(hcd->regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D3 | PS_PME_En;
+ writel(value, hcd->regs + XECP_PM_PMCSR);
+ /* After controller enters D3, disable AXI and SOF
+ * until AXI valid flag changes to 0.
+ */
+ if (sky1_handshake(data->ctst_base, AXI_CLOCK_VALID, 0, 100ULL * 1000))
+ dev_dbg(dev, "enter D3 failed,register value:%x\n",
+ readl(data->ctst_base));
+ else
+ break;
+ }
+ if (count < 0) {
+ dev_err(dev, "enter D3 failed after retries, register value:%x\n",
+ readl(data->ctst_base));
+ }
+ } else {
+ while (count--) {
+ value = readl(hcd->regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D0;
+ value &= ~PS_PME_En;
+ writel(value, hcd->regs + XECP_PM_PMCSR);
+ /* Wait power state back to D0 */
+ if (sky1_handshake(hcd->regs + XECP_PM_PMCSR, PS_MASK, 0, 100ULL * 1000)) {
+ dev_dbg(dev, "exit D3 timeout, power state=0x%lx\n",
+ readl(hcd->regs + XECP_PM_PMCSR) & PS_MASK);
+ } else {
+ break;
+ }
+ }
+ if (count < 0) {
+ dev_err(dev, "exit D3 timeout after retries, power state=0x%lx\n",
+ readl(hcd->regs + XECP_PM_PMCSR) & PS_MASK);
+ }
+ }
+
+ return ret;
+}
+
+static void cdnsp_sky1_configure_controller(struct cdnsp_sky1 *data)
+{
+ int clk;
+ int v0, v1, v2;
+ u32 val = 0;
+
+ if (data->u3_disable) {
+ dev_dbg(data->dev, "disable u3 port\n");
+ writel(D_XEC_CFG_3XPORT_MODE_VALUE, data->device_base
+ + D_XEC_CFG_3XPORT_MODE);
+ }
+ writel(AXI_HALT, data->device_base + D_XEC_AXI_CAP);
+ writel(AXI_HALT, data->xhci_base + D_XEC_AXI_CAP);
+ writel(data->axi_bmax_value, data->device_base + D_XEC_AXI_CTRL0);
+ writel(data->axi_bmax_value, data->xhci_base + D_XEC_AXI_CTRL0);
+ writel((~(u32)(AXI_HALT)), data->device_base + D_XEC_AXI_CAP);
+ writel((~(u32)(AXI_HALT)), data->xhci_base + D_XEC_AXI_CAP);
+ clk = data->sof_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 1),
+ data->device_base + D_XEC_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 1),
+ data->device_base + D_XEC_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 1),
+ data->device_base + D_XEC_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_100MS);
+ dev_dbg(data->dev, "readl:%x, %x ,%x, %x, %x, %x, %x, %x\n",
+ readl(data->device_base + D_XEC_PRE_REG_250NS),
+ readl(data->device_base + D_XEC_PRE_REG_1US),
+ readl(data->device_base + D_XEC_PRE_REG_10US),
+ readl(data->device_base + D_XEC_PRE_REG_100US),
+ readl(data->device_base + D_XEC_PRE_REG_125US),
+ readl(data->device_base + D_XEC_PRE_REG_1MS),
+ readl(data->device_base + D_XEC_PRE_REG_10MS),
+ readl(data->device_base + D_XEC_PRE_REG_100MS));
+ clk = data->lpm_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 1),
+ data->device_base + D_XEC_LPM_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_100MS);
+ v0 = readl(data->xhci_base + XEC_USBSSP_CHICKEN_BITS_3);
+ v0 &= ~(CFG_APB_TIMEOUT_PSLVERR_EN | CFG_APB_PSLVERR_EN);
+ writel(v0, data->xhci_base + XEC_USBSSP_CHICKEN_BITS_3);
+ if (data->u3_disable) {
+ dev_dbg(data->dev, "disable u3 port\n");
+ writel(XEC_CFG_3XPORT_MODE_VALUE, data->xhci_base
+ + XEC_CFG_3XPORT_MODE);
+ } else if (data->ssp_disable) {
+ dev_dbg(data->dev, "disable ssp\n");
+ v0 = readl(data->xhci_base + XEC_CFG_3XPORT_MODE);
+ writel(v0 & CFG_3XPORT_MODE_DIS_SSP, data->xhci_base
+ + XEC_CFG_3XPORT_MODE);
+ }
+ clk = data->sof_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 0),
+ data->xhci_base + XEC_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_100MS);
+ clk = data->lpm_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 0),
+ data->xhci_base + XEC_LPM_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_100MS);
+ val = readl(data->xhci_base + XEC_USBSSP_CLK_GATING_CTRL);
+ val |= HOST20_ACLK_GATING_DISABLE | HOST20_UTMI_GATING_DISABLE;
+ writel(val, data->xhci_base + XEC_USBSSP_CLK_GATING_CTRL);
+}
+
+static int cdnsp_sky1_drd_init(struct cdnsp_sky1 *data)
+{
+ int ret = 0;
+
+ reset_control_assert(data->reset);
+ reset_control_assert(data->preset);
+ cdnsp_sky1_clk_disable_all(data->dev);
+ ret = cdnsp_sky1_clk_enable_all(data->dev);
+ if (ret)
+ return ret;
+ writel(CIX_USB_AXI_WR_CACHE_VALUE, data->axi_base);
+ cdnsp_sky1_set_mode_by_id(data->dev, MODE_STRAP_OTG);
+ reset_control_deassert(data->preset);
+ cdnsp_sky1_configure_controller(data);
+ reset_control_deassert(data->reset);
+ return ret;
+}
+
+static void *sky1_of_get_addr_by_name(struct device_node *parent, char *name)
+{
+ struct device_node *node;
+ int index;
+
+ node = of_get_next_child(parent, NULL);
+ if (node) {
+ index = of_property_match_string(node, "reg-names", name);
+ if (index >= 0)
+ return of_iomap(node, index);
+ }
+ return NULL;
+}
+
+static void *sky1_get_addr_by_name(struct device *dev, char *name)
+{
+ return sky1_of_get_addr_by_name(dev->of_node, name);
+}
+
+static void sky1_put_addr(void __iomem *regs)
+{
+ if (regs)
+ iounmap(regs);
+}
+
+static struct of_dev_auxdata cdns_sky1_auxdata[] = {
+ {
+ .compatible = "cdns,usb3",
+ },
+ {},
+};
+
+static int cdnsp_sky1_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct cdnsp_sky1 *data;
+ int ret = 0;
+ struct cdns3_platform_data *cdns_sky1_pdata;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->axi_base = devm_platform_ioremap_resource_byname(pdev, "axi_property");
+ if (IS_ERR(data->axi_base)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->axi_base);
+ }
+ data->ctst_base = devm_platform_ioremap_resource_byname(pdev, "controller_status");
+ if (IS_ERR(data->ctst_base)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->ctst_base);
+ }
+ data->reset = devm_reset_control_get(&pdev->dev, "usb_reset");
+ if (IS_ERR(data->reset)) {
+ ret = PTR_ERR(data->reset);
+ dev_err(dev, "get reset error:%d\n", ret);
+ return ret;
+ }
+ data->preset = devm_reset_control_get(&pdev->dev, "usb_preset");
+ if (IS_ERR(data->preset)) {
+ ret = PTR_ERR(data->preset);
+ dev_err(dev, "get reset error:%d\n", ret);
+ return ret;
+ }
+ platform_set_drvdata(pdev, data);
+ data->dev = dev;
+ ret = of_alias_get_id(dev->of_node, "usb");
+ if (ret == -ENODEV) {
+ if (device_property_read_u32(dev, "id", &ret))
+ ret = -ENODEV;
+ }
+ if (ret < 0 || ret >= SKY1_USB_S5_NUM) {
+ dev_err(dev, "get alias failed.\n");
+ return ret;
+ }
+ data->id = ret;
+ data->usb_syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "cix,usb_syscon");
+ if (IS_ERR(data->usb_syscon)) {
+ dev_err(dev, "Unable to get cix,usb_syscon regmap");
+ return PTR_ERR(data->usb_syscon);
+ }
+ data->u3_disable = device_property_read_bool(dev, "u3-port-disable");
+ data->ssp_disable = device_property_read_bool(dev, "ssp-disable");
+ if (!device_property_read_u32(dev, "sof_clk_freq", &ret))
+ data->sof_clk_freq = ret;
+ else
+ data->sof_clk_freq = CIX_USB_CLK_32K;
+ if (!device_property_read_u32(dev, "lpm_clk_freq", &ret))
+ data->lpm_clk_freq = ret;
+ else
+ data->lpm_clk_freq = CIX_USB_CLK_8M;
+ if (!device_property_read_u32(dev, "axi_bmax_value", &ret))
+ data->axi_bmax_value = ret;
+ else
+ data->axi_bmax_value = AXI_BMAX_VALUE_DEFAULT;
+ data->xhci_base = sky1_get_addr_by_name(dev, "xhci");
+ if (!data->xhci_base)
+ return -ENODEV;
+ data->device_base = sky1_get_addr_by_name(dev, "dev");
+ if (!data->device_base)
+ return -ENODEV;
+ ret = cdnsp_sky1_drd_init(data);
+ if (ret == -ETIMEDOUT)
+ return -EPROBE_DEFER;
+ if (ret)
+ return ret;
+ data->oc_gpio = devm_gpiod_get_optional(data->dev, "oc", GPIOD_IN);
+ if (IS_ERR(data->oc_gpio)) {
+ dev_err(data->dev, "can not get oc_gpio\n");
+ ret = PTR_ERR(data->oc_gpio);
+ return ret;
+ }
+ if (data->oc_gpio) {
+ ret = gpiod_direction_input(data->oc_gpio);
+ if (ret < 0)
+ dev_err(data->dev, "set oc_gpio input failed:%d\n", ret);
+ }
+ /* release by platform_device_release */
+ cdns_sky1_pdata = kzalloc(sizeof(*cdns_sky1_pdata), GFP_KERNEL);
+ if (!cdns_sky1_pdata)
+ return -ENOMEM;
+ cdns_sky1_pdata->platform_suspend = cdns_sky1_platform_suspend;
+ cdns_sky1_pdata->quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW;
+ cdns_sky1_auxdata->platform_data = cdns_sky1_pdata;
+ ret = of_platform_populate(node, NULL, cdns_sky1_auxdata, dev);
+ if (ret) {
+ dev_err(dev, "failed to create children: %d\n", ret);
+ goto err;
+ }
+ device_set_wakeup_capable(dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ return 0;
+err:
+ kfree(cdns_sky1_pdata);
+ return ret;
+}
+
+static void cdnsp_sky1_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+ of_platform_depopulate(dev);
+ sky1_put_addr(data->xhci_base);
+ sky1_put_addr(data->device_base);
+ reset_control_deassert(data->reset);
+ reset_control_deassert(data->preset);
+ cdnsp_sky1_clk_disable_all(dev);
+ platform_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+/* Because the wake-up interrupt and host interrupt are the same interrupt, closing the axi
+ * and sof clock will result in the inability to generate port status change interrupt.
+ */
+
+static int cdnsp_sky1_system_suspend(struct device *dev)
+{
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ if (!data->wakeup) {
+ reset_control_assert(data->reset);
+ reset_control_assert(data->preset);
+ }
+ cdnsp_sky1_clk_disable_suspend(dev);
+ return 0;
+}
+
+static int cdnsp_sky1_system_resume(struct device *dev)
+{
+ int ret = 0;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ ret = cdnsp_sky1_clk_enable_resume(dev);
+ if (ret)
+ return ret;
+ if (!data->wakeup) {
+ writel(CIX_USB_AXI_WR_CACHE_VALUE, data->axi_base);
+ cdnsp_sky1_set_mode_by_id(data->dev, MODE_STRAP_OTG);
+ reset_control_deassert(data->preset);
+ cdnsp_sky1_configure_controller(data);
+ reset_control_deassert(data->reset);
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops cdnsp_sky1_pm_ops = {
+ .suspend = cdnsp_sky1_system_suspend,
+ .resume = cdnsp_sky1_system_resume,
+};
+#endif /* CONFIG_PM */
+
+static const struct of_device_id cdns_sky1_of_match[] = {
+ { .compatible = "cix,sky1-usbssp", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cdns_sky1_of_match);
+
+static int cdnsp_sky1_find_cdns(struct device *dev, void *data)
+{
+ struct cdns **cdns_ptr = data;
+
+ if (dev->of_node && of_device_is_compatible(dev->of_node, "cdns,usb3")) {
+ *cdns_ptr = dev_get_drvdata(dev);
+ return 1;
+ }
+ return 0;
+}
+
+static int cdnsp_sky1_find_gadget_match(struct device *dev, void *data)
+{
+ struct device **gadget_dev = data;
+ const char *name = dev_name(dev);
+ static const char gadget_prefix[] = "gadget.";
+
+ /*
+ * The gadget device is registered on the gadget bus with name
+ * "gadget.%d" (see usb_add_gadget_udc -> dev_set_name).
+ * It sits on the gadget bus and has the function driver bound to it.
+ * Verify the device is on the gadget bus by checking the bus name.
+ * This prevents matching devices that happen to have "gadget." prefix
+ * in their name but are not real gadget devices, and also avoids
+ * NULL pointer dereference when device bus is being removed.
+ */
+ if (name && dev->bus && !strcmp(dev->bus->name, "gadget") &&
+ !strncmp(name, gadget_prefix, sizeof(gadget_prefix) - 1)) {
+ *gadget_dev = dev;
+ return 1;
+ }
+ return 0;
+}
+
+static void cdnsp_sky1_shutdown(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct cdns *cdns = NULL;
+ struct device *gadget_dev = NULL;
+
+ if (!device_may_wakeup(dev)) {
+ /*
+ * Find the cdns3 child device, then find its gadget device
+ * and release the function driver before disabling clocks.
+ * This ensures all register accesses in gadget_unbind_driver
+ * complete before clocks are turned off.
+ */
+ device_for_each_child(dev, &cdns, cdnsp_sky1_find_cdns);
+ if (cdns)
+ device_for_each_child(cdns->dev, &gadget_dev,
+ cdnsp_sky1_find_gadget_match);
+ if (gadget_dev)
+ device_release_driver(gadget_dev);
+ if (cdns && cdns->host_dev) {
+ struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev);
+
+ if (hcd && hcd->irq > 0) {
+ disable_irq(hcd->irq);
+ synchronize_irq(hcd->irq);
+ /*
+ * Clear HCD_FLAG_HW_ACCESSIBLE before disable_irq.
+ * This prevents usb_hcd_irq from calling xhci_irq
+ * (which reads USBSTS) after clocks are disabled.
+ */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ }
+ }
+ reset_control_assert(data->reset);
+ reset_control_assert(data->preset);
+ cdnsp_sky1_clk_disable_all(dev);
+ }
+}
+
+static struct platform_driver cdnsp_sky1_driver = {
+ .probe = cdnsp_sky1_probe,
+ .remove = cdnsp_sky1_remove,
+ .shutdown = cdnsp_sky1_shutdown,
+ .driver = {
+ .name = "cdnsp-sky1",
+ .of_match_table = cdns_sky1_of_match,
+ .pm = pm_ptr(&cdnsp_sky1_pm_ops),
+ },
+};
+
+module_platform_driver(cdnsp_sky1_driver);
+
+MODULE_ALIAS("platform:cdnsp-sky1");
+MODULE_DESCRIPTION("CIX Sky1 Cadence USBSSP DRD glue driver");
+MODULE_AUTHOR("Hongliang Yang <hongliang.yang@cixtech.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/cdns3/cdnsp-sky1.h b/drivers/usb/cdns3/cdnsp-sky1.h
new file mode 100644
index 000000000000..49c3d5eab174
--- /dev/null
+++ b/drivers/usb/cdns3/cdnsp-sky1.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _CDNSP_SKY1_H
+#define _CDNSP_SKY1_H
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/reset.h>
+
+#define USB_MODE_STRAP_S5_DOMAIN 0x424
+
+#define MODE_STRAP_OTG 0
+
+#define U3_TYPEC_DRD_ID 0
+#define U3_TYPEC_HOST0_ID 1
+#define U3_TYPEC_HOST1_ID 2
+#define U3_TYPEC_HOST2_ID 3
+#define U3_TYPEA_CTRL0_ID 4
+#define U3_TYPEA_CTRL1_ID 5
+#define U2_HOST0_ID 6
+#define U2_HOST1_ID 7
+#define U2_HOST2_ID 8
+#define U2_HOST3_ID 9
+#define SKY1_USB_S5_NUM 10
+
+#define U3_TYPEC_DRD_MODE_STRAP_BIT 12
+#define U3_TYPEC_HOST0_MODE_STRAP_BIT 14
+#define U3_TYPEC_HOST1_MODE_STRAP_BIT 16
+#define U3_TYPEC_HOST2_MODE_STRAP_BIT 18
+#define U3_TYPEA_CTRL0_MODE_STRAP_BIT 8
+#define U3_TYPEA_CTRL1_MODE_STRAP_BIT 10
+#define U2_HOST0_MODE_STRAP_BIT 0
+#define U2_HOST1_MODE_STRAP_BIT 2
+#define U2_HOST2_MODE_STRAP_BIT 4
+#define U2_HOST3_MODE_STRAP_BIT 6
+
+#define AXI_HALT BIT(31)
+#define AXI_BMAX_VALUE_DEFAULT 0x7
+
+#define D_XEC_CFG_3XPORT_MODE 0x2040
+#define D_XEC_AXI_CAP 0x2174
+#define D_XEC_AXI_CTRL0 0x217C
+#define D_XEC_PRE_REG_250NS 0x21E8
+#define D_XEC_PRE_REG_1US 0x21EC
+#define D_XEC_PRE_REG_10US 0x21F0
+#define D_XEC_PRE_REG_100US 0x21F4
+#define D_XEC_PRE_REG_125US 0x21F8
+#define D_XEC_PRE_REG_1MS 0x21FC
+#define D_XEC_PRE_REG_10MS 0x2200
+#define D_XEC_PRE_REG_100MS 0x2204
+#define D_XEC_LPM_PRE_REG_250NS 0x2208
+#define D_XEC_LPM_PRE_REG_1US 0x220C
+#define D_XEC_LPM_PRE_REG_10US 0x2210
+#define D_XEC_LPM_PRE_REG_100US 0x2214
+#define D_XEC_LPM_PRE_REG_125US 0x2218
+#define D_XEC_LPM_PRE_REG_1MS 0x221C
+#define D_XEC_LPM_PRE_REG_10MS 0x2220
+#define D_XEC_LPM_PRE_REG_100MS 0x2224
+
+#define XEC_CFG_3XPORT_MODE 0x2040
+#define XEC_PRE_REG_250NS 0x21E8
+#define XEC_PRE_REG_1US 0x21EC
+#define XEC_PRE_REG_10US 0x21F0
+#define XEC_PRE_REG_100US 0x21F4
+#define XEC_PRE_REG_125US 0x21F8
+#define XEC_PRE_REG_1MS 0x21FC
+#define XEC_PRE_REG_10MS 0x2200
+#define XEC_PRE_REG_100MS 0x2204
+#define XEC_LPM_PRE_REG_250NS 0x2208
+#define XEC_LPM_PRE_REG_1US 0x220C
+#define XEC_LPM_PRE_REG_10US 0x2210
+#define XEC_LPM_PRE_REG_100US 0x2214
+#define XEC_LPM_PRE_REG_125US 0x2218
+#define XEC_LPM_PRE_REG_1MS 0x221C
+#define XEC_LPM_PRE_REG_10MS 0x2220
+#define XEC_LPM_PRE_REG_100MS 0x2224
+#define XEC_USBSSP_CHICKEN_BITS_3 0x2230
+#define XEC_USBSSP_CLK_GATING_CTRL 0x2234
+#define XECP_PM_PMCSR 0x2240
+
+/* XEC_USBSSP_CLK_GATING_CTRL */
+#define HOST20_ACLK_GATING_DISABLE BIT(4)
+#define HOST20_UTMI_GATING_DISABLE BIT(5)
+
+/* XECP_PM_PMCSR */
+#define PS_MASK GENMASK(1, 0)
+#define PS_D0 0
+#define PS_D3 3
+#define PS_PME_En BIT(8)
+
+#define D_XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
+#define XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
+#define CFG_3XPORT_MODE_DIS_SSP (~(1 << 31))
+
+#define CFG_APB_TIMEOUT_PSLVERR_EN BIT(22)
+#define CFG_APB_PSLVERR_EN BIT(23)
+
+#define CIX_USB_CLK_NUM (4)
+#define CIX_USB_CLK_OFF_NUM (2)
+#define CIX_USB_AXI_WR_CACHE_VALUE 0x33
+#define CIX_USB_CLK_32K 32000
+#define CIX_USB_CLK_8M 8000000
+
+#define AXI_CLOCK_VALID BIT(1)
+
+struct cdnsp_sky1 {
+ struct device *dev;
+ void __iomem *axi_base;
+ void __iomem *ctst_base;
+ void __iomem *dr_base;
+ void __iomem *xhci_base;
+ void __iomem *device_base;
+ struct platform_device *cdnsp_pdev;
+ struct reset_control *reset;
+ struct reset_control *preset;
+ struct clk *cix_usb_clks[CIX_USB_CLK_NUM];
+ int id;
+ struct regmap *usb_syscon;
+ int lpm_clk_freq;
+ int sof_clk_freq;
+ bool u3_disable;
+ bool ssp_disable;
+ int axi_bmax_value;
+ struct gpio_desc *oc_gpio;
+ bool wakeup;
+};
+
+#endif /* _CDNSP_SKY1_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v1 4/4] arm64: dts: cix: Add USB4/USB5 nodes for Sky1
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (2 preceding siblings ...)
2026-08-31 8:23 ` [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
@ 2026-08-31 8:23 ` Hongliang Yang
2026-08-31 8:33 ` sashiko-bot
2026-09-02 3:21 ` [PATCH v2 0/5] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (5 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Hongliang Yang @ 2026-08-31 8:23 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add USB4 and USB5 controller nodes (based on Cadence USBSSP DRD)
and their associated USB3 PHY nodes for the CIX Sky1 SoC. Enable
USB4 and USB5 in host mode on the Radxa Orion O6 board.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 36 ++++++
arch/arm64/boot/dts/cix/sky1.dtsi | 133 ++++++++++++++++++++++
2 files changed, 169 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..33d3ffdc3f9e 100644
--- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
+++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
@@ -80,6 +80,22 @@ pins {
};
};
+
+ pinctrl_usb4: usb4-power-on-cfg {
+ pins {
+ pinmux = <CIX_PAD_GPIO041_FUNC_USB_DRIVE_VBUS4>;
+ bias-pull-down;
+ drive-strength = <8>;
+ };
+ };
+
+ pinctrl_usb5: usb5-power-on-cfg {
+ pins {
+ pinmux = <CIX_PAD_GPIO042_FUNC_USB_DRIVE_VBUS5>;
+ bias-pull-down;
+ drive-strength = <8>;
+ };
+ };
};
&pcie_x8_rc {
@@ -117,3 +133,23 @@ &s5_gpio2 {
&uart2 {
status = "okay";
};
+
+&usb4 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usb4>;
+};
+
+&usbss_4 {
+ status = "okay";
+ dr_mode = "host";
+};
+
+&usb5 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_usb5>;
+};
+
+&usbss_5 {
+ status = "okay";
+ dr_mode = "host";
+};
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index a77c52296ebd..44736c071573 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -6,6 +6,8 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/cix,sky1.h>
+#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
#include "sky1-power.h"
/ {
@@ -13,6 +15,11 @@ / {
#address-cells = <2>;
#size-cells = <2>;
+ aliases {
+ usb4 = &usb4;
+ usb5 = &usb5;
+ };
+
cpus {
#address-cells = <2>;
#size-cells = <0>;
@@ -574,6 +581,132 @@ mbox_ap2sfh: mailbox@80a0000 {
cix,mbox-dir = "tx";
};
+ usb4: usb@91c0300 {
+ compatible = "cix,sky1-usbssp";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ reg = <0x00 0x091c0314 0x00 0x4>,
+ <0x00 0x091c0400 0x00 0x4>;
+ reg-names = "axi_property", "controller_status";
+
+ resets = <&s5_syscon SKY1_USBC_SS2_PRST_N>,
+ <&s5_syscon SKY1_USBC_SS2_RST_N>;
+ reset-names = "usb_preset", "usb_reset";
+
+ clocks = <&scmi_clk CLK_TREE_USB3A_H0_CLK_SOF>,
+ <&scmi_clk CLK_TREE_USB3A_0_AXI_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_H0_CLK_LPM>,
+ <&scmi_clk CLK_TREE_USB3A_0_APB_GATE>;
+ clock-names = "sof_clk", "usb_aclk", "lpm_clk", "usb_pclk";
+
+ cix,usb_syscon = <&s5_syscon>;
+ axi_bmax_value = <0x7>;
+ sof_clk_freq = <8000000>;
+ lpm_clk_freq = <32000>;
+ status = "disabled";
+
+ usbss_4: usb-controller@91d0000 {
+ compatible = "cdns,usb3";
+ reg = <0x00 0x91d0000 0x00 0x4000>,
+ <0x00 0x91d4000 0x00 0x4000>,
+ <0x00 0x91d8000 0x00 0x8000>;
+ reg-names = "otg", "dev", "xhci";
+
+ interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "host",
+ "peripheral",
+ "otg",
+ "wakeup";
+
+ maximum-speed = "super-speed-plus";
+ dr_mode = "otg";
+ phys = <&usb3_phy4_0>;
+ phy-names = "cdns3,usb3-phy";
+ status = "disabled";
+ };
+ };
+
+ usb5: usb@91c0304 {
+ compatible = "cix,sky1-usbssp";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ reg = <0x00 0x091c0324 0x00 0x4>,
+ <0x00 0x091c0410 0x00 0x4>;
+ reg-names = "axi_property", "controller_status";
+
+ resets = <&s5_syscon SKY1_USBC_SS3_PRST_N>,
+ <&s5_syscon SKY1_USBC_SS3_RST_N>;
+ reset-names = "usb_preset", "usb_reset";
+
+ clocks = <&scmi_clk CLK_TREE_USB3A_H1_CLK_SOF>,
+ <&scmi_clk CLK_TREE_USB3A_1_AXI_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_H1_CLK_LPM>,
+ <&scmi_clk CLK_TREE_USB3A_1_APB_GATE>;
+ clock-names = "sof_clk", "usb_aclk", "lpm_clk", "usb_pclk";
+
+ cix,usb_syscon = <&s5_syscon>;
+ axi_bmax_value = <0x7>;
+ sof_clk_freq = <8000000>;
+ lpm_clk_freq = <32000>;
+ status = "disabled";
+
+ usbss_5: usb-controller@91e0000 {
+ compatible = "cdns,usb3";
+ reg = <0x00 0x91e0000 0x00 0x4000>,
+ <0x00 0x91e4000 0x00 0x4000>,
+ <0x00 0x91e8000 0x00 0x8000>;
+ reg-names = "otg", "dev", "xhci";
+
+ interrupts = <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "host",
+ "peripheral",
+ "otg",
+ "wakeup";
+
+ maximum-speed = "super-speed-plus";
+ dr_mode = "otg";
+ phys = <&usb3_phy4_1>;
+ phy-names = "cdns3,usb3-phy";
+ status = "disabled";
+ };
+ };
+
+ usb3_phy4: usb-phy@9210000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "cix,sky1-usb3-phy";
+ reg = <0x00 0x09210000 0x00 0x40000>;
+ status = "disabled";
+
+ resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
+ <&s5_syscon SKY1_USBPHY_SS_PST_N>;
+ reset-names = "reset", "preset";
+
+ clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
+ clock-names = "apb_clk", "ref_clk";
+
+ usb3_phy4_0: usb-port@0 {
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
+ usb3_phy4_1: usb-port@1 {
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+ };
+
pcie_x8_rc: pcie@a010000 {
compatible = "cix,sky1-pcie-host";
reg = <0x00 0x0a010000 0x00 0x10000>,
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
@ 2026-08-31 8:32 ` sashiko-bot
2026-08-31 16:07 ` Conor Dooley
2026-09-01 5:47 ` Krzysztof Kozlowski
2 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-31 8:32 UTC (permalink / raw)
To: Hongliang Yang
Cc: conor+dt, vkoul, robh, neil.armstrong, olteanv, linux-phy,
devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Custom devicetree properties do not follow standard naming conventions (missing vendor prefix and using underscores).
- [Low] The unit-address in the example node name does not match the first address in its 'reg' property.
--
commit a013b6f9b48698942425d14e2f091ce0c4c04f8c
Author: Hongliang Yang <hongliang.yang@cixtech.com>
dt-bindings: Add CIX Sky1 USB bindings
This commit adds device tree bindings for the CIX Sky1 USB3 PHY and
Cadence USBSSP controller glue layer.
> diff --git a/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
> new file mode 100644
> index 0000000000000..47202ff5b9e57
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
[ ... ]
> @@ -0,0 +1,153 @@
> + reset-names:
> + items:
> + - const: usb_preset
> + - const: usb_reset
> +
> + cix,usb_syscon:
[Severity: Low]
Does this property name follow the standard devicetree naming conventions?
Standard convention strongly prefers hyphens over underscores in property
names (e.g., cix,usb-syscon).
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description:
> + Phandle to the Sky1 S5 system controller used to program USB mode
> + strap controls.
> +
> + axi_bmax_value:
[Severity: Low]
Does this property need a vendor prefix and hyphens instead of underscores?
Custom hardware-specific properties require a vendor prefix to avoid
collisions with standard properties (e.g., cix,axi-bmax-value).
> + $ref: /schemas/types.yaml#/definitions/uint32
> + default: 0x7
> + description:
> + AXI bus maximum burst length configuration value.
[ ... ]
> +examples:
> + - |
> + #include <dt-bindings/clock/cix,sky1.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + usb@91c0300 {
[Severity: Low]
Does the unit address 91c0300 match the first address in the reg property?
The Devicetree Specification requires that the unit address in a node name
exactly matches the first address specified in the node's reg property.
Here, it does not match the actual base register address 091c0314, which
will trigger a simple_bus_reg warning when the devicetree compiler evaluates
the example.
> + compatible = "cix,sky1-usbssp";
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + reg = <0x00 0x091c0314 0x00 0x4>,
> + <0x00 0x091c0400 0x00 0x4>;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831082309.3854507-1-hongliang.yang@cixtech.com?part=1
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v1 4/4] arm64: dts: cix: Add USB4/USB5 nodes for Sky1
2026-08-31 8:23 ` [PATCH v1 4/4] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
@ 2026-08-31 8:33 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-31 8:33 UTC (permalink / raw)
To: Hongliang Yang
Cc: robh, neil.armstrong, vkoul, conor+dt, olteanv, linux-phy,
devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The parent USB wrapper nodes (`usb4`, `usb5`) and the required USB PHY node (`usb3_phy4`) are left disabled in the board device tree, preventing the USB controllers from probing.
- [Low] The unit addresses for the `usb4` and `usb5` nodes do not match their first `reg` address, violating the Device Tree specification.
- [Low] The `usb-port` child nodes inside `usb3_phy4` use unit addresses but lack the required `reg` properties.
- [Low] Incorrect `reg-names` order for the `cdns,usb3` controllers violates the DT schema binding.
--
commit fb5456e6e85188c30dcbeb49499209cebdd39407
Author: Hongliang Yang <hongliang.yang@cixtech.com>
arm64: dts: cix: Add USB4/USB5 nodes for Sky1
Add USB4 and USB5 controller nodes and their associated USB3 PHY nodes for
the CIX Sky1 SoC. Enable USB4 and USB5 in host mode 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..33d3ffdc3f9e0 100644
> --- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
> +++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
[ ... ]
> @@ -117,3 +133,23 @@ &s5_gpio2 {
> &uart2 {
> status = "okay";
> };
> +
> +&usb4 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_usb4>;
> +};
[Severity: High]
Are the usb4 and usb5 parent nodes intentionally left disabled in the board
configuration?
The parent nodes usb4 and usb5 are marked as status = "disabled" in the
DTSI. Although this patch adds pin control configurations for them, it
omits status = "okay";. The Linux DT core will not instantiate disabled
platform devices, which would prevent their children (usbss_4, usbss_5)
from probing.
> +
> +&usbss_4 {
> + status = "okay";
> + dr_mode = "host";
> +};
> +
> +&usb5 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_usb5>;
> +};
> +
> +&usbss_5 {
> + status = "okay";
> + dr_mode = "host";
> +};
[Severity: High]
Is the required PHY node (usb3_phy4) missing its enablement in this board
file?
The PHY is marked disabled by default in sky1.dtsi, but it is completely
omitted from the board file. This could cause PHY consumer requests to fail
with -ENODEV even if the controllers did probe, preventing USB4 and USB5
from functioning.
> diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
> index a77c52296ebd3..44736c0715732 100644
> --- a/arch/arm64/boot/dts/cix/sky1.dtsi
> +++ b/arch/arm64/boot/dts/cix/sky1.dtsi
[ ... ]
> @@ -574,6 +581,132 @@ mbox_ap2sfh: mailbox@80a0000 {
> cix,mbox-dir = "tx";
> };
>
> + usb4: usb@91c0300 {
> + compatible = "cix,sky1-usbssp";
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + reg = <0x00 0x091c0314 0x00 0x4>,
[Severity: Low]
Does the unit address @91c0300 need to be updated to match the first reg
address 0x091c0314? The DeviceTree specification mandates that the unit
address must exactly match the first address in the node's reg property,
which could trigger validation warnings.
> + <0x00 0x091c0400 0x00 0x4>;
> + reg-names = "axi_property", "controller_status";
> +
[ ... ]
> + usbss_4: usb-controller@91d0000 {
> + compatible = "cdns,usb3";
> + reg = <0x00 0x91d0000 0x00 0x4000>,
> + <0x00 0x91d4000 0x00 0x4000>,
> + <0x00 0x91d8000 0x00 0x8000>;
> + reg-names = "otg", "dev", "xhci";
[Severity: Low]
Is the order of the reg-names correct here?
The device tree binding for Cadence USB3 controllers requires the reg-names
property to be strictly ordered as "otg", "xhci", "dev". Do the names and
the corresponding reg tuples need to be reordered so that the "xhci" memory
region matches the required order to avoid schema validation errors?
> +
> + interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH 0>,
[ ... ]
> + usb5: usb@91c0304 {
> + compatible = "cix,sky1-usbssp";
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + reg = <0x00 0x091c0324 0x00 0x4>,
[Severity: Low]
Similarly, does the unit address @91c0304 need to be updated to match the
first reg address 0x091c0324 to avoid validation warnings?
> + <0x00 0x091c0410 0x00 0x4>;
> + reg-names = "axi_property", "controller_status";
> +
[ ... ]
> + usbss_5: usb-controller@91e0000 {
> + compatible = "cdns,usb3";
> + reg = <0x00 0x91e0000 0x00 0x4000>,
> + <0x00 0x91e4000 0x00 0x4000>,
> + <0x00 0x91e8000 0x00 0x8000>;
> + reg-names = "otg", "dev", "xhci";
[Severity: Low]
Does the order of the reg-names and corresponding reg tuples for usbss_5
also need to be adjusted to "otg", "xhci", "dev" to match the binding
schema?
> +
> + interrupts = <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH 0>,
[ ... ]
> + usb3_phy4: usb-phy@9210000 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "cix,sky1-usb3-phy";
> + reg = <0x00 0x09210000 0x00 0x40000>;
> + status = "disabled";
> +
> + resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
> + <&s5_syscon SKY1_USBPHY_SS_PST_N>;
> + reset-names = "reset", "preset";
> +
> + clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
> + <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
> + clock-names = "apb_clk", "ref_clk";
> +
> + usb3_phy4_0: usb-port@0 {
> + #phy-cells = <0>;
> + status = "disabled";
> + };
[Severity: Low]
Since this node has a unit-address (@0) and the parent has
#address-cells = <1>;, should it include a reg = <0>; property?
The DeviceTree specification states that if a node has a unit-address in
its name, it must have a corresponding reg property.
> +
> + usb3_phy4_1: usb-port@1 {
> + #phy-cells = <0>;
> + status = "disabled";
> + };
[Severity: Low]
Should this node also include a reg = <1>; property for the unit-address @1?
> + };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831082309.3854507-1-hongliang.yang@cixtech.com?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver
2026-08-31 8:23 ` [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
@ 2026-08-31 8:33 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-31 8:33 UTC (permalink / raw)
To: Hongliang Yang
Cc: linux-phy, robh, conor+dt, devicetree, vkoul, neil.armstrong,
olteanv
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Uninitialized pointer dereference and double-free of OF node reference on probe error path.
- [High] The probe function fails to propagate error codes and always returns 0 (success) on failure paths.
- [High] Missing cleanup for dynamically allocated `phy_lookup` entries causes a memory leak and Use-After-Free.
- [High] Hardware resets are unconditionally asserted without locking after the PHY provider is registered, creating a race condition with consumer initialization.
- [Medium] The error path in `sky1_u3phy_init` mistakenly deasserts the hardware resets instead of asserting them.
- [Medium] Missing `MODULE_DEVICE_TABLE(of, ...)` macro prevents automatic module loading.
--
commit 2c8f0e43d1973dd21cdeec9d0d419e0f7d1939aa
Author: Hongliang Yang <hongliang.yang@cixtech.com>
phy: cix: Add CIX Sky1 USB3 PHY driver
This patch adds a new PHY driver for the USB3 PHY found on CIX Sky1 SoCs.
The driver handles initialization, reset, and clock management for the
multi-port USB3 PHY.
> diff --git a/drivers/phy/cix/phy-cix-usb3.c b/drivers/phy/cix/phy-cix-usb3.c
> new file mode 100644
> index 0000000000000..3b6e2669b09d8
> --- /dev/null
> +++ b/drivers/phy/cix/phy-cix-usb3.c
[ ... ]
> +static int sky1_u3phy_init(struct cix_u3phy *u3phy)
> +{
[ ... ]
> +disable_ref_clk:
> + clk_disable_unprepare(u3phy->ref_clk);
> +disable_apb_clk:
> + clk_disable_unprepare(u3phy->apb_clk);
> +assert_reset_preset:
> + reset_control_deassert(u3phy->preset);
> + reset_control_deassert(u3phy->reset);
[Severity: Medium]
Does this error path correctly assert the reset lines?
The code calls reset_control_deassert() instead of reset_control_assert(),
which leaves the PHY in a deasserted reset state while its clocks are
disabled. This might violate the intended quiescent hardware state and
could potentially lead to hardware lockups or excessive power consumption.
> + u3phy->init = false;
> + return ret;
> +}
[ ... ]
> +static int cix_u3phy_probe(struct platform_device *pdev)
> +{
[ ... ]
> + device_for_each_child_node(dev, child_fn) {
> + child_np = to_of_node(child_fn);
> + if (!strncmp(fwnode_get_name(child_fn), "usb-port", 8) ||
> + !strncmp(fwnode_get_name(child_fn), "USB", 3))
> + phy = devm_phy_create(dev, child_np, &cix_u3phy_ops);
> + else
> + continue;
> + if (IS_ERR(phy)) {
> + dev_err(dev, "failed to create phy: %s\n",
> + fwnode_get_name(child_fn));
> + goto put_child;
[Severity: High]
Does this error path intentionally return 0 instead of propagating the
error code?
The variable 'ret' is initialized to 0 and is not updated with the error
from devm_phy_create() before jumping to the cleanup label. This causes
the kernel framework to consider the driver successfully probed despite
the initialization failure.
> + }
> + phy_set_drvdata(phy, u3phy);
> + phy_create_lookup(phy, fwnode_get_name(child_fn), dev_name(dev));
[Severity: High]
Is there a corresponding cleanup mechanism for this dynamically allocated
phy_lookup entry?
The driver creates lookup entries using phy_create_lookup() but does not
seem to provide a .remove callback or devres action to call
phy_remove_lookup(). When the driver is unbound, devres frees the struct
phy, leaving dangling pointers in the global lookup list which could cause
a use-after-free for subsequent lookups by consumers.
> + }
> + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> + if (IS_ERR(phy_provider)) {
> + dev_err(dev, "failed to register phy provider\n");
> + goto put_child;
[Severity: High]
Similar to the devm_phy_create() error path, does this drop the error code
from devm_of_phy_provider_register()?
Additionally, does jumping to put_child here cause a double-free or a
dereference of an uninitialized pointer?
If the device_for_each_child_node loop executed zero times, child_np is
completely uninitialized. If the loop executed at least once and completed
normally, child_np holds a pointer to the last child whose reference was
already released by the iterator's final condition check.
> + }
> + reset_control_assert(u3phy->reset);
> + reset_control_assert(u3phy->preset);
[Severity: High]
Is it safe to unconditionally assert these hardware resets here without
locking after the PHY provider is registered?
A deferred consumer could immediately acquire the exposed PHY and call
phy_init(), which initializes the PHY and deasserts resets while holding a
mutex. Concurrently, this probe function unconditionally executes
reset_control_assert() without holding the mutex, which might forcefully
reset the active PHY hardware.
> + return 0;
> +
> +put_child:
> + of_node_put(child_np);
> + return ret;
> +}
> +
> +static const struct of_device_id cix_u3phy_dt_match[] = {
> + {
> + .compatible = "cix,sky1-usb3-phy",
> + .data = &sky1_u3phy_cfg
> + },
> + { /* sentinel */ }
> +};
[Severity: Medium]
Is the MODULE_DEVICE_TABLE(of, ...) macro missing here?
Without it, the driver module cannot be automatically loaded by udev or kmod
based on the Device Tree compatible string.
> +
> +static struct platform_driver cix_usb3_phy_driver = {
> + .probe = cix_u3phy_probe,
> + .driver = {
> + .name = "cix-usb3-phy",
> + .of_match_table = cix_u3phy_dt_match,
> + },
> +};
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831082309.3854507-1-hongliang.yang@cixtech.com?part=2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver
2026-08-31 8:23 ` [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
@ 2026-08-31 8:41 ` sashiko-bot
2026-08-31 8:57 ` Philipp Zabel
1 sibling, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-31 8:41 UTC (permalink / raw)
To: Hongliang Yang
Cc: neil.armstrong, olteanv, conor+dt, devicetree, vkoul, linux-phy,
robh
Thank you for your contribution! Sashiko AI review found 11 potential issue(s) to consider:
- [High] IOMEM, clock, and reset leaks on probe error paths.
- [High] Missing get_device() causes unsafe device pointer storage in match function, leading to Use-After-Free.
- [High] Global state corruption of shared static cdns_sky1_auxdata array in probe.
- [High] Flawed platform_data memory management causes leaks or double-frees when multiple or zero children exist.
- [High] Unbalanced Runtime PM state during driver removal.
- [High] Incorrect hardware reset logic during driver removal.
- [High] Signed integer overflow in clock frequency calculations.
- [High] Writing inverted AXI_HALT bitmask to the capability register inadvertently sets all other bits to 1.
- [Medium] Device node reference leak in sky1_of_get_addr_by_name.
- [Medium] Clock error recovery paths destructively nullify devres managed clock pointers.
- [Low] Signed integer overflow when defining CFG_3XPORT_MODE_DIS_SSP.
--
commit f5b41d8b4e76c0521bdb8557f0e54b6023714500
Author: Hongliang Yang <hongliang.yang@cixtech.com>
usb: cdns3: Add CIX Sky1 glue driver
Add glue layer for the Cadence USBSSP DRD controller on CIX Sky1 SoCs.
The glue driver handles platform-specific initialization including clocks,
resets, and PHY configuration.
> diff --git a/drivers/usb/cdns3/cdnsp-sky1.c b/drivers/usb/cdns3/cdnsp-sky1.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdnsp-sky1.c
> @@ -0,0 +1,682 @@
[ ... ]
> +static int cdnsp_sky1_clk_enable_resume(struct device *dev)
> +{
[ ... ]
> +err_usb_clks:
> + cix_usb_clks[i] = NULL;
> + while (--i >= 0) {
> + clk_disable_unprepare(cix_usb_clks[i]);
> + cix_usb_clks[i] = NULL;
[Severity: Medium]
Is it safe to nullify these clock pointers?
Because the clocks are devres-managed, devres will still properly free them,
but nullifying the pointers destroys the driver's ability to ever reference
them again.
If a transient error occurs during a resume attempt, any subsequent retry or
cleanup will fail because the driver lost its internal pointers.
> + }
> + return ret;
> +};
[ ... ]
> +static void cdnsp_sky1_configure_controller(struct cdnsp_sky1 *data)
> +{
[ ... ]
> + writel(AXI_HALT, data->device_base + D_XEC_AXI_CAP);
> + writel(AXI_HALT, data->xhci_base + D_XEC_AXI_CAP);
> + writel(data->axi_bmax_value, data->device_base + D_XEC_AXI_CTRL0);
> + writel(data->axi_bmax_value, data->xhci_base + D_XEC_AXI_CTRL0);
> + writel((~(u32)(AXI_HALT)), data->device_base + D_XEC_AXI_CAP);
> + writel((~(u32)(AXI_HALT)), data->xhci_base + D_XEC_AXI_CAP);
[Severity: High]
Does this inadvertently overwrite other capability bits?
AXI_HALT is defined as BIT(31). Blindly writing ~(u32)(AXI_HALT) yields
0x7FFFFFFF, which sets bits 0 through 30 to 1 rather than preserving their
existing state with a read-modify-write or clearing them with 0.
Overwriting 31 capability bits with 1s can enable unintended modes or cause
unpredictable hardware behavior.
> + clk = data->sof_clk_freq;
> + v0 = 25 * clk / 100000000;
> + v1 = clk / 10000;
> + v2 = clk / 10;
> + writel(((v0 > 1) ? v0 - 1 : 1), data->device_base
> + + D_XEC_PRE_REG_250NS);
> + writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 1),
> + data->device_base + D_XEC_PRE_REG_1US);
> + writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 1),
> + data->device_base + D_XEC_PRE_REG_10US);
> + writel(((v1) > 1 ? v1 - 1 : 1), data->device_base
> + + D_XEC_PRE_REG_100US);
> + writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 1),
> + data->device_base + D_XEC_PRE_REG_125US);
[Severity: High]
Will this calculation overflow for frequencies above 17.1 MHz?
Because clk is typed as a signed 32-bit int, 125 * clk will exceed INT_MAX
(2,147,483,647) and overflow for values above approximately 17.1 MHz. This
yields a negative value that improperly configures the hardware pre-scaler
registers.
Could this be mitigated by using an unsigned 64-bit cast, such as 125ULL * clk?
[ ... ]
> +static void *sky1_of_get_addr_by_name(struct device_node *parent, char *name)
> +{
> + struct device_node *node;
> + int index;
> +
> + node = of_get_next_child(parent, NULL);
> + if (node) {
> + index = of_property_match_string(node, "reg-names", name);
> + if (index >= 0)
> + return of_iomap(node, index);
> + }
> + return NULL;
> +}
[Severity: Medium]
Can this leak the device node reference?
of_get_next_child() acquires and returns a device node with an incremented
reference count, but the reference is never released with of_node_put() on
either the success or failure paths.
[ ... ]
> +static struct of_dev_auxdata cdns_sky1_auxdata[] = {
> + {
> + .compatible = "cdns,usb3",
> + },
> + {},
> +};
> +
> +static int cdnsp_sky1_probe(struct platform_device *pdev)
> +{
[ ... ]
> + data->device_base = sky1_get_addr_by_name(dev, "dev");
> + if (!data->device_base)
> + return -ENODEV;
> + ret = cdnsp_sky1_drd_init(data);
> + if (ret == -ETIMEDOUT)
> + return -EPROBE_DEFER;
> + if (ret)
> + return ret;
[Severity: High]
Do these error paths leak the IOMEM regions, hardware clocks, and resets?
cdnsp_sky1_probe() manually maps IOMEM regions using sky1_get_addr_by_name()
and enables hardware clocks in cdnsp_sky1_drd_init(). If subsequent
initialization steps fail, the function returns directly or jumps to the err
label without unmapping the IOMEM with iounmap(), disabling the clocks, or
re-asserting the hardware resets.
Since probe can be retried multiple times, these leaked resources will
accumulate.
> + data->oc_gpio = devm_gpiod_get_optional(data->dev, "oc", GPIOD_IN);
> + if (IS_ERR(data->oc_gpio)) {
> + dev_err(data->dev, "can not get oc_gpio\n");
> + ret = PTR_ERR(data->oc_gpio);
> + return ret;
> + }
> + if (data->oc_gpio) {
> + ret = gpiod_direction_input(data->oc_gpio);
> + if (ret < 0)
> + dev_err(data->dev, "set oc_gpio input failed:%d\n", ret);
> + }
> + /* release by platform_device_release */
> + cdns_sky1_pdata = kzalloc(sizeof(*cdns_sky1_pdata), GFP_KERNEL);
[Severity: High]
Can this cause a memory leak or double-free of cdns_sky1_pdata?
The instance-specific structure cdns_sky1_pdata is allocated here and passed
to child devices via of_platform_populate(). If of_platform_populate() matches
zero children, the pointer is never attached to a device and permanently leaks.
If it matches multiple children, all children receive the exact same pointer,
resulting in a double-free kernel panic when each child's release function
attempts to free the same pointer upon unbind.
> + if (!cdns_sky1_pdata)
> + return -ENOMEM;
> + cdns_sky1_pdata->platform_suspend = cdns_sky1_platform_suspend;
> + cdns_sky1_pdata->quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW;
> + cdns_sky1_auxdata->platform_data = cdns_sky1_pdata;
> + ret = of_platform_populate(node, NULL, cdns_sky1_auxdata, dev);
[Severity: High]
Does this corrupt the global cdns_sky1_auxdata array?
cdns_sky1_auxdata is defined as a statically shared array for the driver, but
a dynamically allocated, instance-specific pointer (cdns_sky1_pdata) is
assigned to cdns_sky1_auxdata->platform_data.
Because the device tree contains multiple instances of this driver, probing
them concurrently or sequentially will race to overwrite this shared global
state, causing cross-device memory corruption and dangling pointers.
[ ... ]
> +static void cdnsp_sky1_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct cdnsp_sky1 *data = dev_get_drvdata(dev);
> +
> + pm_runtime_get_sync(dev);
> + of_platform_depopulate(dev);
> + sky1_put_addr(data->xhci_base);
> + sky1_put_addr(data->device_base);
> + reset_control_deassert(data->reset);
> + reset_control_deassert(data->preset);
[Severity: High]
Should these resets be asserted instead of deasserted?
The driver attempts to tear down the hardware but incorrectly calls
reset_control_deassert() again instead of reset_control_assert(). This fails
to reset the hardware and leaves it active while its clocks are subsequently
disabled, which can hang the system or cause undefined hardware behavior.
> + cdnsp_sky1_clk_disable_all(dev);
> + platform_set_drvdata(pdev, NULL);
> +}
[Severity: High]
Does this leave the device's runtime PM subsystem in an unbalanced state?
cdnsp_sky1_probe() enables runtime PM, but cdnsp_sky1_remove() omits a call
to pm_runtime_disable() before exiting.
Additionally, cdnsp_sky1_remove() calls pm_runtime_get_sync() at the start
of the function but omits a balancing pm_runtime_put() call, permanently
leaking the device's usage counter.
[ ... ]
> +static int cdnsp_sky1_find_gadget_match(struct device *dev, void *data)
> +{
[ ... ]
> + if (name && dev->bus && !strcmp(dev->bus->name, "gadget") &&
> + !strncmp(name, gadget_prefix, sizeof(gadget_prefix) - 1)) {
> + *gadget_dev = dev;
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void cdnsp_sky1_shutdown(struct platform_device *pdev)
> +{
[ ... ]
> + if (!device_may_wakeup(dev)) {
> + /*
> + * Find the cdns3 child device, then find its gadget device
> + * and release the function driver before disabling clocks.
> + * This ensures all register accesses in gadget_unbind_driver
> + * complete before clocks are turned off.
> + */
> + device_for_each_child(dev, &cdns, cdnsp_sky1_find_cdns);
> + if (cdns)
> + device_for_each_child(cdns->dev, &gadget_dev,
> + cdnsp_sky1_find_gadget_match);
> + if (gadget_dev)
> + device_release_driver(gadget_dev);
[Severity: High]
Can this result in a use-after-free condition?
In cdnsp_sky1_find_gadget_match(), a pointer to the matched device is stored
without acquiring a persistent reference via get_device(). The iterator
device_for_each_child() immediately drops its reference when advancing or
returning, leaving a bare pointer.
If the gadget device is removed concurrently immediately before
device_release_driver() is called, the pointer becomes dangling.
[ ... ]
> diff --git a/drivers/usb/cdns3/cdnsp-sky1.h b/drivers/usb/cdns3/cdnsp-sky1.h
> new file mode 100644
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdnsp-sky1.h
> @@ -0,0 +1,127 @@
[ ... ]
> +#define D_XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
> +#define XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
> +#define CFG_3XPORT_MODE_DIS_SSP (~(1 << 31))
[Severity: Low]
Does this trigger undefined behavior?
1 is treated as a signed 32-bit integer, and shifting it left by 31 bits
overflows the sign bit. Should this be explicitly unsigned, such as
(~(1U << 31)) or ~BIT(31)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831082309.3854507-1-hongliang.yang@cixtech.com?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver
2026-08-31 8:23 ` [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
2026-08-31 8:41 ` sashiko-bot
@ 2026-08-31 8:57 ` Philipp Zabel
1 sibling, 0 replies; 21+ messages in thread
From: Philipp Zabel @ 2026-08-31 8:57 UTC (permalink / raw)
To: Hongliang Yang, peter.chen, pawell, vkoul, robh, krzk+dt,
conor+dt
Cc: gregkh, rogerq, neil.armstrong, fugang.duan, cix-kernel-upstream,
linux-usb, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Gary Yang
On Mo, 2026-08-31 at 16:23 +0800, Hongliang Yang wrote:
> Add glue layer for the Cadence USBSSP DRD controller on CIX Sky1
> SoCs. The glue driver handles platform-specific initialization
> including clocks, resets, and PHY configuration.
>
> Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
> Reviewed-by: Gary Yang <gary.yang@cixtech.com>
> ---
> drivers/usb/cdns3/Kconfig | 13 +
> drivers/usb/cdns3/Makefile | 1 +
> drivers/usb/cdns3/cdnsp-sky1.c | 682 +++++++++++++++++++++++++++++++++
> drivers/usb/cdns3/cdnsp-sky1.h | 127 ++++++
> 4 files changed, 823 insertions(+)
> create mode 100644 drivers/usb/cdns3/cdnsp-sky1.c
> create mode 100644 drivers/usb/cdns3/cdnsp-sky1.h
>
[...]
> diff --git a/drivers/usb/cdns3/cdnsp-sky1.c b/drivers/usb/cdns3/cdnsp-sky1.c
> new file mode 100644
> index 000000000000..3dd20451569e
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdnsp-sky1.c
> @@ -0,0 +1,682 @@
[...]
> +static int cdnsp_sky1_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *node = dev->of_node;
> + struct cdnsp_sky1 *data;
> + int ret = 0;
> + struct cdns3_platform_data *cdns_sky1_pdata;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> + data->axi_base = devm_platform_ioremap_resource_byname(pdev, "axi_property");
> + if (IS_ERR(data->axi_base)) {
> + dev_err(dev, "can't map IOMEM resource\n");
> + return PTR_ERR(data->axi_base);
> + }
> + data->ctst_base = devm_platform_ioremap_resource_byname(pdev, "controller_status");
> + if (IS_ERR(data->ctst_base)) {
> + dev_err(dev, "can't map IOMEM resource\n");
> + return PTR_ERR(data->ctst_base);
> + }
> + data->reset = devm_reset_control_get(&pdev->dev, "usb_reset");
Please use devm_reset_control_get_exclusive() directly.
regards
Philipp
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
2026-08-31 8:32 ` sashiko-bot
@ 2026-08-31 16:07 ` Conor Dooley
2026-09-01 5:47 ` Krzysztof Kozlowski
2 siblings, 0 replies; 21+ messages in thread
From: Conor Dooley @ 2026-08-31 16:07 UTC (permalink / raw)
To: Hongliang Yang
Cc: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt, gregkh,
rogerq, neil.armstrong, p.zabel, fugang.duan, cix-kernel-upstream,
linux-usb, linux-phy, devicetree, linux-arm-kernel, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 9727 bytes --]
On Mon, Aug 31, 2026 at 04:23:05PM +0800, Hongliang Yang wrote:
> Add device tree bindings for CIX Sky1 USB3 PHY and Cadence USBSSP
> controller glue layer.
>
> Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
> ---
> .../bindings/phy/cix,sky1-usb3-phy.yaml | 109 +++++++++++++
> .../bindings/usb/cix,sky1-usbssp.yaml | 153 ++++++++++++++++++
Two patches please.
> MAINTAINERS | 3 +
> 3 files changed, 265 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> create mode 100644 Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
>
> diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> new file mode 100644
> index 000000000000..76d020df72dd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/phy/cix,sky1-usb3-phy.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: CIX Sky1 USB3 PHY
> +
> +maintainers:
> + - Hongliang Yang <hongliang.yang@cixtech.com>
> +
> +description:
> + The CIX Sky1 USB3 PHY is a multi-port SuperSpeedPlus PHY that
> + supports up to two USB3 lanes. Each port is represented as a
> + child node of the PHY controller.
> +
> +properties:
> + compatible:
> + const: cix,sky1-usb3-phy
> +
> + reg:
> + maxItems: 1
> +
> + clocks:
> + items:
> + - description: APB register interface clock
> + - description: PHY reference clock
> +
> + clock-names:
> + items:
> + - const: apb_clk
> + - const: ref_clk
drop "_clk", they're both clocks..
pw-bot: changes-requested
> +
> + resets:
> + items:
> + - description: PHY reset
> + - description: PHY APB preset
> +
> + reset-names:
> + items:
> + - const: reset
> + - const: preset
drop "reset" from here, they're both resets. Call them "core" and "apb"
or something instead.
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> +patternProperties:
> + "^usb-port@[0-9a-f]+$":
> + type: object
> + description:
> + A single USB3 PHY port/lane.
> +
> + properties:
> + reg:
> + maxItems: 1
> +
> + "#phy-cells":
> + const: 0
> +
> + required:
> + - reg
> + - "#phy-cells"
> +
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> + - clock-names
> + - resets
> + - reset-names
> + - "#address-cells"
> + - "#size-cells"
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/cix,sky1.h>
> + #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
> +
> + usb-phy@9210000 {
> + compatible = "cix,sky1-usb3-phy";
> + reg = <0x09210000 0x40000>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
> + <&s5_syscon SKY1_USBPHY_SS_PST_N>;
> + reset-names = "reset", "preset";
> +
> + clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
> + <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
> + clock-names = "apb_clk", "ref_clk";
> +
> + usb-port@0 {
> + reg = <0>;
> + #phy-cells = <0>;
> + };
> +
> + usb-port@1 {
> + reg = <1>;
> + #phy-cells = <0>;
> + };
> + };
> diff --git a/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
> new file mode 100644
> index 000000000000..47202ff5b9e5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
> @@ -0,0 +1,153 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/usb/cix,sky1-usbssp.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: CIX Sky1 Cadence USBSSP Controller
> +
> +maintainers:
> + - Hongliang Yang <hongliang.yang@cixtech.com>
> +
> +description:
> + The CIX Sky1 USBSSP controller is based on the Cadence USBSSP DRD
> + controller. The integration adds glue registers and mode strap controls
> + in the Sky1 S5 system controller. The CIX glue node wraps a Cadence
> + USBSSP core as a child node.
> +
> +properties:
> + compatible:
> + const: cix,sky1-usbssp
> +
> + reg:
> + items:
> + - description: AXI property configuration register
> + - description: Controller status register
> +
> + reg-names:
> + items:
> + - const: axi_property
> + - const: controller_status
> +
> + clocks:
> + items:
> + - description: Start-of-frame clock
> + - description: AXI bus clock
> + - description: Low-power mode clock
> + - description: APB register interface clock
> +
> + clock-names:
> + items:
> + - const: sof_clk
> + - const: usb_aclk
> + - const: lpm_clk
> + - const: usb_pclk
Same stuff here on naming
> +
> + resets:
> + items:
> + - description: APB register reset
> + - description: Controller reset
> +
> + reset-names:
> + items:
> + - const: usb_preset
> + - const: usb_reset
and here.
> +
> + cix,usb_syscon:
No underscores in property names.
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description:
> + Phandle to the Sky1 S5 system controller used to program USB mode
> + strap controls.
> +
> + axi_bmax_value:
Missing vendor prefix.
No underscores in property names.
Why is this not a fixed value per SoC?
Cheers,
Conor.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + default: 0x7
> + description:
> + AXI bus maximum burst length configuration value.
> +
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 2
> +
> + ranges: true
> +
> +patternProperties:
> + "^usb-controller@[0-9a-f]+$":
> + $ref: cdns,usb3.yaml#
> + description:
> + The Cadence USBSSP core controller node. See cdns,usb3.yaml
> + for properties such as dr_mode, maximum-speed, phys, and interrupts.
> + unevaluatedProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - clocks
> + - clock-names
> + - resets
> + - reset-names
> + - cix,usb_syscon
> + - "#address-cells"
> + - "#size-cells"
> + - ranges
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/cix,sky1.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + usb@91c0300 {
> + compatible = "cix,sky1-usbssp";
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + reg = <0x00 0x091c0314 0x00 0x4>,
> + <0x00 0x091c0400 0x00 0x4>;
> + reg-names = "axi_property", "controller_status";
> +
> + resets = <&src SKY1_USBC_SS2_PRST_N>,
> + <&src SKY1_USBC_SS2_RST_N>;
> + reset-names = "usb_preset", "usb_reset";
> +
> + clocks = <&scmi_clk CLK_TREE_USB3A_H0_CLK_SOF>,
> + <&scmi_clk CLK_TREE_USB3A_0_AXI_GATE>,
> + <&scmi_clk CLK_TREE_USB3A_H0_CLK_LPM>,
> + <&scmi_clk CLK_TREE_USB3A_0_APB_GATE>;
> + clock-names = "sof_clk", "usb_aclk", "lpm_clk", "usb_pclk";
> +
> + cix,usb_syscon = <&src>;
> + axi_bmax_value = <0x7>;
> +
> + usb-controller@91d0000 {
> + compatible = "cdns,usb3";
> + reg = <0x00 0x91d0000 0x00 0x4000>,
> + <0x00 0x91d8000 0x00 0x8000>,
> + <0x00 0x91d4000 0x00 0x4000>;
> + reg-names = "otg", "xhci", "dev";
> +
> + interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "host", "peripheral", "otg", "wakeup";
> +
> + /* Cadence USBSSP core properties - see cdns,usb3.yaml */
> + maximum-speed = "super-speed-plus";
> + dr_mode = "otg";
> +
> + phys = <&usb3_phy4_0>, <&usb2_phy6>;
> + phy-names = "cdns3,usb3-phy", "cdns3,usb2-phy";
> + };
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 15011f5752a9..4c5f6cbe9124 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2775,8 +2775,11 @@ S: Maintained
> T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git
> F: Documentation/devicetree/bindings/arm/cix.yaml
> F: Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
> +F: Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> +F: Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
> F: arch/arm64/boot/dts/cix/
> F: drivers/mailbox/cix-mailbox.c
> +F: drivers/phy/cix/
> K: \bcix\b
>
> ARM/CLKDEV SUPPORT
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
2026-08-31 8:32 ` sashiko-bot
2026-08-31 16:07 ` Conor Dooley
@ 2026-09-01 5:47 ` Krzysztof Kozlowski
2026-09-02 3:23 ` Hongliang Yang
2 siblings, 1 reply; 21+ messages in thread
From: Krzysztof Kozlowski @ 2026-09-01 5:47 UTC (permalink / raw)
To: Hongliang Yang, peter.chen, pawell, vkoul, robh, krzk+dt,
conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel
On 31/08/2026 10:23, Hongliang Yang wrote:
> Add device tree bindings for CIX Sky1 USB3 PHY and Cadence USBSSP
> controller glue layer.
>
> Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
> ---
> .../bindings/phy/cix,sky1-usb3-phy.yaml | 109 +++++++++++++
> .../bindings/usb/cix,sky1-usbssp.yaml | 153 ++++++++++++++++++
Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
A nit, subject: drop second/last, redundant "bindings". The
"dt-bindings" prefix is already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v7.1-rc7/source/Documentation/devicetree/bindings/submitting-patches.rst#L23
And do not combine USB patches with other subsystems.
> MAINTAINERS | 3 +
> 3 files changed, 265 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> create mode 100644 Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
>
> diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> new file mode 100644
> index 000000000000..76d020df72dd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/phy/cix,sky1-usb3-phy.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: CIX Sky1 USB3 PHY
> +
> +maintainers:
> + - Hongliang Yang <hongliang.yang@cixtech.com>
> +
> +description:
> + The CIX Sky1 USB3 PHY is a multi-port SuperSpeedPlus PHY that
> + supports up to two USB3 lanes. Each port is represented as a
> + child node of the PHY controller.
Please read and follow DTS coding style.
> +
> +properties:
> + compatible:
> + const: cix,sky1-usb3-phy
> +
> + reg:
> + maxItems: 1
> +
> + clocks:
> + items:
> + - description: APB register interface clock
> + - description: PHY reference clock
> +
> + clock-names:
> + items:
> + - const: apb_clk
> + - const: ref_clk
> +
> + resets:
> + items:
> + - description: PHY reset
> + - description: PHY APB preset
> +
> + reset-names:
> + items:
> + - const: reset
> + - const: preset
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> +patternProperties:
> + "^usb-port@[0-9a-f]+$":
> + type: object
> + description:
> + A single USB3 PHY port/lane.
> +
> + properties:
> + reg:
> + maxItems: 1
> +
> + "#phy-cells":
> + const: 0
> +
> + required:
> + - reg
> + - "#phy-cells"
> +
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> + - clock-names
> + - resets
> + - reset-names
> + - "#address-cells"
> + - "#size-cells"
> +
> +unevaluatedProperties: false
Which other schema do you want to reference?
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/cix,sky1.h>
> + #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
> +
> + usb-phy@9210000 {
> + compatible = "cix,sky1-usb3-phy";
> + reg = <0x09210000 0x40000>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
> + <&s5_syscon SKY1_USBPHY_SS_PST_N>;
> + reset-names = "reset", "preset";
> +
> + clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
> + <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
> + clock-names = "apb_clk", "ref_clk";
> +
> + usb-port@0 {
> + reg = <0>;
> + #phy-cells = <0>;
> + };
> +
> + usb-port@1 {
> + reg = <1>;
> + #phy-cells = <0>;
> + };
> + };
> diff --git a/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
> new file mode 100644
> index 000000000000..47202ff5b9e5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
> @@ -0,0 +1,153 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/usb/cix,sky1-usbssp.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: CIX Sky1 Cadence USBSSP Controller
> +
> +maintainers:
> + - Hongliang Yang <hongliang.yang@cixtech.com>
> +
> +description:
> + The CIX Sky1 USBSSP controller is based on the Cadence USBSSP DRD
> + controller. The integration adds glue registers and mode strap controls
> + in the Sky1 S5 system controller. The CIX glue node wraps a Cadence
> + USBSSP core as a child node.
> +
> +properties:
> + compatible:
> + const: cix,sky1-usbssp
> +
> + reg:
> + items:
> + - description: AXI property configuration register
> + - description: Controller status register
Registers are not address spaces. Devices do not have address spaces of
one register, so this is not a real device.
> +
> + reg-names:
> + items:
> + - const: axi_property
axi
> + - const: controller_status
controller
> +
> + clocks:
> + items:
> + - description: Start-of-frame clock
> + - description: AXI bus clock
> + - description: Low-power mode clock
> + - description: APB register interface clock
> +
> + clock-names:
> + items:
> + - const: sof_clk
> + - const: usb_aclk
> + - const: lpm_clk
> + - const: usb_pclk
> +
> + resets:
> + items:
> + - description: APB register reset
> + - description: Controller reset
> +
> + reset-names:
> + items:
> + - const: usb_preset
> + - const: usb_reset
> +
> + cix,usb_syscon:
> + $ref: /schemas/types.yaml#/definitions/phandle
> + description:
> + Phandle to the Sky1 S5 system controller used to program USB mode
> + strap controls.
So this is part of that device most likely.
> +
> + axi_bmax_value:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + default: 0x7
> + description:
> + AXI bus maximum burst length configuration value.
> +
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 2
> +
> + ranges: true
> +
> +patternProperties:
> + "^usb-controller@[0-9a-f]+$":
Why using completely different names than entire Linux kernel?
> + $ref: cdns,usb3.yaml#
> + description:
> + The Cadence USBSSP core controller node. See cdns,usb3.yaml
> + for properties such as dr_mode, maximum-speed, phys, and interrupts.
Redundant description.
> + unevaluatedProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - clocks
> + - clock-names
> + - resets
> + - reset-names
> + - cix,usb_syscon
> + - "#address-cells"
> + - "#size-cells"
> + - ranges
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/cix,sky1.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + usb@91c0300 {
> + compatible = "cix,sky1-usbssp";
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> + reg = <0x00 0x091c0314 0x00 0x4>,
> + <0x00 0x091c0400 0x00 0x4>;
Nope. You do not have a device with two registers.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 0/5] Add CIX Sky1 USB3/USBSSP support
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (3 preceding siblings ...)
2026-08-31 8:23 ` [PATCH v1 4/4] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
@ 2026-09-02 3:21 ` Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 1/5] dt-bindings: phy: Add CIX Sky1 USB3 PHY Hongliang Yang
` (4 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:21 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add USB3 PHY binding and driver, Cadence USBSSP glue binding and
driver, and USB4/USB5 device tree nodes for the CIX Sky1 SoC.
This series adds support for the USB4 and USB5 controllers (based on
Cadence USBSSP DRD) found on the CIX Sky1 SoC, along with their
associated multi-port USB3 PHY.
Changes in v2:
- Split the dt-bindings patch into separate PHY and USBSSP binding
patches with proper subsystem subject prefixes.
- Follow the DTS coding style in the bindings and DTS (property
ordering, no redundant unit-address suffixes in examples); examples
pass dt-check-style in strict mode.
- Drop the redundant "_clk" suffix and "reset"/"usb" prefixes from
clock-names and reset-names ("apb_clk" -> "apb", "ref_clk" ->
"ref", "reset"/"preset" -> "core"/"apb", "usb_preset"/"usb_reset"
-> "apb"/"core", "sof_clk"/"usb_aclk"/"lpm_clk"/"usb_pclk" ->
"sof"/"axi"/"lpm"/"apb").
- Rename reg-names "axi_property"/"controller_status" to
"axi"/"controller".
- Rename the Cadence core child nodes from usb-controller@ to the
conventional usb@ node name.
- Drop the cix,axi-bmax-value, sof_clk_freq and lpm_clk_freq
properties. These are fixed per SoC and are now hardcoded in the
glue driver.
- Rename cix,usb_syscon -> cix,usb-syscon to follow devicetree
property naming conventions.
- Use additionalProperties: false instead of
unevaluatedProperties: false in the PHY binding (no other schema
is referenced).
- Fix unit addresses in the binding example and DTS nodes to match
the first reg address (usb@91c0314, usb@91c0324).
- Enable the usb4/usb5 wrapper nodes, usb3_phy4 PHY and its ports in
the Orion O6 board file so the controllers can probe.
- Add reg properties to the usb-port@0/usb-port@1 child nodes.
- Fix the reg-names order of the cdns,usb3 child nodes to
"otg", "xhci", "dev" as required by the binding schema.
- Use devm_reset_control_get_exclusive() instead of the deprecated
devm_reset_control_get() in the USB3 PHY and glue drivers.
- Propagate error codes from the PHY driver probe paths instead of
returning 0 unconditionally.
- Use device_for_each_child_node_scoped() to drop the manual
of_node_put() and fix the double-put/uninitialized access on the
probe error path.
- Remove the phy_create_lookup() entries which leaked on unbind and
were unused (consumers reference the PHY via the DT "phys"
phandle).
- Assert (not deassert) the hardware resets on the sky1_u3phy_init()
error path.
- Add MODULE_DEVICE_TABLE(of, ...) for automatic module loading.
Hongliang Yang (5):
dt-bindings: phy: Add CIX Sky1 USB3 PHY
dt-bindings: usb: Add CIX Sky1 USBSSP controller
phy: cix: Add CIX Sky1 USB3 PHY driver
usb: cdns3: Add CIX Sky1 glue driver
arm64: dts: cix: Add USB4/USB5 nodes for Sky1
.../bindings/phy/cix,sky1-usb3-phy.yaml | 107 +++
.../bindings/usb/cix,sky1-usbssp.yaml | 137 ++++
MAINTAINERS | 3 +
arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 50 ++
arch/arm64/boot/dts/cix/sky1.dtsi | 125 ++++
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/cix/Kconfig | 20 +
drivers/phy/cix/Makefile | 6 +
drivers/phy/cix/phy-cix-usb3.c | 407 +++++++++++
drivers/phy/cix/phy-cix-usbdp.h | 310 ++++++++
drivers/usb/cdns3/Kconfig | 13 +
drivers/usb/cdns3/Makefile | 1 +
drivers/usb/cdns3/cdnsp-sky1.c | 673 ++++++++++++++++++
drivers/usb/cdns3/cdnsp-sky1.h | 127 ++++
15 files changed, 1981 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
create mode 100644 Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
create mode 100644 drivers/phy/cix/Kconfig
create mode 100644 drivers/phy/cix/Makefile
create mode 100644 drivers/phy/cix/phy-cix-usb3.c
create mode 100644 drivers/phy/cix/phy-cix-usbdp.h
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.c
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.h
--
2.54.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 1/5] dt-bindings: phy: Add CIX Sky1 USB3 PHY
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (4 preceding siblings ...)
2026-09-02 3:21 ` [PATCH v2 0/5] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
@ 2026-09-02 3:21 ` Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 2/5] dt-bindings: usb: Add CIX Sky1 USBSSP controller Hongliang Yang
` (3 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:21 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add device tree binding for the CIX Sky1 USB3 PHY, a multi-port
SuperSpeedPlus PHY supporting up to two USB3 lanes.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
.../bindings/phy/cix,sky1-usb3-phy.yaml | 107 ++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
diff --git a/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
new file mode 100644
index 000000000000..5b025a48c844
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/cix,sky1-usb3-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CIX Sky1 USB3 PHY
+
+maintainers:
+ - Hongliang Yang <hongliang.yang@cixtech.com>
+
+description:
+ The CIX Sky1 USB3 PHY is a multi-port SuperSpeedPlus PHY that
+ supports up to two USB3 lanes. Each port is represented as a
+ child node of the PHY controller.
+
+properties:
+ compatible:
+ const: cix,sky1-usb3-phy
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: APB register interface clock
+ - description: PHY reference clock
+
+ clock-names:
+ items:
+ - const: apb
+ - const: ref
+
+ resets:
+ items:
+ - description: PHY reset
+ - description: PHY APB preset
+
+ reset-names:
+ items:
+ - const: core
+ - const: apb
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^usb-port@[0-9a-f]+$":
+ type: object
+ description:
+ A single USB3 PHY port/lane.
+
+ properties:
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 0
+
+ required:
+ - reg
+ - "#phy-cells"
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - "#address-cells"
+ - "#size-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/cix,sky1.h>
+ #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+
+ usb-phy@9210000 {
+ compatible = "cix,sky1-usb3-phy";
+ reg = <0x9210000 0x40000>;
+ #address-cells = <1>;
+ clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
+ clock-names = "apb", "ref";
+ resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
+ <&s5_syscon SKY1_USBPHY_SS_PST_N>;
+ reset-names = "core", "apb";
+ #size-cells = <0>;
+
+ usb-port@0 {
+ reg = <0>;
+ #phy-cells = <0>;
+ };
+
+ usb-port@1 {
+ reg = <1>;
+ #phy-cells = <0>;
+ };
+ };
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 2/5] dt-bindings: usb: Add CIX Sky1 USBSSP controller
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (5 preceding siblings ...)
2026-09-02 3:21 ` [PATCH v2 1/5] dt-bindings: phy: Add CIX Sky1 USB3 PHY Hongliang Yang
@ 2026-09-02 3:21 ` Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 3/5] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
` (2 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:21 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add device tree binding for the CIX Sky1 Cadence USBSSP controller
glue layer. The integration adds glue registers and mode strap
controls in the Sky1 S5 system controller. The CIX glue node wraps
a Cadence USBSSP core as a child node.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
.../bindings/usb/cix,sky1-usbssp.yaml | 137 ++++++++++++++++++
MAINTAINERS | 3 +
2 files changed, 140 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
diff --git a/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
new file mode 100644
index 000000000000..73231a2741bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/cix,sky1-usbssp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CIX Sky1 Cadence USBSSP Controller
+
+maintainers:
+ - Hongliang Yang <hongliang.yang@cixtech.com>
+
+description:
+ The CIX Sky1 USBSSP controller is based on the Cadence USBSSP DRD
+ controller. The integration adds glue registers and mode strap controls
+ in the Sky1 S5 system controller. The CIX glue node wraps a Cadence
+ USBSSP core as a child node.
+
+properties:
+ compatible:
+ const: cix,sky1-usbssp
+
+ reg:
+ items:
+ - description: AXI property configuration register
+ - description: Controller status register
+
+ reg-names:
+ items:
+ - const: axi
+ - const: controller
+
+ clocks:
+ items:
+ - description: Start-of-frame clock
+ - description: AXI bus clock
+ - description: Low-power mode clock
+ - description: APB register interface clock
+
+ clock-names:
+ items:
+ - const: sof
+ - const: axi
+ - const: lpm
+ - const: apb
+
+ resets:
+ items:
+ - description: APB register reset
+ - description: Controller reset
+
+ reset-names:
+ items:
+ - const: apb
+ - const: core
+
+ cix,usb-syscon:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the Sky1 S5 system controller used to program USB mode
+ strap controls.
+
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 2
+
+ ranges: true
+
+patternProperties:
+ "^usb@[0-9a-f]+$":
+ $ref: cdns,usb3.yaml#
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - cix,usb-syscon
+ - "#address-cells"
+ - "#size-cells"
+ - ranges
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/cix,sky1.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ usb@91c0314 {
+ compatible = "cix,sky1-usbssp";
+ reg = <0x00 0x091c0314 0x00 0x4>,
+ <0x00 0x091c0400 0x00 0x4>;
+ reg-names = "axi", "controller";
+ ranges;
+ #address-cells = <2>;
+
+ clocks = <&scmi_clk CLK_TREE_USB3A_H0_CLK_SOF>,
+ <&scmi_clk CLK_TREE_USB3A_0_AXI_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_H0_CLK_LPM>,
+ <&scmi_clk CLK_TREE_USB3A_0_APB_GATE>;
+ clock-names = "sof", "axi", "lpm", "apb";
+ resets = <&src SKY1_USBC_SS2_PRST_N>,
+ <&src SKY1_USBC_SS2_RST_N>;
+ reset-names = "apb", "core";
+ #size-cells = <2>;
+ cix,usb-syscon = <&src>;
+
+ usb@91d0000 {
+ compatible = "cdns,usb3";
+ reg = <0x00 0x91d0000 0x00 0x4000>,
+ <0x00 0x91d8000 0x00 0x8000>,
+ <0x00 0x91d4000 0x00 0x4000>;
+ reg-names = "otg", "xhci", "dev";
+ dr_mode = "otg";
+
+ interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host", "peripheral", "otg", "wakeup";
+ maximum-speed = "super-speed-plus";
+
+ phys = <&usb3_phy4_0>, <&usb2_phy6>;
+ phy-names = "cdns3,usb3-phy", "cdns3,usb2-phy";
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..4c5f6cbe9124 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2775,8 +2775,11 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/cix.git
F: Documentation/devicetree/bindings/arm/cix.yaml
F: Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
+F: Documentation/devicetree/bindings/phy/cix,sky1-usb3-phy.yaml
+F: Documentation/devicetree/bindings/usb/cix,sky1-usbssp.yaml
F: arch/arm64/boot/dts/cix/
F: drivers/mailbox/cix-mailbox.c
+F: drivers/phy/cix/
K: \bcix\b
ARM/CLKDEV SUPPORT
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 3/5] phy: cix: Add CIX Sky1 USB3 PHY driver
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (6 preceding siblings ...)
2026-09-02 3:21 ` [PATCH v2 2/5] dt-bindings: usb: Add CIX Sky1 USBSSP controller Hongliang Yang
@ 2026-09-02 3:21 ` Hongliang Yang
2026-09-02 3:32 ` sashiko-bot
2026-09-02 3:21 ` [PATCH v2 4/5] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 5/5] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
9 siblings, 1 reply; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:21 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add PHY driver for the USB3 PHY found on CIX Sky1 SoCs. The driver
handles initialization, reset, and clock management for the
multi-port USB3 PHY.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/cix/Kconfig | 20 ++
drivers/phy/cix/Makefile | 6 +
drivers/phy/cix/phy-cix-usb3.c | 407 ++++++++++++++++++++++++++++++++
drivers/phy/cix/phy-cix-usbdp.h | 310 ++++++++++++++++++++++++
6 files changed, 745 insertions(+)
create mode 100644 drivers/phy/cix/Kconfig
create mode 100644 drivers/phy/cix/Makefile
create mode 100644 drivers/phy/cix/phy-cix-usb3.c
create mode 100644 drivers/phy/cix/phy-cix-usbdp.h
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19f3b7d12b7d..cb0d4be47628 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -175,6 +175,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 d7aa516bcc49..6fd0f3910018 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -27,6 +27,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..aaa2eb9d4552
--- /dev/null
+++ b/drivers/phy/cix/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Phy drivers for Cix PHYs
+#
+
+if ARCH_CIX || COMPILE_TEST
+
+config PHY_CIX_USB3
+ tristate "Cix USB3 PHY Driver"
+ depends on OF && HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Enable this to support the CIX USB3 PHY for TYPEA Connection.
+
+ This driver provides initialization and power management for
+ the USB3 PHY found on CIX Sky1 SoCs.
+
+ If unsure, say N.
+
+endif # ARCH_CIX || COMPILE_TEST
diff --git a/drivers/phy/cix/Makefile b/drivers/phy/cix/Makefile
new file mode 100644
index 000000000000..b46b1059b2ab
--- /dev/null
+++ b/drivers/phy/cix/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Cix phy drivers.
+#
+
+obj-$(CONFIG_PHY_CIX_USB3) += phy-cix-usb3.o
diff --git a/drivers/phy/cix/phy-cix-usb3.c b/drivers/phy/cix/phy-cix-usb3.c
new file mode 100644
index 000000000000..22bc1b4630a4
--- /dev/null
+++ b/drivers/phy/cix/phy-cix-usb3.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy driver for cdn_sd0803_t7g_typea
+ *
+ * Author: Hongliang Yang <hongliang.yang@cixtech.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include "phy-cix-usbdp.h"
+
+struct cix_u3phy;
+
+struct cix_u3phy_cfg {
+ int (*u3phy_init)(struct cix_u3phy *u3phy);
+ int (*u3phy_exit)(struct cix_u3phy *u3phy);
+};
+
+static const struct reg_sequence sky1_u3phy_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_ITER_TMR_LANE0, 0x0078},
+ {RX_SDCAL0_ITER_TMR_LANE1, 0x0078},
+ {RX_SDCAL1_INIT_TMR_LANE0, 0x0018},
+ {RX_SDCAL1_INIT_TMR_LANE1, 0x0018},
+ {RX_SDCAL1_ITER_TMR_LANE0, 0x0078},
+ {RX_SDCAL1_ITER_TMR_LANE1, 0x0078},
+ {TX_RCVDET_ST_TMR_LANE0, 0x0960},
+ {TX_RCVDET_ST_TMR_LANE1, 0x0960},
+
+ {PHY_PLL_CFG_0803, 0x0000},
+ {CMN_PDIAG_PLL0_CLK_SEL_M0, 0x8600},
+ {XCVR_DIAG_HSCLK_SEL_LANE0, 0x0000},
+ {XCVR_DIAG_HSCLK_SEL_LANE1, 0x0000},
+ {XCVR_DIAG_HSCLK_DIV_LANE0, 0x0001},
+ {XCVR_DIAG_HSCLK_DIV_LANE1, 0x0001},
+ {XCVR_DIAG_PLLDRC_CTRL_LANE0, 0x0041},
+ {XCVR_DIAG_PLLDRC_CTRL_LANE1, 0x0041},
+ {CMN_PLL0_DSM_DIAG_M0, 0x0004},
+ {CMN_PLL1_DSM_DIAG_M0, 0x0004},
+ {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, 0x01a0},
+ {CMN_PLL0_FRACDIVL_M0, 0xaaab},
+ {CMN_PLL1_FRACDIVL_M0, 0xaaab},
+ {CMN_PLL0_FRACDIVH_M0, 0x0002},
+ {CMN_PLL1_FRACDIVH_M0, 0x0002},
+ {CMN_PLL0_HIGH_THR_M0, 0x0116},
+ {CMN_PLL1_HIGH_THR_M0, 0x0116},
+ {CMN_PDIAG_PLL0_CTRL_M0, 0x1002},
+ {CMN_PDIAG_PLL1_CTRL_M0, 0x1002},
+ {CMN_PLL0_SS_CTRL1_M0, 0x0001}, /* SSC enabled */
+ {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, 0x02f8},
+ {CMN_PLL1_VCOCAL_PLLCNT_START, 0x02f8},
+ {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, 0x0003},
+ {CMN_PLL1_LOCK_PLLCNT_THR, 0x0003},
+ {PHY_PIPE_USB3_GEN2_PRE_CFG0_0803, 0x0a0a},
+ {PHY_PIPE_USB3_GEN2_POST_CFG0_0803, 0x1000},
+ {PHY_PIPE_USB3_GEN2_POST_CFG1_0803, 0x0010},
+ {CMN_CDIAG_CDB_PWRI_OVRD, 0x8200},
+ {CMN_CDIAG_XCVRC_PWRI_OVRD, 0x8200},
+ {TX_PSC_A0_LANE0, 0x02ff},
+ {TX_PSC_A0_LANE1, 0x02ff},
+ {TX_PSC_A1_LANE0, 0x06af},
+ {TX_PSC_A1_LANE1, 0x06af},
+ {TX_PSC_A2_LANE0, 0x06ae},
+ {TX_PSC_A2_LANE1, 0x06ae},
+ {TX_PSC_A3_LANE0, 0x06ae},
+ {TX_PSC_A3_LANE1, 0x06ae},
+ {RX_PSC_A0_LANE0, 0x0d1d},
+ {RX_PSC_A0_LANE1, 0x0d1d},
+ {RX_PSC_A1_LANE0, 0x0d1d},
+ {RX_PSC_A1_LANE1, 0x0d1d},
+ {RX_PSC_A2_LANE0, 0x0d00},
+ {RX_PSC_A2_LANE1, 0x0d00},
+ {RX_PSC_A3_LANE0, 0x0500},
+ {RX_PSC_A3_LANE1, 0x0500},
+ {TX_TXCC_CTRL_LANE0, 0x2a82},
+ {TX_TXCC_CTRL_LANE1, 0x2a82},
+ {TX_TXCC_CPOST_MULT_01_LANE0, 0x0014},
+ {TX_TXCC_CPOST_MULT_01_LANE1, 0x0014},
+ {TX_TXCC_MGNFS_MULT_000_LANE0, 0x0002},
+ {TX_TXCC_MGNFS_MULT_000_LANE1, 0x0002},
+ {RX_SIGDET_HL_FILT_TMR_LANE0, 0x0013},
+ {RX_SIGDET_HL_FILT_TMR_LANE1, 0x0013},
+ {RX_REE_GCSM1_CTRL_LANE0, 0x0000},
+ {RX_REE_GCSM1_CTRL_LANE1, 0x0000},
+ {RX_REE_ATTEN_THR_LANE0, 0x0c02},
+ {RX_REE_ATTEN_THR_LANE1, 0x0c02},
+ {RX_REE_SMGM_CTRL1_LANE0, 0x0330},
+ {RX_REE_SMGM_CTRL1_LANE1, 0x0330},
+ {RX_REE_SMGM_CTRL2_LANE0, 0x0300},
+ {RX_REE_SMGM_CTRL2_LANE1, 0x0300},
+ {XCVR_DIAG_PSC_OVRD_LANE0, 0x0003},
+ {XCVR_DIAG_PSC_OVRD_LANE1, 0x0003},
+ {RX_DIAG_SIGDET_TUNE_LANE0, 0x1004},
+ {RX_DIAG_SIGDET_TUNE_LANE1, 0x1004},
+ {RX_DIAG_NQST_CTRL_LANE0, 0x00f9},
+ {RX_DIAG_NQST_CTRL_LANE1, 0x00f9},
+ {RX_DIAG_DFE_AMP_TUNE_2_LANE0, 0x0c01},
+ {RX_DIAG_DFE_AMP_TUNE_2_LANE1, 0x0c01},
+ {RX_DIAG_DFE_AMP_TUNE_3_LANE0, 0x0002},
+ {RX_DIAG_DFE_AMP_TUNE_3_LANE1, 0x0002},
+ {RX_DIAG_PI_CAP_LANE0, 0x0000},
+ {RX_DIAG_PI_CAP_LANE1, 0x0000},
+ {RX_DIAG_PI_RATE_LANE0, 0x0031},
+ {RX_DIAG_PI_RATE_LANE1, 0x0031},
+ {RX_CDRLF_CNFG_LANE0, 0x018c},
+ {RX_CDRLF_CNFG_LANE1, 0x018c},
+ {RX_CDRLF_CNFG3_LANE0, 0x0003},
+ {RX_CDRLF_CNFG3_LANE1, 0x0003}
+};
+
+struct cix_u3phy {
+ struct device *dev;
+ void __iomem *base;
+ struct regmap *phy_regmap;
+ struct reset_control *preset;
+ struct reset_control *reset;
+ struct clk *apb_clk;
+ struct clk *ref_clk;
+ struct mutex mutex; /* mutex to protect access to individual PHYs */
+ bool init;
+ int init_count;
+ const struct cix_u3phy_cfg *cfg;
+};
+
+static int sky1_u3phy_exit(struct cix_u3phy *u3phy)
+{
+ u3phy->init_count--;
+ if (u3phy->init && u3phy->init_count == 0) {
+ reset_control_assert(u3phy->reset);
+ reset_control_assert(u3phy->preset);
+ clk_disable_unprepare(u3phy->apb_clk);
+ clk_disable_unprepare(u3phy->ref_clk);
+ u3phy->init = false;
+ }
+ return 0;
+}
+
+static int sky1_u3phy_init(struct cix_u3phy *u3phy)
+{
+ int ret;
+
+ if (!u3phy->init) {
+ /* usb rcsu reset is default deassert */
+ reset_control_assert(u3phy->reset);
+ reset_control_assert(u3phy->preset);
+ ret = clk_prepare_enable(u3phy->apb_clk);
+ if (ret) {
+ dev_err(u3phy->dev, "Failed to prepare_enable u3phy apb clock\n");
+ goto assert_reset_preset;
+ }
+ ret = clk_prepare_enable(u3phy->ref_clk);
+ if (ret) {
+ dev_err(u3phy->dev, "Failed to prepare_enable u3phy ref clock\n");
+ goto disable_apb_clk;
+ }
+ reset_control_deassert(u3phy->preset);
+ ret = regmap_multi_reg_write(u3phy->phy_regmap, sky1_u3phy_conf,
+ ARRAY_SIZE(sky1_u3phy_conf));
+ if (ret) {
+ dev_err(u3phy->dev, "Failed to write the reg sequence\n");
+ goto disable_ref_clk;
+ }
+ u3phy->init = true;
+ reset_control_deassert(u3phy->reset);
+ }
+ u3phy->init_count++;
+ return 0;
+
+disable_ref_clk:
+ clk_disable_unprepare(u3phy->ref_clk);
+disable_apb_clk:
+ clk_disable_unprepare(u3phy->apb_clk);
+assert_reset_preset:
+ reset_control_assert(u3phy->preset);
+ reset_control_assert(u3phy->reset);
+ u3phy->init = false;
+ return ret;
+}
+
+static const struct cix_u3phy_cfg sky1_u3phy_cfg = {
+ .u3phy_init = sky1_u3phy_init,
+ .u3phy_exit = sky1_u3phy_exit
+};
+
+static int u3phy_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct cix_u3phy *u3phy = context;
+ u32 offset = reg << 2;
+
+ writel(val, u3phy->base + offset);
+ return 0;
+}
+
+static int u3phy_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct cix_u3phy *u3phy = context;
+ u32 offset = reg << 2;
+
+ *val = readl(u3phy->base + offset);
+ return 0;
+}
+
+static const struct regmap_config cix_u3phy_regmap_cfg = {
+ .reg_bits = 32,
+ .reg_stride = 1,
+ .val_bits = 16,
+ .fast_io = true,
+ .reg_write = u3phy_regmap_write,
+ .reg_read = u3phy_regmap_read,
+};
+
+static int cix_usb3_phy_power_on(struct cix_u3phy *u3phy)
+{
+ int ret;
+ const struct cix_u3phy_cfg *phy_cfgs = u3phy->cfg;
+
+ if (phy_cfgs->u3phy_init) {
+ ret = phy_cfgs->u3phy_init(u3phy);
+ if (ret) {
+ dev_err(u3phy->dev, "failed to init udphy\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int cix_usb3_phy_power_off(struct cix_u3phy *u3phy)
+{
+ int ret;
+ const struct cix_u3phy_cfg *phy_cfgs = u3phy->cfg;
+
+ if (phy_cfgs->u3phy_exit) {
+ ret = phy_cfgs->u3phy_exit(u3phy);
+ if (ret) {
+ dev_err(u3phy->dev, "failed to exit udphy\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int cix_u3phy_init(struct phy *phy)
+{
+ struct cix_u3phy *u3phy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ mutex_lock(&u3phy->mutex);
+ ret = cix_usb3_phy_power_on(u3phy);
+ mutex_unlock(&u3phy->mutex);
+ return ret;
+}
+
+static int cix_u3phy_exit(struct phy *phy)
+{
+ struct cix_u3phy *u3phy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ mutex_lock(&u3phy->mutex);
+ ret = cix_usb3_phy_power_off(u3phy);
+ mutex_unlock(&u3phy->mutex);
+ return ret;
+}
+
+static const struct phy_ops cix_u3phy_ops = {
+ .init = cix_u3phy_init,
+ .exit = cix_u3phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int cix_u3phy_probe(struct platform_device *pdev)
+{
+ struct cix_u3phy *u3phy;
+ struct device *dev = &pdev->dev;
+ struct phy_provider *phy_provider;
+ struct device_node *np = dev->of_node;
+ struct device_node *child_np;
+ struct phy *phy;
+ int ret;
+
+ u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
+ if (!u3phy)
+ return -ENOMEM;
+ dev_set_drvdata(dev, u3phy);
+ u3phy->dev = dev;
+ u3phy->cfg = device_get_match_data(dev);
+ if (!u3phy->cfg) {
+ dev_err(dev, "no OF data can be matched with %p node\n", np);
+ return -EINVAL;
+ }
+ u3phy->reset = devm_reset_control_get_exclusive(dev, "core");
+ if (IS_ERR(u3phy->reset)) {
+ dev_err(dev, "failed to get reset\n");
+ return PTR_ERR(u3phy->reset);
+ }
+ u3phy->preset = devm_reset_control_get_exclusive(dev, "apb");
+ if (IS_ERR(u3phy->preset)) {
+ dev_err(dev, "failed to get preset\n");
+ return PTR_ERR(u3phy->preset);
+ }
+ u3phy->apb_clk = devm_clk_get(dev, "apb");
+ if (IS_ERR(u3phy->apb_clk)) {
+ dev_err(dev, "phy apb clock not found\n");
+ return PTR_ERR(u3phy->apb_clk);
+ }
+ u3phy->ref_clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(u3phy->ref_clk)) {
+ dev_err(dev, "phy ref clock not found\n");
+ return PTR_ERR(u3phy->ref_clk);
+ }
+ u3phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(u3phy->base))
+ return PTR_ERR(u3phy->base);
+ u3phy->phy_regmap = devm_regmap_init(dev, NULL, u3phy, &cix_u3phy_regmap_cfg);
+ if (IS_ERR(u3phy->phy_regmap)) {
+ dev_err(dev, "failed to remap phy register\n");
+ return PTR_ERR(u3phy->phy_regmap);
+ }
+ mutex_init(&u3phy->mutex);
+ device_for_each_child_node_scoped(dev, child_fn) {
+ child_np = to_of_node(child_fn);
+ if (!strncmp(fwnode_get_name(child_fn), "usb-port", 8) ||
+ !strncmp(fwnode_get_name(child_fn), "USB", 3))
+ phy = devm_phy_create(dev, child_np, &cix_u3phy_ops);
+ else
+ continue;
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ dev_err(dev, "failed to create phy: %s\n",
+ fwnode_get_name(child_fn));
+ return ret;
+ }
+ phy_set_drvdata(phy, u3phy);
+ }
+ 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;
+ }
+ reset_control_assert(u3phy->reset);
+ reset_control_assert(u3phy->preset);
+ return 0;
+}
+
+static const struct of_device_id cix_u3phy_dt_match[] = {
+ {
+ .compatible = "cix,sky1-usb3-phy",
+ .data = &sky1_u3phy_cfg
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cix_u3phy_dt_match);
+
+static struct platform_driver cix_usb3_phy_driver = {
+ .probe = cix_u3phy_probe,
+ .driver = {
+ .name = "cix-usb3-phy",
+ .of_match_table = cix_u3phy_dt_match,
+ },
+};
+
+module_platform_driver(cix_usb3_phy_driver);
+
+MODULE_AUTHOR("Hongliang Yang <hongliang.yang@cixtech.com>");
+MODULE_DESCRIPTION("Cix USB3 Only 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..6ec7eb5c81aa
--- /dev/null
+++ b/drivers/phy/cix/phy-cix-usbdp.h
@@ -0,0 +1,310 @@
+/* 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_LANE1 0x4301
+#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_LANE0 0x8001
+#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_LANE1 0x424d
+#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_LANE0 0x8090
+#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_LANE0 0x8149
+#define RX_REE_ATTEN_THR_LANE1 0x8349
+#define RX_REE_ATTEN_THR_LANE2 0x8549
+
+#define RX_REE_SMGM_CTRL1_LANE0 0x8177
+#define RX_REE_SMGM_CTRL1_LANE1 0x8377
+#define RX_REE_SMGM_CTRL1_LANE2 0x8577
+
+#define RX_REE_SMGM_CTRL2_LANE0 0x8178
+#define RX_REE_SMGM_CTRL2_LANE1 0x8378
+#define RX_REE_SMGM_CTRL2_LANE2 0x8578
+
+#define XCVR_DIAG_PSC_OVRD_LANE0 0x40eb
+#define XCVR_DIAG_PSC_OVRD_LANE1 0x42eb
+#define XCVR_DIAG_PSC_OVRD_LANE2 0x44eb
+
+#define RX_DIAG_SIGDET_TUNE_LANE0 0x81e8
+#define RX_DIAG_SIGDET_TUNE_LANE1 0x83e8
+#define RX_DIAG_SIGDET_TUNE_LANE2 0x85e8
+
+#define RX_DIAG_NQST_CTRL_LANE0 0x81e5
+#define RX_DIAG_NQST_CTRL_LANE1 0x83e5
+#define RX_DIAG_NQST_CTRL_LANE2 0x85e5
+
+#define RX_DIAG_DFE_AMP_TUNE_2_LANE0 0x81e2
+#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_LANE0 0x81e3
+#define RX_DIAG_DFE_AMP_TUNE_3_LANE1 0x83e3
+#define RX_DIAG_DFE_AMP_TUNE_3_LANE2 0x85e3
+
+#define RX_DIAG_PI_CAP_LANE0 0x81f5
+#define RX_DIAG_PI_CAP_LANE1 0x83f5
+#define RX_DIAG_PI_CAP_LANE2 0x85f5
+
+#define RX_DIAG_PI_RATE_LANE0 0x81f4
+#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_LANE0 0x8080
+#define RX_CDRLF_CNFG_LANE1 0x8280
+#define RX_CDRLF_CNFG_LANE2 0x8480
+
+#define RX_CDRLF_CNFG3_LANE0 0x8082
+#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
+
+/* Bits */
+
+#define PHY_PLL_CFG_0803 0xC00E
+/* 0803 phy register */
+#define PHY_PIPE_USB3_GEN2_PRE_CFG0_0803 0xc01c
+#define PHY_PIPE_USB3_GEN2_POST_CFG0_0803 0xc01e
+#define PHY_PIPE_USB3_GEN2_POST_CFG1_0803 0xc01f
+
+/* gop status address */
+#define GOP_STATUS_ADDRESS 0x83E05000
+#define GOP_STATUS_SIZE 0x04
+
+struct gop_status {
+ unsigned char phy_status[4];
+};
+
+/*
+ * 0: usb
+ * 1: 2 lane usb+ 2 lane dp
+ * 2: usb device
+ * 3: usb2.0 + 4 lane dp
+ */
+enum phy_role {
+ USB_ROLE_NONE,
+ USB_ROLE_HOST,
+ USB_ROLE_DEVICE,
+ USB_ROLE_HOST_20,
+};
+
+#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 4/5] usb: cdns3: Add CIX Sky1 glue driver
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (7 preceding siblings ...)
2026-09-02 3:21 ` [PATCH v2 3/5] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
@ 2026-09-02 3:21 ` Hongliang Yang
2026-09-02 3:32 ` sashiko-bot
2026-09-02 3:21 ` [PATCH v2 5/5] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
9 siblings, 1 reply; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:21 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add a glue layer driver for the Cadence USBSSP controller integrated
in the CIX Sky1 SoC. The driver manages the controller clocks,
resets, and the mode strap controls located in the Sky1 S5 system
controller.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
drivers/usb/cdns3/Kconfig | 13 +
drivers/usb/cdns3/Makefile | 1 +
drivers/usb/cdns3/cdnsp-sky1.c | 673 +++++++++++++++++++++++++++++++++
drivers/usb/cdns3/cdnsp-sky1.h | 127 +++++++
4 files changed, 814 insertions(+)
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.c
create mode 100644 drivers/usb/cdns3/cdnsp-sky1.h
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index 39ad23d1ada8..03d8e170a98a 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -77,6 +77,19 @@ config USB_CDNSP_PCI
If you choose to build this driver as module it will
be dynamically linked and module will be called cdnsp-pci.ko
+config USB_CDNSP_SKY1
+ tristate "Cadence USB3 support on CIX Sky1 SoC platforms"
+ depends on USB_CDNS3
+ depends on ARCH_CIX || COMPILE_TEST
+ default USB_CDNS3
+ help
+ Glue driver for the Cadence USB dual-role controllers on CIX Sky1
+ (device tree compatible cix,sky1-usb3). It enables clocks and resets
+ from the SoC, then uses the shared cdns3 platform core (cdns.ko).
+
+ If built as a module, the module is named cdnsp-sky1.ko and must be
+ loaded after the cdns core module when both are loadable modules.
+
config USB_CDNS3_TI
tristate "Cadence USB3 support on TI platforms"
depends on ARCH_K3 || COMPILE_TEST
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index b2e4ba6a49a3..ab813aaf9940 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_USB_CDNSP_PCI) += cdnsp-pci.o
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o
+obj-$(CONFIG_USB_CDNSP_SKY1) += cdnsp-sky1.o
diff --git a/drivers/usb/cdns3/cdnsp-sky1.c b/drivers/usb/cdns3/cdnsp-sky1.c
new file mode 100644
index 000000000000..447c683b726c
--- /dev/null
+++ b/drivers/usb/cdns3/cdnsp-sky1.c
@@ -0,0 +1,673 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cdnsp-sky1.c - CIX Sky1 glue for Cadence USBSSP DRD controller
+ *
+ * Copyright (C) 2026 CIX Technology Group Co., Ltd.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "cdnsp-sky1.h"
+#include "core.h"
+
+static const char *cix_usb_clk_names[CIX_USB_CLK_NUM] = {
+ "sof",
+ "axi",
+ "lpm",
+ "apb",
+};
+
+struct cdnsp_sky1_strap_signal {
+ unsigned int offset, bit;
+};
+
+static const struct cdnsp_sky1_strap_signal strap_signals[SKY1_USB_S5_NUM] = {
+ /* usb config in s5 domain */
+ [U3_TYPEC_DRD_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_DRD_MODE_STRAP_BIT },
+ [U3_TYPEC_HOST0_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_HOST0_MODE_STRAP_BIT },
+ [U3_TYPEC_HOST1_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_HOST1_MODE_STRAP_BIT },
+ [U3_TYPEC_HOST2_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEC_HOST2_MODE_STRAP_BIT },
+ [U3_TYPEA_CTRL0_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEA_CTRL0_MODE_STRAP_BIT },
+ [U3_TYPEA_CTRL1_ID] = { USB_MODE_STRAP_S5_DOMAIN, U3_TYPEA_CTRL1_MODE_STRAP_BIT},
+ [U2_HOST0_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST0_MODE_STRAP_BIT },
+ [U2_HOST1_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST1_MODE_STRAP_BIT },
+ [U2_HOST2_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST2_MODE_STRAP_BIT },
+ [U2_HOST3_ID] = { USB_MODE_STRAP_S5_DOMAIN, U2_HOST3_MODE_STRAP_BIT },
+};
+
+static int cdnsp_sky1_set_mode_by_id(struct device *dev, int mode)
+{
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ return regmap_update_bits(data->usb_syscon,
+ strap_signals[data->id].offset,
+ GENMASK(strap_signals[data->id].bit + 1,
+ strap_signals[data->id].bit),
+ mode << strap_signals[data->id].bit);
+}
+
+/**
+ * cdnsp_sky1_clk_enable_all() - enable all clocks for usb controller
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static int cdnsp_sky1_clk_enable_all(struct device *dev)
+{
+ int i, ret = 0;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_NUM; i++) {
+ cix_usb_clks[i] = devm_clk_get(dev, cix_usb_clk_names[i]);
+ if (IS_ERR(cix_usb_clks[i])) {
+ ret = dev_err_probe(dev, PTR_ERR(cix_usb_clks[i]),
+ "could not get %s clock\n",
+ cix_usb_clk_names[i]);
+ goto err_usb_clks;
+ }
+ ret = clk_prepare_enable(cix_usb_clks[i]);
+ if (ret) {
+ dev_err(dev, "%s enable failed:%d\n", cix_usb_clk_names[i], ret);
+ goto err_usb_clks;
+ }
+ }
+ dev_dbg(dev, "enable sky1 USB clock done\n");
+ return ret;
+
+err_usb_clks:
+ cix_usb_clks[i] = NULL;
+ while (--i >= 0) {
+ clk_disable_unprepare(cix_usb_clks[i]);
+ cix_usb_clks[i] = NULL;
+ }
+ return ret;
+};
+
+/**
+ * cdnsp_sky1_clk_disable_all() - disable all clocks for usb controller
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static void cdnsp_sky1_clk_disable_all(struct device *dev)
+{
+ int i;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_NUM; i++)
+ clk_disable_unprepare(cix_usb_clks[i]);
+};
+
+/**
+ * cdnsp_sky1_clk_enable_resume() - enable the clocks that are turned
+ * off while suspend
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static int cdnsp_sky1_clk_enable_resume(struct device *dev)
+{
+ int i, ret;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_OFF_NUM; i++) {
+ ret = clk_prepare_enable(cix_usb_clks[i]);
+ if (ret) {
+ dev_err(dev, "failed to enable clock %s: %d\n",
+ cix_usb_clk_names[i], ret);
+ goto err_usb_clks;
+ }
+ }
+ return 0;
+
+err_usb_clks:
+ cix_usb_clks[i] = NULL;
+ while (--i >= 0) {
+ clk_disable_unprepare(cix_usb_clks[i]);
+ cix_usb_clks[i] = NULL;
+ }
+ return ret;
+};
+
+/**
+ * cdnsp_sky1_clk_disable_suspend() - disable the clocks which are not
+ * needed when suspend
+ * @dev: Pointer to the device of platform_device
+ *
+ */
+
+static void cdnsp_sky1_clk_disable_suspend(struct device *dev)
+{
+ int i;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct clk **cix_usb_clks = data->cix_usb_clks;
+
+ for (i = 0; i < CIX_USB_CLK_OFF_NUM; i++)
+ clk_disable_unprepare(cix_usb_clks[i]);
+};
+
+static int sky1_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us)
+{
+ u32 result;
+ int ret;
+
+ ret = readl_poll_timeout(ptr, result,
+ (result & mask) == done || result == U32_MAX,
+ 10, timeout_us);
+ if (result == U32_MAX) /* card removed */
+ return -ENODEV;
+ return ret;
+}
+
+static int cdns_sky1_platform_suspend(struct device *dev,
+ bool suspend, bool wakeup)
+{
+ struct cdns *cdns = dev_get_drvdata(dev);
+ struct platform_device *xhci_dev = cdns->host_dev;
+ struct usb_hcd *hcd;
+ struct device *parent = cdns->dev->parent;
+ struct cdnsp_sky1 *data = dev_get_drvdata(parent);
+ u32 value;
+ int ret = 0;
+ int count = 3;
+
+ data->wakeup = wakeup;
+
+ if (cdns->role != USB_ROLE_HOST)
+ return 0;
+
+ hcd = dev_get_drvdata(&xhci_dev->dev);
+ if (!hcd) {
+ dev_dbg(dev, "host controller have not registered\n");
+ return 0;
+ }
+
+ if (suspend) {
+ while (count--) {
+ value = readl(hcd->regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D3 | PS_PME_En;
+ writel(value, hcd->regs + XECP_PM_PMCSR);
+ /* After controller enters D3, disable AXI and SOF
+ * until AXI valid flag changes to 0.
+ */
+ if (sky1_handshake(data->ctst_base, AXI_CLOCK_VALID, 0, 100ULL * 1000))
+ dev_dbg(dev, "enter D3 failed,register value:%x\n",
+ readl(data->ctst_base));
+ else
+ break;
+ }
+ if (count < 0) {
+ dev_err(dev, "enter D3 failed after retries, register value:%x\n",
+ readl(data->ctst_base));
+ }
+ } else {
+ while (count--) {
+ value = readl(hcd->regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D0;
+ value &= ~PS_PME_En;
+ writel(value, hcd->regs + XECP_PM_PMCSR);
+ /* Wait power state back to D0 */
+ if (sky1_handshake(hcd->regs + XECP_PM_PMCSR, PS_MASK, 0, 100ULL * 1000)) {
+ dev_dbg(dev, "exit D3 timeout, power state=0x%lx\n",
+ readl(hcd->regs + XECP_PM_PMCSR) & PS_MASK);
+ } else {
+ break;
+ }
+ }
+ if (count < 0) {
+ dev_err(dev, "exit D3 timeout after retries, power state=0x%lx\n",
+ readl(hcd->regs + XECP_PM_PMCSR) & PS_MASK);
+ }
+ }
+
+ return ret;
+}
+
+static void cdnsp_sky1_configure_controller(struct cdnsp_sky1 *data)
+{
+ int clk;
+ int v0, v1, v2;
+ u32 val = 0;
+
+ if (data->u3_disable) {
+ dev_dbg(data->dev, "disable u3 port\n");
+ writel(D_XEC_CFG_3XPORT_MODE_VALUE, data->device_base
+ + D_XEC_CFG_3XPORT_MODE);
+ }
+ writel(AXI_HALT, data->device_base + D_XEC_AXI_CAP);
+ writel(AXI_HALT, data->xhci_base + D_XEC_AXI_CAP);
+ writel(data->axi_bmax_value, data->device_base + D_XEC_AXI_CTRL0);
+ writel(data->axi_bmax_value, data->xhci_base + D_XEC_AXI_CTRL0);
+ writel((~(u32)(AXI_HALT)), data->device_base + D_XEC_AXI_CAP);
+ writel((~(u32)(AXI_HALT)), data->xhci_base + D_XEC_AXI_CAP);
+ clk = data->sof_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 1),
+ data->device_base + D_XEC_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 1),
+ data->device_base + D_XEC_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 1),
+ data->device_base + D_XEC_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 1), data->device_base
+ + D_XEC_PRE_REG_100MS);
+ dev_dbg(data->dev, "readl:%x, %x ,%x, %x, %x, %x, %x, %x\n",
+ readl(data->device_base + D_XEC_PRE_REG_250NS),
+ readl(data->device_base + D_XEC_PRE_REG_1US),
+ readl(data->device_base + D_XEC_PRE_REG_10US),
+ readl(data->device_base + D_XEC_PRE_REG_100US),
+ readl(data->device_base + D_XEC_PRE_REG_125US),
+ readl(data->device_base + D_XEC_PRE_REG_1MS),
+ readl(data->device_base + D_XEC_PRE_REG_10MS),
+ readl(data->device_base + D_XEC_PRE_REG_100MS));
+ clk = data->lpm_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 1),
+ data->device_base + D_XEC_LPM_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 1), data->device_base
+ + D_XEC_LPM_PRE_REG_100MS);
+ v0 = readl(data->xhci_base + XEC_USBSSP_CHICKEN_BITS_3);
+ v0 &= ~(CFG_APB_TIMEOUT_PSLVERR_EN | CFG_APB_PSLVERR_EN);
+ writel(v0, data->xhci_base + XEC_USBSSP_CHICKEN_BITS_3);
+ if (data->u3_disable) {
+ dev_dbg(data->dev, "disable u3 port\n");
+ writel(XEC_CFG_3XPORT_MODE_VALUE, data->xhci_base
+ + XEC_CFG_3XPORT_MODE);
+ } else if (data->ssp_disable) {
+ dev_dbg(data->dev, "disable ssp\n");
+ v0 = readl(data->xhci_base + XEC_CFG_3XPORT_MODE);
+ writel(v0 & CFG_3XPORT_MODE_DIS_SSP, data->xhci_base
+ + XEC_CFG_3XPORT_MODE);
+ }
+ clk = data->sof_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 0),
+ data->xhci_base + XEC_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 0), data->xhci_base
+ + XEC_PRE_REG_100MS);
+ clk = data->lpm_clk_freq;
+ v0 = 25 * clk / 100000000;
+ v1 = clk / 10000;
+ v2 = clk / 10;
+ writel(((v0 > 1) ? v0 - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_250NS);
+ writel((unsigned int)((v1 / 100 > 1) > 0 ? (v1 / 100) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_1US);
+ writel((unsigned int)((v1 / 10 > 1) > 0 ? (v1 / 10) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_10US);
+ writel(((v1) > 1 ? v1 - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_100US);
+ writel((unsigned int)((125 * clk / 1000000) > 1 ? (125 * clk / 1000000) : 0),
+ data->xhci_base + XEC_LPM_PRE_REG_125US);
+ writel(((v2 / 100 > 1) ? (v2 / 100) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_1MS);
+ writel(((v2 / 10 > 1) ? (v2 / 10) - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_10MS);
+ writel((v2 > 1 ? v2 - 1 : 0), data->xhci_base
+ + XEC_LPM_PRE_REG_100MS);
+ val = readl(data->xhci_base + XEC_USBSSP_CLK_GATING_CTRL);
+ val |= HOST20_ACLK_GATING_DISABLE | HOST20_UTMI_GATING_DISABLE;
+ writel(val, data->xhci_base + XEC_USBSSP_CLK_GATING_CTRL);
+}
+
+static int cdnsp_sky1_drd_init(struct cdnsp_sky1 *data)
+{
+ int ret = 0;
+
+ reset_control_assert(data->reset);
+ reset_control_assert(data->preset);
+ cdnsp_sky1_clk_disable_all(data->dev);
+ ret = cdnsp_sky1_clk_enable_all(data->dev);
+ if (ret)
+ return ret;
+ writel(CIX_USB_AXI_WR_CACHE_VALUE, data->axi_base);
+ cdnsp_sky1_set_mode_by_id(data->dev, MODE_STRAP_OTG);
+ reset_control_deassert(data->preset);
+ cdnsp_sky1_configure_controller(data);
+ reset_control_deassert(data->reset);
+ return ret;
+}
+
+static void *sky1_of_get_addr_by_name(struct device_node *parent, char *name)
+{
+ struct device_node *node;
+ int index;
+
+ node = of_get_next_child(parent, NULL);
+ if (node) {
+ index = of_property_match_string(node, "reg-names", name);
+ if (index >= 0)
+ return of_iomap(node, index);
+ }
+ return NULL;
+}
+
+static void *sky1_get_addr_by_name(struct device *dev, char *name)
+{
+ return sky1_of_get_addr_by_name(dev->of_node, name);
+}
+
+static void sky1_put_addr(void __iomem *regs)
+{
+ if (regs)
+ iounmap(regs);
+}
+
+static struct of_dev_auxdata cdns_sky1_auxdata[] = {
+ {
+ .compatible = "cdns,usb3",
+ },
+ {},
+};
+
+static int cdnsp_sky1_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct cdnsp_sky1 *data;
+ int ret = 0;
+ struct cdns3_platform_data *cdns_sky1_pdata;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->axi_base = devm_platform_ioremap_resource_byname(pdev, "axi");
+ if (IS_ERR(data->axi_base)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->axi_base);
+ }
+ data->ctst_base = devm_platform_ioremap_resource_byname(pdev, "controller");
+ if (IS_ERR(data->ctst_base)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->ctst_base);
+ }
+ data->reset = devm_reset_control_get_exclusive(&pdev->dev, "core");
+ if (IS_ERR(data->reset)) {
+ ret = PTR_ERR(data->reset);
+ dev_err(dev, "get reset error:%d\n", ret);
+ return ret;
+ }
+ data->preset = devm_reset_control_get_exclusive(&pdev->dev, "apb");
+ if (IS_ERR(data->preset)) {
+ ret = PTR_ERR(data->preset);
+ dev_err(dev, "get reset error:%d\n", ret);
+ return ret;
+ }
+ platform_set_drvdata(pdev, data);
+ data->dev = dev;
+ ret = of_alias_get_id(dev->of_node, "usb");
+ if (ret == -ENODEV) {
+ if (device_property_read_u32(dev, "id", &ret))
+ ret = -ENODEV;
+ }
+ if (ret < 0 || ret >= SKY1_USB_S5_NUM) {
+ dev_err(dev, "get alias failed.\n");
+ return ret;
+ }
+ data->id = ret;
+ data->usb_syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "cix,usb-syscon");
+ if (IS_ERR(data->usb_syscon)) {
+ dev_err(dev, "Unable to get cix,usb-syscon regmap");
+ return PTR_ERR(data->usb_syscon);
+ }
+ data->u3_disable = device_property_read_bool(dev, "u3-port-disable");
+ data->ssp_disable = device_property_read_bool(dev, "ssp-disable");
+ data->sof_clk_freq = CIX_USB_CLK_8M;
+ data->lpm_clk_freq = CIX_USB_CLK_32K;
+ data->axi_bmax_value = AXI_BMAX_VALUE_DEFAULT;
+ data->xhci_base = sky1_get_addr_by_name(dev, "xhci");
+ if (!data->xhci_base)
+ return -ENODEV;
+ data->device_base = sky1_get_addr_by_name(dev, "dev");
+ if (!data->device_base)
+ return -ENODEV;
+ ret = cdnsp_sky1_drd_init(data);
+ if (ret == -ETIMEDOUT)
+ return -EPROBE_DEFER;
+ if (ret)
+ return ret;
+ data->oc_gpio = devm_gpiod_get_optional(data->dev, "oc", GPIOD_IN);
+ if (IS_ERR(data->oc_gpio)) {
+ dev_err(data->dev, "can not get oc_gpio\n");
+ ret = PTR_ERR(data->oc_gpio);
+ return ret;
+ }
+ if (data->oc_gpio) {
+ ret = gpiod_direction_input(data->oc_gpio);
+ if (ret < 0)
+ dev_err(data->dev, "set oc_gpio input failed:%d\n", ret);
+ }
+ /* release by platform_device_release */
+ cdns_sky1_pdata = kzalloc(sizeof(*cdns_sky1_pdata), GFP_KERNEL);
+ if (!cdns_sky1_pdata)
+ return -ENOMEM;
+ cdns_sky1_pdata->platform_suspend = cdns_sky1_platform_suspend;
+ cdns_sky1_pdata->quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW;
+ cdns_sky1_auxdata->platform_data = cdns_sky1_pdata;
+ ret = of_platform_populate(node, NULL, cdns_sky1_auxdata, dev);
+ if (ret) {
+ dev_err(dev, "failed to create children: %d\n", ret);
+ goto err;
+ }
+ device_set_wakeup_capable(dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ return 0;
+err:
+ kfree(cdns_sky1_pdata);
+ return ret;
+}
+
+static void cdnsp_sky1_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+ of_platform_depopulate(dev);
+ sky1_put_addr(data->xhci_base);
+ sky1_put_addr(data->device_base);
+ reset_control_deassert(data->reset);
+ reset_control_deassert(data->preset);
+ cdnsp_sky1_clk_disable_all(dev);
+ platform_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+/* Because the wake-up interrupt and host interrupt are the same interrupt, closing the axi
+ * and sof clock will result in the inability to generate port status change interrupt.
+ */
+
+static int cdnsp_sky1_system_suspend(struct device *dev)
+{
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ if (!data->wakeup) {
+ reset_control_assert(data->reset);
+ reset_control_assert(data->preset);
+ }
+ cdnsp_sky1_clk_disable_suspend(dev);
+ return 0;
+}
+
+static int cdnsp_sky1_system_resume(struct device *dev)
+{
+ int ret = 0;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+
+ ret = cdnsp_sky1_clk_enable_resume(dev);
+ if (ret)
+ return ret;
+ if (!data->wakeup) {
+ writel(CIX_USB_AXI_WR_CACHE_VALUE, data->axi_base);
+ cdnsp_sky1_set_mode_by_id(data->dev, MODE_STRAP_OTG);
+ reset_control_deassert(data->preset);
+ cdnsp_sky1_configure_controller(data);
+ reset_control_deassert(data->reset);
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops cdnsp_sky1_pm_ops = {
+ .suspend = cdnsp_sky1_system_suspend,
+ .resume = cdnsp_sky1_system_resume,
+};
+#endif /* CONFIG_PM */
+
+static const struct of_device_id cdns_sky1_of_match[] = {
+ { .compatible = "cix,sky1-usbssp", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cdns_sky1_of_match);
+
+static int cdnsp_sky1_find_cdns(struct device *dev, void *data)
+{
+ struct cdns **cdns_ptr = data;
+
+ if (dev->of_node && of_device_is_compatible(dev->of_node, "cdns,usb3")) {
+ *cdns_ptr = dev_get_drvdata(dev);
+ return 1;
+ }
+ return 0;
+}
+
+static int cdnsp_sky1_find_gadget_match(struct device *dev, void *data)
+{
+ struct device **gadget_dev = data;
+ const char *name = dev_name(dev);
+ static const char gadget_prefix[] = "gadget.";
+
+ /*
+ * The gadget device is registered on the gadget bus with name
+ * "gadget.%d" (see usb_add_gadget_udc -> dev_set_name).
+ * It sits on the gadget bus and has the function driver bound to it.
+ * Verify the device is on the gadget bus by checking the bus name.
+ * This prevents matching devices that happen to have "gadget." prefix
+ * in their name but are not real gadget devices, and also avoids
+ * NULL pointer dereference when device bus is being removed.
+ */
+ if (name && dev->bus && !strcmp(dev->bus->name, "gadget") &&
+ !strncmp(name, gadget_prefix, sizeof(gadget_prefix) - 1)) {
+ *gadget_dev = dev;
+ return 1;
+ }
+ return 0;
+}
+
+static void cdnsp_sky1_shutdown(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cdnsp_sky1 *data = dev_get_drvdata(dev);
+ struct cdns *cdns = NULL;
+ struct device *gadget_dev = NULL;
+
+ if (!device_may_wakeup(dev)) {
+ /*
+ * Find the cdns3 child device, then find its gadget device
+ * and release the function driver before disabling clocks.
+ * This ensures all register accesses in gadget_unbind_driver
+ * complete before clocks are turned off.
+ */
+ device_for_each_child(dev, &cdns, cdnsp_sky1_find_cdns);
+ if (cdns)
+ device_for_each_child(cdns->dev, &gadget_dev,
+ cdnsp_sky1_find_gadget_match);
+ if (gadget_dev)
+ device_release_driver(gadget_dev);
+ if (cdns && cdns->host_dev) {
+ struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev);
+
+ if (hcd && hcd->irq > 0) {
+ disable_irq(hcd->irq);
+ synchronize_irq(hcd->irq);
+ /*
+ * Clear HCD_FLAG_HW_ACCESSIBLE before disable_irq.
+ * This prevents usb_hcd_irq from calling xhci_irq
+ * (which reads USBSTS) after clocks are disabled.
+ */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ }
+ }
+ reset_control_assert(data->reset);
+ reset_control_assert(data->preset);
+ cdnsp_sky1_clk_disable_all(dev);
+ }
+}
+
+static struct platform_driver cdnsp_sky1_driver = {
+ .probe = cdnsp_sky1_probe,
+ .remove = cdnsp_sky1_remove,
+ .shutdown = cdnsp_sky1_shutdown,
+ .driver = {
+ .name = "cdnsp-sky1",
+ .of_match_table = cdns_sky1_of_match,
+ .pm = pm_ptr(&cdnsp_sky1_pm_ops),
+ },
+};
+
+module_platform_driver(cdnsp_sky1_driver);
+
+MODULE_ALIAS("platform:cdnsp-sky1");
+MODULE_DESCRIPTION("CIX Sky1 Cadence USBSSP DRD glue driver");
+MODULE_AUTHOR("Hongliang Yang <hongliang.yang@cixtech.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/cdns3/cdnsp-sky1.h b/drivers/usb/cdns3/cdnsp-sky1.h
new file mode 100644
index 000000000000..49c3d5eab174
--- /dev/null
+++ b/drivers/usb/cdns3/cdnsp-sky1.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _CDNSP_SKY1_H
+#define _CDNSP_SKY1_H
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/reset.h>
+
+#define USB_MODE_STRAP_S5_DOMAIN 0x424
+
+#define MODE_STRAP_OTG 0
+
+#define U3_TYPEC_DRD_ID 0
+#define U3_TYPEC_HOST0_ID 1
+#define U3_TYPEC_HOST1_ID 2
+#define U3_TYPEC_HOST2_ID 3
+#define U3_TYPEA_CTRL0_ID 4
+#define U3_TYPEA_CTRL1_ID 5
+#define U2_HOST0_ID 6
+#define U2_HOST1_ID 7
+#define U2_HOST2_ID 8
+#define U2_HOST3_ID 9
+#define SKY1_USB_S5_NUM 10
+
+#define U3_TYPEC_DRD_MODE_STRAP_BIT 12
+#define U3_TYPEC_HOST0_MODE_STRAP_BIT 14
+#define U3_TYPEC_HOST1_MODE_STRAP_BIT 16
+#define U3_TYPEC_HOST2_MODE_STRAP_BIT 18
+#define U3_TYPEA_CTRL0_MODE_STRAP_BIT 8
+#define U3_TYPEA_CTRL1_MODE_STRAP_BIT 10
+#define U2_HOST0_MODE_STRAP_BIT 0
+#define U2_HOST1_MODE_STRAP_BIT 2
+#define U2_HOST2_MODE_STRAP_BIT 4
+#define U2_HOST3_MODE_STRAP_BIT 6
+
+#define AXI_HALT BIT(31)
+#define AXI_BMAX_VALUE_DEFAULT 0x7
+
+#define D_XEC_CFG_3XPORT_MODE 0x2040
+#define D_XEC_AXI_CAP 0x2174
+#define D_XEC_AXI_CTRL0 0x217C
+#define D_XEC_PRE_REG_250NS 0x21E8
+#define D_XEC_PRE_REG_1US 0x21EC
+#define D_XEC_PRE_REG_10US 0x21F0
+#define D_XEC_PRE_REG_100US 0x21F4
+#define D_XEC_PRE_REG_125US 0x21F8
+#define D_XEC_PRE_REG_1MS 0x21FC
+#define D_XEC_PRE_REG_10MS 0x2200
+#define D_XEC_PRE_REG_100MS 0x2204
+#define D_XEC_LPM_PRE_REG_250NS 0x2208
+#define D_XEC_LPM_PRE_REG_1US 0x220C
+#define D_XEC_LPM_PRE_REG_10US 0x2210
+#define D_XEC_LPM_PRE_REG_100US 0x2214
+#define D_XEC_LPM_PRE_REG_125US 0x2218
+#define D_XEC_LPM_PRE_REG_1MS 0x221C
+#define D_XEC_LPM_PRE_REG_10MS 0x2220
+#define D_XEC_LPM_PRE_REG_100MS 0x2224
+
+#define XEC_CFG_3XPORT_MODE 0x2040
+#define XEC_PRE_REG_250NS 0x21E8
+#define XEC_PRE_REG_1US 0x21EC
+#define XEC_PRE_REG_10US 0x21F0
+#define XEC_PRE_REG_100US 0x21F4
+#define XEC_PRE_REG_125US 0x21F8
+#define XEC_PRE_REG_1MS 0x21FC
+#define XEC_PRE_REG_10MS 0x2200
+#define XEC_PRE_REG_100MS 0x2204
+#define XEC_LPM_PRE_REG_250NS 0x2208
+#define XEC_LPM_PRE_REG_1US 0x220C
+#define XEC_LPM_PRE_REG_10US 0x2210
+#define XEC_LPM_PRE_REG_100US 0x2214
+#define XEC_LPM_PRE_REG_125US 0x2218
+#define XEC_LPM_PRE_REG_1MS 0x221C
+#define XEC_LPM_PRE_REG_10MS 0x2220
+#define XEC_LPM_PRE_REG_100MS 0x2224
+#define XEC_USBSSP_CHICKEN_BITS_3 0x2230
+#define XEC_USBSSP_CLK_GATING_CTRL 0x2234
+#define XECP_PM_PMCSR 0x2240
+
+/* XEC_USBSSP_CLK_GATING_CTRL */
+#define HOST20_ACLK_GATING_DISABLE BIT(4)
+#define HOST20_UTMI_GATING_DISABLE BIT(5)
+
+/* XECP_PM_PMCSR */
+#define PS_MASK GENMASK(1, 0)
+#define PS_D0 0
+#define PS_D3 3
+#define PS_PME_En BIT(8)
+
+#define D_XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
+#define XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
+#define CFG_3XPORT_MODE_DIS_SSP (~(1 << 31))
+
+#define CFG_APB_TIMEOUT_PSLVERR_EN BIT(22)
+#define CFG_APB_PSLVERR_EN BIT(23)
+
+#define CIX_USB_CLK_NUM (4)
+#define CIX_USB_CLK_OFF_NUM (2)
+#define CIX_USB_AXI_WR_CACHE_VALUE 0x33
+#define CIX_USB_CLK_32K 32000
+#define CIX_USB_CLK_8M 8000000
+
+#define AXI_CLOCK_VALID BIT(1)
+
+struct cdnsp_sky1 {
+ struct device *dev;
+ void __iomem *axi_base;
+ void __iomem *ctst_base;
+ void __iomem *dr_base;
+ void __iomem *xhci_base;
+ void __iomem *device_base;
+ struct platform_device *cdnsp_pdev;
+ struct reset_control *reset;
+ struct reset_control *preset;
+ struct clk *cix_usb_clks[CIX_USB_CLK_NUM];
+ int id;
+ struct regmap *usb_syscon;
+ int lpm_clk_freq;
+ int sof_clk_freq;
+ bool u3_disable;
+ bool ssp_disable;
+ int axi_bmax_value;
+ struct gpio_desc *oc_gpio;
+ bool wakeup;
+};
+
+#endif /* _CDNSP_SKY1_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 5/5] arm64: dts: cix: Add USB4/USB5 nodes for Sky1
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (8 preceding siblings ...)
2026-09-02 3:21 ` [PATCH v2 4/5] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
@ 2026-09-02 3:21 ` Hongliang Yang
9 siblings, 0 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:21 UTC (permalink / raw)
To: peter.chen, pawell, vkoul, robh, krzk+dt, conor+dt
Cc: gregkh, rogerq, neil.armstrong, p.zabel, fugang.duan,
cix-kernel-upstream, linux-usb, linux-phy, devicetree,
linux-arm-kernel, linux-kernel, Hongliang Yang
Add USB4 and USB5 controller nodes and their associated USB3 PHY nodes
for the CIX Sky1 SoC. Enable USB4 and USB5 in host mode on the Radxa
Orion O6 board.
Signed-off-by: Hongliang Yang <hongliang.yang@cixtech.com>
---
arch/arm64/boot/dts/cix/sky1-orion-o6.dts | 54 ++++++++++
arch/arm64/boot/dts/cix/sky1.dtsi | 118 ++++++++++++++++++++++
2 files changed, 172 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..4f47e8cd70fc 100644
--- a/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
+++ b/arch/arm64/boot/dts/cix/sky1-orion-o6.dts
@@ -80,6 +80,22 @@ pins {
};
};
+
+ pinctrl_usb4: usb4-power-on-cfg {
+ pins {
+ bias-pull-down;
+ drive-strength = <8>;
+ pinmux = <CIX_PAD_GPIO041_FUNC_USB_DRIVE_VBUS4>;
+ };
+ };
+
+ pinctrl_usb5: usb5-power-on-cfg {
+ pins {
+ bias-pull-down;
+ drive-strength = <8>;
+ pinmux = <CIX_PAD_GPIO042_FUNC_USB_DRIVE_VBUS5>;
+ };
+ };
};
&pcie_x8_rc {
@@ -117,3 +133,41 @@ &s5_gpio2 {
&uart2 {
status = "okay";
};
+
+&usb4 {
+ pinctrl-0 = <&pinctrl_usb4>;
+ pinctrl-names = "default";
+
+ status = "okay";
+};
+
+&usbss_4 {
+ dr_mode = "host";
+
+ status = "okay";
+};
+
+&usb5 {
+ pinctrl-0 = <&pinctrl_usb5>;
+ pinctrl-names = "default";
+
+ status = "okay";
+};
+
+&usbss_5 {
+ dr_mode = "host";
+
+ status = "okay";
+};
+
+&usb3_phy4 {
+ status = "okay";
+
+ usb-port@0 {
+ status = "okay";
+ };
+
+ usb-port@1 {
+ status = "okay";
+ };
+};
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index a77c52296ebd..661251765d22 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -6,6 +6,8 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/cix,sky1.h>
+#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
#include "sky1-power.h"
/ {
@@ -13,6 +15,11 @@ / {
#address-cells = <2>;
#size-cells = <2>;
+ aliases {
+ usb4 = &usb4;
+ usb5 = &usb5;
+ };
+
cpus {
#address-cells = <2>;
#size-cells = <0>;
@@ -574,6 +581,117 @@ mbox_ap2sfh: mailbox@80a0000 {
cix,mbox-dir = "tx";
};
+ usb4: usb@91c0314 {
+ compatible = "cix,sky1-usbssp";
+ reg = <0x00 0x091c0314 0x00 0x4>,
+ <0x00 0x091c0400 0x00 0x4>;
+ reg-names = "axi", "controller";
+ ranges;
+ #address-cells = <2>;
+ clocks = <&scmi_clk CLK_TREE_USB3A_H0_CLK_SOF>,
+ <&scmi_clk CLK_TREE_USB3A_0_AXI_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_H0_CLK_LPM>,
+ <&scmi_clk CLK_TREE_USB3A_0_APB_GATE>;
+ clock-names = "sof", "axi", "lpm", "apb";
+ resets = <&s5_syscon SKY1_USBC_SS2_PRST_N>,
+ <&s5_syscon SKY1_USBC_SS2_RST_N>;
+ reset-names = "apb", "core";
+ #size-cells = <2>;
+ cix,usb-syscon = <&s5_syscon>;
+
+ status = "disabled";
+
+ usbss_4: usb@91d0000 {
+ compatible = "cdns,usb3";
+ reg = <0x00 0x91d0000 0x00 0x4000>,
+ <0x00 0x91d8000 0x00 0x8000>,
+ <0x00 0x91d4000 0x00 0x4000>;
+ reg-names = "otg", "xhci", "dev";
+ dr_mode = "otg";
+ interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host", "peripheral",
+ "otg", "wakeup";
+ maximum-speed = "super-speed-plus";
+ phys = <&usb3_phy4_0>;
+ phy-names = "cdns3,usb3-phy";
+
+ status = "disabled";
+ };
+ };
+
+ usb5: usb@91c0324 {
+ compatible = "cix,sky1-usbssp";
+ reg = <0x00 0x091c0324 0x00 0x4>,
+ <0x00 0x091c0410 0x00 0x4>;
+ reg-names = "axi", "controller";
+ ranges;
+ #address-cells = <2>;
+ clocks = <&scmi_clk CLK_TREE_USB3A_H1_CLK_SOF>,
+ <&scmi_clk CLK_TREE_USB3A_1_AXI_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_H1_CLK_LPM>,
+ <&scmi_clk CLK_TREE_USB3A_1_APB_GATE>;
+ clock-names = "sof", "axi", "lpm", "apb";
+ resets = <&s5_syscon SKY1_USBC_SS3_PRST_N>,
+ <&s5_syscon SKY1_USBC_SS3_RST_N>;
+ reset-names = "apb", "core";
+ #size-cells = <2>;
+ cix,usb-syscon = <&s5_syscon>;
+
+ status = "disabled";
+
+ usbss_5: usb@91e0000 {
+ compatible = "cdns,usb3";
+ reg = <0x00 0x91e0000 0x00 0x4000>,
+ <0x00 0x91e8000 0x00 0x8000>,
+ <0x00 0x91e4000 0x00 0x4000>;
+ reg-names = "otg", "xhci", "dev";
+ dr_mode = "otg";
+ interrupts = <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 258 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 257 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "host", "peripheral",
+ "otg", "wakeup";
+ maximum-speed = "super-speed-plus";
+ phys = <&usb3_phy4_1>;
+ phy-names = "cdns3,usb3-phy";
+
+ status = "disabled";
+ };
+ };
+
+ usb3_phy4: usb-phy@9210000 {
+ compatible = "cix,sky1-usb3-phy";
+ reg = <0x00 0x09210000 0x00 0x40000>;
+ #address-cells = <1>;
+ clocks = <&scmi_clk CLK_TREE_USB3A_PHY3_GATE>,
+ <&scmi_clk CLK_TREE_USB3A_PHY_x2_REF>;
+ clock-names = "apb", "ref";
+ resets = <&s5_syscon SKY1_USBPHY_SS_RST_N>,
+ <&s5_syscon SKY1_USBPHY_SS_PST_N>;
+ reset-names = "core", "apb";
+ #size-cells = <0>;
+
+ status = "disabled";
+
+ usb3_phy4_0: usb-port@0 {
+ reg = <0>;
+ #phy-cells = <0>;
+
+ status = "disabled";
+ };
+
+ usb3_phy4_1: usb-port@1 {
+ reg = <1>;
+ #phy-cells = <0>;
+
+ status = "disabled";
+ };
+ };
+
pcie_x8_rc: pcie@a010000 {
compatible = "cix,sky1-pcie-host";
reg = <0x00 0x0a010000 0x00 0x10000>,
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings
2026-09-01 5:47 ` Krzysztof Kozlowski
@ 2026-09-02 3:23 ` Hongliang Yang
0 siblings, 0 replies; 21+ messages in thread
From: Hongliang Yang @ 2026-09-02 3:23 UTC (permalink / raw)
To: krzk
Cc: cix-kernel-upstream, conor+dt, devicetree, fugang.duan, gregkh,
hongliang.yang, krzk+dt, linux-arm-kernel, linux-kernel,
linux-phy, linux-usb, neil.armstrong, p.zabel, pawell, peter.chen,
robh, rogerq, vkoul
Hi Krzysztof,
Thank you for the review. Most of the comments are already addressed in
v2 [1]; the remaining points are answered inline below.
[1] https://lore.kernel.org/all/20260902032134.1182634-1-hongliang.yang@cixtech.com/
On 01/09/2026 07:47, Krzysztof Kozlowski wrote:
> Please use subject prefixes matching the subsystem. You can get them for
> example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
> your patch is touching. For bindings, the preferred subjects are
> explained here:
> https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
>
> A nit, subject: drop second/last, redundant "bindings". The
> "dt-bindings" prefix is already stating that these are bindings.
> See also:
> https://elixir.bootlin.com/linux/v7.1-rc7/source/Documentation/devicetree/bindings/submitting-patches.rst#L23
>
> And do not combine USB patches with other subsystems.
Done in v2: the combined bindings patch is split into separate patches
with "dt-bindings: phy:" and "dt-bindings: usb:" subject prefixes, one
schema per patch, e.g.
dt-bindings: phy: Add CIX Sky1 USB3 PHY
dt-bindings: usb: Add CIX Sky1 USBSSP controller
> Please read and follow DTS coding style.
Done in v2: the schemas and the new DTS regions now pass
dt-check-style in strict mode.
> > +unevaluatedProperties: false
>
> Which other schema do you want to reference?
None. Changed to additionalProperties: false in v2.
> > + reg:
> > + items:
> > + - description: AXI property configuration register
> > + - description: Controller status register
>
> Registers are not address spaces. Devices do not have address spaces of
> one register, so this is not a real device.
I understand the concern, and we looked at this again against the SoC
documentation. These two registers are RCSU (system configuration
unit) registers, but each of them is dedicated to one USB controller
and not shared with any other device:
- The first register is the controller's Control Register for the AXI
cache attributes. Its bit fields are awcache[3:0] (bits 3:0) and
arcache[3:0] (bits 7:4), i.e. the AXI cache attributes of the
controller's own AXI master interface. The glue driver programs it
once during initialization.
- The second register is a status register providing the AXI clock
valid status the driver polls while enabling the controller.
The two instances submitted in this series illustrate the per-device
offsets: the USB4 controller uses 0x091c0314 (AXI cache attributes)
and 0x091c0400 (status), while USB5 uses 0x091c0324 and 0x091c0410.
The reason they end up in a separate node from the Cadence USBSSP core
is purely physical: on Sky1 the controller's control registers and the
Cadence USBSSP core registers are two disjoint regions in the address
map (e.g. 0x091c0314 vs 0x091d0000 for the USB4 controller). The glue
node therefore describes the Sky1-specific control registers, clocks
and resets, while the core node is the Cadence USBSSP core itself,
describing the actual register windows the cdns3 driver maps
("otg", "xhci", "dev").
That said, the register windows are admittedly tiny, so if you prefer
this modelled differently we are open to suggestions. One alternative
we considered was folding the control register file into the core node
and referencing the Sky1 glue properties from there, but that would
mix the Cadence core registers with Sky1-specific ones and lose the
1:1 mapping to the hardware topology.
> > + - const: axi_property
>
> axi
>
> > + - const: controller_status
>
> controller
Renamed in v2 (reg-names = "axi", "controller").
> > + cix,usb_syscon:
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > + description:
> > + Phandle to the Sky1 S5 system controller used to program USB mode
> > + strap controls.
>
> So this is part of that device most likely.
The OTG mode strap bits live in the S5 power-management controller,
which is a separate hardware block shared by several subsystems, so
they are not part of the USB controller's register file. That is why
the strap is accessed through the syscon phandle instead of a reg
entry, while the per-controller registers discussed above stay in the
USB node's own reg.
> > + axi_bmax_value:
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + default: 0x7
> > + description:
> > + AXI bus maximum burst length configuration value.
Dropped in v2: the value is fixed for the SoC, so the driver now uses a
compile-time constant and the property is gone.
> > +patternProperties:
> > + "^usb-controller@[0-9a-f]+$":
>
> Why using completely different names than entire Linux kernel?
Renamed to "usb@..." in v2, matching the cdns,usb3 nodes elsewhere.
> > + $ref: cdns,usb3.yaml#
> > + description:
> > + The Cadence USBSSP core controller node. See cdns,usb3.yaml
> > + for properties such as dr_mode, maximum-speed, phys, and interrupts.
>
> Redundant description.
Shortened in v2.
Best regards,
Hongliang Yang
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/5] phy: cix: Add CIX Sky1 USB3 PHY driver
2026-09-02 3:21 ` [PATCH v2 3/5] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
@ 2026-09-02 3:32 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 3:32 UTC (permalink / raw)
To: Hongliang Yang
Cc: robh, vkoul, devicetree, olteanv, conor+dt, linux-phy,
neil.armstrong
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Hardware resets asserted after exposing the PHY provider, risking a race condition with consumers.
- [Medium] Teardown asymmetry and inverted unroll order for hardware clocks and resets.
- [Medium] Unsafe string comparison using `fwnode_get_name()` without NULL check.
--
commit dd0ea5bd301cf4c2946f4bb80843b35c7ad1ab64
Author: Hongliang Yang <hongliang.yang@cixtech.com>
phy: cix: Add CIX Sky1 USB3 PHY driver
This commit adds a PHY driver for the USB3 PHY on CIX Sky1 SoCs, handling
initialization, reset, and clock management for the multi-port USB3 PHY.
> diff --git a/drivers/phy/cix/phy-cix-usb3.c b/drivers/phy/cix/phy-cix-usb3.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/phy/cix/phy-cix-usb3.c
[ ... ]
> +static int sky1_u3phy_exit(struct cix_u3phy *u3phy)
> +{
> + u3phy->init_count--;
> + if (u3phy->init && u3phy->init_count == 0) {
> + reset_control_assert(u3phy->reset);
> + reset_control_assert(u3phy->preset);
> + clk_disable_unprepare(u3phy->apb_clk);
> + clk_disable_unprepare(u3phy->ref_clk);
[Severity: Medium]
Does this disable the clocks in the correct order? The initialization path
enables apb_clk before ref_clk. To ensure a proper LIFO teardown sequence,
should ref_clk be disabled before apb_clk here?
[ ... ]
> +static int sky1_u3phy_init(struct cix_u3phy *u3phy)
> +{
[ ... ]
> +disable_ref_clk:
> + clk_disable_unprepare(u3phy->ref_clk);
> +disable_apb_clk:
> + clk_disable_unprepare(u3phy->apb_clk);
> +assert_reset_preset:
> + reset_control_assert(u3phy->preset);
> + reset_control_assert(u3phy->reset);
[Severity: Medium]
Will this error unroll sequence leave the hardware in an unrecoverable state?
This path disables the clocks before asserting the hardware resets. This
contradicts the normal teardown sequence in sky1_u3phy_exit(), which asserts
the resets before disabling the clocks.
[ ... ]
> +static int cix_u3phy_probe(struct platform_device *pdev)
> +{
[ ... ]
> + device_for_each_child_node_scoped(dev, child_fn) {
> + child_np = to_of_node(child_fn);
> + if (!strncmp(fwnode_get_name(child_fn), "usb-port", 8) ||
> + !strncmp(fwnode_get_name(child_fn), "USB", 3))
[Severity: Medium]
Can this trigger a NULL pointer dereference? fwnode_get_name() can return
NULL for certain software nodes.
Additionally, does using strncmp() with a fixed length incorrectly match
arbitrary suffixes like "usb-port-foo" while failing to properly handle
unit addresses like "usb-port@0"? Could fwnode_name_eq() be used here
instead?
[ ... ]
> + 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;
> + }
> + reset_control_assert(u3phy->reset);
> + reset_control_assert(u3phy->preset);
[Severity: High]
Does asserting these resets after exposing the PHY provider create a race
condition with consumers?
Once devm_of_phy_provider_register() completes, a consumer driver could
concurrently probe and call phy_init(). If the consumer initializes the
PHY and deasserts the resets, this probe thread will then continue and
unconditionally assert the resets without holding u3phy->mutex. This would
destroy the initialized state while the consumer is actively using the PHY.
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831082309.3854507-1-hongliang.yang@cixtech.com?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 4/5] usb: cdns3: Add CIX Sky1 glue driver
2026-09-02 3:21 ` [PATCH v2 4/5] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
@ 2026-09-02 3:32 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 3:32 UTC (permalink / raw)
To: Hongliang Yang
Cc: vkoul, linux-phy, olteanv, robh, devicetree, conor+dt,
neil.armstrong
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] Massive resource leak on probe error paths (I/O memory, clocks, and resets).
- [High] Thread-safety and double-free vulnerability due to modifying a static global of_dev_auxdata array during probe.
- [High] Unbalanced runtime PM usage count and missing PM disable in cdnsp_sky1_remove.
- [High] Incorrect reset control assertion logic in remove path fails to quiesce hardware.
- [High] NULL pointer dereference when accessing xhci_dev->dev in platform_suspend.
- [Medium] OF node reference leak in sky1_of_get_addr_by_name() because the reference obtained by of_get_next_child() is never released.
- [Medium] Destructive error handling in cdnsp_sky1_clk_enable_resume corrupts internal clock pointers.
- [Medium] Potential Use-After-Free (UAF) due to missing device refcounting in iterator callbacks during shutdown.
- [Medium] Undefined behavior due to bitwise shift into the sign bit.
- [Medium] PM subsystem guideline violation: PM sleep callbacks are assigned without the required pm_sleep_ptr() wrapper.
--
commit 0232318426cffe797c62a93651116101c396ebb4
Author: Hongliang Yang <hongliang.yang@cixtech.com>
usb: cdns3: Add CIX Sky1 glue driver
This commit adds a glue layer driver for the Cadence USBSSP controller
integrated in the CIX Sky1 SoC. The driver manages the controller clocks,
resets, and the mode strap controls located in the Sky1 S5 system
controller.
> diff --git a/drivers/usb/cdns3/cdnsp-sky1.c b/drivers/usb/cdns3/cdnsp-sky1.c
> new file mode 100644
> index 0000000000000..447c683b726c1
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdnsp-sky1.c
[ ... ]
> +static int cdnsp_sky1_clk_enable_resume(struct device *dev)
> +{
> + int i, ret;
> + struct cdnsp_sky1 *data = dev_get_drvdata(dev);
> + struct clk **cix_usb_clks = data->cix_usb_clks;
> +
> + for (i = 0; i < CIX_USB_CLK_OFF_NUM; i++) {
> + ret = clk_prepare_enable(cix_usb_clks[i]);
> + if (ret) {
> + dev_err(dev, "failed to enable clock %s: %d\n",
> + cix_usb_clk_names[i], ret);
> + goto err_usb_clks;
> + }
> + }
> + return 0;
> +
> +err_usb_clks:
> + cix_usb_clks[i] = NULL;
> + while (--i >= 0) {
> + clk_disable_unprepare(cix_usb_clks[i]);
> + cix_usb_clks[i] = NULL;
> + }
> + return ret;
> +};
[Severity: Medium]
Does setting cix_usb_clks[i] to NULL here permanently wipe the devm-managed
clock references from the driver's private state? If a system clock fails
to resume, won't subsequent calls to enable or disable these clocks receive
NULL pointers and silently fail to configure the hardware?
[ ... ]
> +static int cdns_sky1_platform_suspend(struct device *dev,
> + bool suspend, bool wakeup)
> +{
> + struct cdns *cdns = dev_get_drvdata(dev);
> + struct platform_device *xhci_dev = cdns->host_dev;
> + struct usb_hcd *hcd;
> + struct device *parent = cdns->dev->parent;
> + struct cdnsp_sky1 *data = dev_get_drvdata(parent);
> + u32 value;
> + int ret = 0;
> + int count = 3;
> +
> + data->wakeup = wakeup;
> +
> + if (cdns->role != USB_ROLE_HOST)
> + return 0;
> +
> + hcd = dev_get_drvdata(&xhci_dev->dev);
[Severity: High]
Could xhci_dev be NULL if the host initialization failed or was unbound?
If so, would accessing &xhci_dev->dev lead to an out-of-bounds pointer
dereference during system suspend?
[ ... ]
> +static void *sky1_of_get_addr_by_name(struct device_node *parent, char *name)
> +{
> + struct device_node *node;
> + int index;
> +
> + node = of_get_next_child(parent, NULL);
> + if (node) {
> + index = of_property_match_string(node, "reg-names", name);
> + if (index >= 0)
> + return of_iomap(node, index);
> + }
> + return NULL;
> +}
[Severity: Medium]
Does of_get_next_child() return the node with an incremented reference count?
Should there be a call to of_node_put(node) before returning to prevent a
memory leak?
[ ... ]
> +static int cdnsp_sky1_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *node = dev->of_node;
> + struct cdnsp_sky1 *data;
> + int ret = 0;
> + struct cdns3_platform_data *cdns_sky1_pdata;
[ ... ]
> + ret = cdnsp_sky1_drd_init(data);
> + if (ret == -ETIMEDOUT)
> + return -EPROBE_DEFER;
> + if (ret)
> + return ret;
[Severity: High]
If probe fails here and returns directly, are the I/O memory mappings
for xhci_base and device_base (which were mapped via non-devm of_iomap)
left active? Should they be unmapped and the clocks/resets cleaned up?
[ ... ]
> + /* release by platform_device_release */
> + cdns_sky1_pdata = kzalloc(sizeof(*cdns_sky1_pdata), GFP_KERNEL);
> + if (!cdns_sky1_pdata)
> + return -ENOMEM;
> + cdns_sky1_pdata->platform_suspend = cdns_sky1_platform_suspend;
> + cdns_sky1_pdata->quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW;
> + cdns_sky1_auxdata->platform_data = cdns_sky1_pdata;
> + ret = of_platform_populate(node, NULL, cdns_sky1_auxdata, dev);
[Severity: High]
Is it safe to assign a dynamically allocated instance-specific pointer
(cdns_sky1_pdata) to the statically allocated global array cdns_sky1_auxdata?
Could this lead to a double-free vulnerability if multiple controller instances
exist or if the driver is repeatedly unbound and rebound?
> + if (ret) {
> + dev_err(dev, "failed to create children: %d\n", ret);
> + goto err;
> + }
> + device_set_wakeup_capable(dev, true);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + return 0;
> +err:
> + kfree(cdns_sky1_pdata);
> + return ret;
> +}
[Severity: High]
Does this shared error path fail to clean up the I/O memory mappings, assert
resets, and disable clocks before returning?
> +static void cdnsp_sky1_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct cdnsp_sky1 *data = dev_get_drvdata(dev);
> +
> + pm_runtime_get_sync(dev);
> + of_platform_depopulate(dev);
> + sky1_put_addr(data->xhci_base);
> + sky1_put_addr(data->device_base);
> + reset_control_deassert(data->reset);
> + reset_control_deassert(data->preset);
[Severity: High]
Should these be reset_control_assert() instead of deassert() to correctly
quiesce the hardware before the clocks are disabled?
> + cdnsp_sky1_clk_disable_all(dev);
> + platform_set_drvdata(pdev, NULL);
> +}
[Severity: High]
Does this function need to call pm_runtime_put_sync() and pm_runtime_disable()?
Because pm_runtime_get_sync(dev) increments the usage counter at the start of
the function, could omitting the decrement lead to a leaked usage count and a
broken PM state machine for the device on subsequent binds?
[ ... ]
> +static const struct dev_pm_ops cdnsp_sky1_pm_ops = {
> + .suspend = cdnsp_sky1_system_suspend,
> + .resume = cdnsp_sky1_system_resume,
> +};
[Severity: Medium]
Should these sleep callbacks be wrapped with pm_sleep_ptr()? Without it,
if CONFIG_PM is enabled but CONFIG_PM_SLEEP is disabled, could this generate
dead code warnings for the suspend and resume functions?
[ ... ]
> +static void cdnsp_sky1_shutdown(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct cdnsp_sky1 *data = dev_get_drvdata(dev);
> + struct cdns *cdns = NULL;
> + struct device *gadget_dev = NULL;
> +
> + if (!device_may_wakeup(dev)) {
> + /*
> + * Find the cdns3 child device, then find its gadget device
> + * and release the function driver before disabling clocks.
> + * This ensures all register accesses in gadget_unbind_driver
> + * complete before clocks are turned off.
> + */
> + device_for_each_child(dev, &cdns, cdnsp_sky1_find_cdns);
> + if (cdns)
> + device_for_each_child(cdns->dev, &gadget_dev,
> + cdnsp_sky1_find_gadget_match);
> + if (gadget_dev)
> + device_release_driver(gadget_dev);
[Severity: Medium]
Does device_for_each_child() release the reference on the child device
immediately after the callback returns?
If so, could extracting the cdns and gadget_dev pointers out of the iterator
loop without acquiring references create a use-after-free window when they
are subsequently dereferenced?
> diff --git a/drivers/usb/cdns3/cdnsp-sky1.h b/drivers/usb/cdns3/cdnsp-sky1.h
> new file mode 100644
> index 0000000000000..49c3d5eab1744
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdnsp-sky1.h
[ ... ]
> +#define D_XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
> +#define XEC_CFG_3XPORT_MODE_VALUE 0xa0031e07
> +#define CFG_3XPORT_MODE_DIS_SSP (~(1 << 31))
[Severity: Medium]
Does shifting 1 by 31 bits here trigger undefined behavior since 1 is a
signed int? Could this use 1U or BIT(31) instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831082309.3854507-1-hongliang.yang@cixtech.com?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-09-02 3:32 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 8:23 [PATCH v1 0/4] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
2026-08-31 8:23 ` [PATCH v1 1/4] dt-bindings: Add CIX Sky1 USB bindings Hongliang Yang
2026-08-31 8:32 ` sashiko-bot
2026-08-31 16:07 ` Conor Dooley
2026-09-01 5:47 ` Krzysztof Kozlowski
2026-09-02 3:23 ` Hongliang Yang
2026-08-31 8:23 ` [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
2026-08-31 8:33 ` sashiko-bot
2026-08-31 8:23 ` [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
2026-08-31 8:41 ` sashiko-bot
2026-08-31 8:57 ` Philipp Zabel
2026-08-31 8:23 ` [PATCH v1 4/4] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
2026-08-31 8:33 ` sashiko-bot
2026-09-02 3:21 ` [PATCH v2 0/5] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 1/5] dt-bindings: phy: Add CIX Sky1 USB3 PHY Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 2/5] dt-bindings: usb: Add CIX Sky1 USBSSP controller Hongliang Yang
2026-09-02 3:21 ` [PATCH v2 3/5] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
2026-09-02 3:32 ` sashiko-bot
2026-09-02 3:21 ` [PATCH v2 4/5] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
2026-09-02 3:32 ` sashiko-bot
2026-09-02 3:21 ` [PATCH v2 5/5] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox