* [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 16:07 ` Conor Dooley
2026-09-01 5:47 ` Krzysztof Kozlowski
2026-08-31 8:23 ` [PATCH v1 2/4] phy: cix: Add CIX Sky1 USB3 PHY driver Hongliang Yang
` (8 subsequent siblings)
9 siblings, 2 replies; 15+ 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] 15+ 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 16:07 ` Conor Dooley
2026-09-01 5:47 ` Krzysztof Kozlowski
1 sibling, 0 replies; 15+ 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] 15+ 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 16:07 ` Conor Dooley
@ 2026-09-01 5:47 ` Krzysztof Kozlowski
2026-09-02 3:23 ` Hongliang Yang
1 sibling, 1 reply; 15+ 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] 15+ 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; 15+ 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] 15+ 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:23 ` [PATCH v1 3/4] usb: cdns3: Add CIX Sky1 glue driver Hongliang Yang
` (7 subsequent siblings)
9 siblings, 0 replies; 15+ 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] 15+ 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: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, 1 reply; 15+ 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] 15+ 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:57 ` Philipp Zabel
0 siblings, 0 replies; 15+ 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] 15+ 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-09-02 3:21 ` [PATCH v2 0/5] Add CIX Sky1 USB3/USBSSP support Hongliang Yang
` (5 subsequent siblings)
9 siblings, 0 replies; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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: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, 0 replies; 15+ 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] 15+ 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:21 ` [PATCH v2 5/5] arm64: dts: cix: Add USB4/USB5 nodes for Sky1 Hongliang Yang
9 siblings, 0 replies; 15+ 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] 15+ 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; 15+ 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] 15+ messages in thread