diff --git a/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml b/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml index 9d058c00ab3d59..d46d13597466fe 100644 --- a/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml +++ b/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: - thead,th1520-clk-ap + - thead,th1520-clk-misc - thead,th1520-clk-vo reg: @@ -32,8 +33,8 @@ properties: items: - description: | One input clock: - - For "thead,th1520-clk-ap": the clock input must be the 24 MHz - main oscillator. + - For "thead,th1520-clk-ap" and "thead,th1520-clk-misc": the clock + input must be the 24 MHz main oscillator. - For "thead,th1520-clk-vo": the clock input must be the VIDEO_PLL, which is configured by the AP clock controller. According to the TH1520 manual, VIDEO_PLL is a Silicon Creations Sigma-Delta PLL diff --git a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml index bba6f5b6606fdf..55069533f6d912 100644 --- a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml @@ -95,6 +95,12 @@ patternProperties: '#interrupt-cells': const: 2 + patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml b/Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml new file mode 100644 index 00000000000000..37f5cfb95bad01 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/thead,th1520-usb-phy.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/thead,th1520-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-Head TH1520 USB PHY + +description: | + The T-HEAD TH1520 USB PHY is a Synopsys USB 3.0 FemtoPHY glued with some + custom logic to configure PHY parameters. + +maintainers: + - Icenowy Zheng + - Wei Fu + - Drew Fustini + +properties: + compatible: + const: thead,th1520-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: PHY bus clock + - description: PHY reference clock + + clock-names: + items: + - const: bus + - const: ref + + resets: + items: + - description: PHY bus reset + - description: PHY reset + + reset-names: + items: + - const: bus + - const: phy + + avdd33-usb3-supply: + description: | + 3.3V power supply for the PHY, named AVDD33_USB3 in the SoC pin list. + +required: + - compatible + - "#phy-cells" + - clocks + - clock-names + - resets + - reset-names + - avdd33-usb3-supply + +additionalProperties: false + +examples: + - | + phy@ec030000 { + compatible = "thead,th1520-usb-phy"; + reg = <0xec030000 0x10000>; + #phy-cells = <0>; + clocks = <&clk_misc 1>, <&clk_misc 3>; + clock-names = "bus", "ref"; + resets = <&rst_misc 6>, <&rst_misc 7>; + reset-names = "bus", "phy"; + avdd33-usb3-supply = <&avdd33_usb3>; + }; diff --git a/Documentation/devicetree/bindings/usb/vialab,vl817.yaml b/Documentation/devicetree/bindings/usb/vialab,vl817.yaml index c815010ba9c2e8..7387f4fae54d1e 100644 --- a/Documentation/devicetree/bindings/usb/vialab,vl817.yaml +++ b/Documentation/devicetree/bindings/usb/vialab,vl817.yaml @@ -10,7 +10,7 @@ maintainers: - Anand Moon allOf: - - $ref: usb-device.yaml# + - $ref: usb-hub.yaml# properties: compatible: @@ -34,6 +34,8 @@ properties: description: phandle to the peer hub on the controller. + ports: true + required: - compatible - reg diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi b/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi index 8e76b63e0100aa..bfda5a6b56b8f8 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi +++ b/arch/riscv/boot/dts/thead/th1520-lichee-module-4a.dtsi @@ -20,6 +20,16 @@ device_type = "memory"; reg = <0x0 0x00000000 0x2 0x00000000>; }; + + /* TODO: Switch to AON regulator when it's available. */ + avdd33_usb3: regulator-avdd33-usb3 { + compatible = "regulator-fixed"; + regulator-name = "AVDD33_USB3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + /* Marked as always on on the schematics */ + regulator-always-on; + }; }; &osc { @@ -202,3 +212,8 @@ max-frequency = <198000000>; status = "okay"; }; + +&usb_phy { + avdd33-usb3-supply = <&avdd33_usb3>; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts index 7cb7d28683bce7..de38f1f457e6ba 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -4,6 +4,7 @@ */ #include "th1520-lichee-module-4a.dtsi" +#include / { model = "Sipeed Lichee Pi 4A"; @@ -16,6 +17,9 @@ gpio3 = &gpio3; gpio4 = &gpio4; gpio5 = &aogpio; + i2c0 = &i2c0; + i2c1 = &i2c1; + i2c3 = &i2c3; serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; @@ -94,6 +98,225 @@ cooling-levels = <0 66 196 255>; }; + hub_5v: regulator-hub-5v { + compatible = "regulator-fixed"; + regulator-name = "HUB_5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&ioexp3 3 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + vcc5v_usb: regulator-vcc5v-usb { + compatible = "regulator-fixed"; + regulator-name = "VCC5V_USB"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + enable-active-high; + /* + * Workaround for Linux currently being not able to power on + * Vbus for USB Type-A connectors. + */ + regulator-always-on; + }; + + connector-usb-a-1 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_1_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port1_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_1_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port1_ep>; + }; + }; + }; + }; + + connector-usb-a-2 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_2_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port2_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_2_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port2_ep>; + }; + }; + }; + }; + + connector-usb-a-3 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_3_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port3_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_3_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port3_ep>; + }; + }; + }; + }; + + connector-usb-a-4 { + compatible = "usb-a-connector"; + vbus-supply = <&vcc5v_usb>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + usb_a_4_hs_ep: endpoint { + remote-endpoint = <&hub_hs_port4_ep>; + }; + }; + + port@1 { + reg = <1>; + + usb_a_4_ss_ep: endpoint { + remote-endpoint = <&hub_ss_port4_ep>; + }; + }; + }; + }; +}; + +&aogpio { + /* Route USB2 to the onboard hub for normal operation */ + sel-usb-hub-hog { + gpio-hog; + gpios = <4 GPIO_ACTIVE_HIGH>; + output-high; + }; +}; + +&dpu { + status = "okay"; +}; + +&hdmi { + status = "okay"; +}; + +&hdmi_out_port { + hdmi_out_con: endpoint { + remote-endpoint = <&hdmi_con_in>; + }; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + status = "okay"; + + ioexp1: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "cam0_dvdd12", + "cam0_avdd28", + "cam0_dovdd18"; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + status = "okay"; + + ioexp2: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "", + "cam0_reset", + "cam1_reset", + "cam2_reset", + "wl_host_wake", + "bt_resetn", + "", + "bt_host_wake"; + }; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c3_pins>; + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + status = "okay"; + + ioexp3: gpio@18 { + compatible = "nxp,pca9557"; + reg = <0x18>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "tp0_rst", + "", + "", + "vcc5v_usb", + "vdd28_tp0", + "vdd33_lcd0", + "vdd18_lcd0", + "lcd0_reset"; + }; }; &padctrl0_apsys { @@ -109,6 +332,18 @@ }; }; + i2c3_pins: i2c3-0 { + i2c-pins { + pins = "I2C3_SCL", "I2C3_SDA"; + function = "i2c"; + bias-disable; /* external pull-up */ + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; + uart0_pins: uart0-0 { tx-pins { pins = "UART0_TXD"; @@ -132,17 +367,29 @@ }; }; -&dpu { - status = "okay"; -}; - -&hdmi { - status = "okay"; -}; +&padctrl1_apsys { + i2c0_pins: i2c0-0 { + i2c-pins { + pins = "I2C0_SCL", "I2C0_SDA"; + function = "i2c"; + bias-disable; /* external pull-up */ + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; -&hdmi_out_port { - hdmi_out_con: endpoint { - remote-endpoint = <&hdmi_con_in>; + i2c1_pins: i2c1-0 { + i2c-pins { + pins = "I2C1_SCL", "I2C1_SDA"; + function = "i2c"; + bias-disable; /* external pull-up */ + drive-strength = <7>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; }; }; @@ -151,3 +398,98 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; }; + +&usb { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + hub_hs: hub@1 { + compatible = "usb2109,2817"; + reg = <1>; + peer-hub = <&hub_ss>; + vdd-supply = <&hub_5v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + hub_hs_port1_ep: endpoint { + remote-endpoint = <&usb_a_1_hs_ep>; + }; + }; + + port@2 { + reg = <2>; + + hub_hs_port2_ep: endpoint { + remote-endpoint = <&usb_a_2_hs_ep>; + }; + }; + + port@3 { + reg = <3>; + + hub_hs_port3_ep: endpoint { + remote-endpoint = <&usb_a_3_hs_ep>; + }; + }; + + port@4 { + reg = <4>; + + hub_hs_port4_ep: endpoint { + remote-endpoint = <&usb_a_4_hs_ep>; + }; + }; + }; + }; + + hub_ss: hub@2 { + compatible = "usb2109,817"; + reg = <2>; + peer-hub = <&hub_hs>; + vdd-supply = <&hub_5v>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + + hub_ss_port1_ep: endpoint { + remote-endpoint = <&usb_a_1_ss_ep>; + }; + }; + + port@2 { + reg = <2>; + + hub_ss_port2_ep: endpoint { + remote-endpoint = <&usb_a_2_ss_ep>; + }; + }; + + port@3 { + reg = <3>; + + hub_ss_port3_ep: endpoint { + remote-endpoint = <&usb_a_3_ss_ep>; + }; + }; + + port@4 { + reg = <4>; + + hub_ss_port4_ep: endpoint { + remote-endpoint = <&usb_a_4_ss_ep>; + }; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index 5e91dc1d2b9b7e..df49f8f749ef78 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -316,6 +316,20 @@ status = "disabled"; }; + usb: usb@ffe7040000 { + compatible = "snps,dwc3"; + reg = <0xff 0xe7040000 0x0 0x10000>; + interrupts = <68 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_misc CLK_USB>, + <&clk_misc CLK_USB_CTL_REF>, + <&clk_misc CLK_USB_SUSPEND>; + clock-names = "bus_early", "ref", "suspend"; + resets = <&rst_misc TH1520_RESET_ID_USB3_VCC>; + phys = <&usb_phy>; + phy-names = "usb3-phy"; + status = "disabled"; + }; + gmac1: ethernet@ffe7060000 { compatible = "thead,th1520-gmac", "snps,dwmac-3.70a"; reg = <0xff 0xe7060000 0x0 0x2000>, <0xff 0xec004000 0x0 0x1000>; @@ -366,8 +380,8 @@ compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe7080000 0x0 0x10000>; interrupts = <62 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk CLK_EMMC_SDIO>; - clock-names = "core"; + clocks = <&clk CLK_EMMC_SDIO>, <&clk_misc CLK_EMMC>; + clock-names = "core", "bus"; status = "disabled"; }; @@ -375,8 +389,8 @@ compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe7090000 0x0 0x10000>; interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk CLK_EMMC_SDIO>; - clock-names = "core"; + clocks = <&clk CLK_EMMC_SDIO>, <&clk_misc CLK_SDIO0>; + clock-names = "core", "bus"; status = "disabled"; }; @@ -384,8 +398,8 @@ compatible = "thead,th1520-dwcmshc"; reg = <0xff 0xe70a0000 0x0 0x10000>; interrupts = <71 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk CLK_EMMC_SDIO>; - clock-names = "core"; + clocks = <&clk CLK_EMMC_SDIO>, <&clk_misc CLK_SDIO1>; + clock-names = "core", "bus"; status = "disabled"; }; @@ -411,6 +425,36 @@ status = "disabled"; }; + i2c0: i2c@ffe7f20000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xe7f20000 0x0 0x4000>; + interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@ffe7f24000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xe7f24000 0x0 0x4000>; + interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c4: i2c@ffe7f28000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xe7f28000 0x0 0x4000>; + interrupts = <48 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C4>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + gpio@ffe7f34000 { compatible = "snps,dw-apb-gpio"; reg = <0xff 0xe7f34000 0x0 0x1000>; @@ -509,6 +553,16 @@ thead,pad-group = <3>; }; + i2c2: i2c@ffec00c000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xec00c000 0x0 0x4000>; + interrupts = <46 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + uart2: serial@ffec010000 { compatible = "snps,dw-apb-uart"; reg = <0xff 0xec010000 0x0 0x4000>; @@ -520,6 +574,16 @@ status = "disabled"; }; + i2c3: i2c@ffec014000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xec014000 0x0 0x4000>; + interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C3>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pwm: pwm@ffec01c000 { compatible = "thead,th1520-pwm"; reg = <0xff 0xec01c000 0x0 0x4000>; @@ -533,6 +597,26 @@ #reset-cells = <1>; }; + clk_misc: clock-controller@ffec02c100 { + compatible = "thead,th1520-clk-misc"; + reg = <0xff 0xec02c100 0x0 0x100>; + clocks = <&osc>; + #clock-cells = <1>; + }; + + usb_phy: phy@ffec030000 { + compatible = "thead,th1520-usb-phy"; + reg = <0xff 0xec030000 0x0 0x10000>; + clocks = <&clk_misc CLK_USB>, + <&clk_misc CLK_USB_PHY_REF>; + clock-names = "bus", "ref"; + resets = <&rst_misc TH1520_RESET_ID_USB3_APB>, + <&rst_misc TH1520_RESET_ID_USB3_PHY>; + reset-names = "bus", "phy"; + #phy-cells = <0>; + status = "disabled"; + }; + rst_vp: reset-controller@ffecc30000 { compatible = "thead,th1520-reset-vp"; reg = <0xff 0xecc30000 0x0 0x14>; @@ -725,6 +809,16 @@ status = "disabled"; }; + i2c5: i2c@fff7f2c000 { + compatible = "thead,th1520-i2c", "snps,designware-i2c"; + reg = <0xff 0xf7f2c000 0x0 0x4000>; + interrupts = <49 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk CLK_I2C5>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + timer4: timer@ffffc33000 { compatible = "snps,dw-apb-timer"; reg = <0xff 0xffc33000 0x0 0x14>; diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c index 3a6847f1c950fc..24f785f0b329a7 100644 --- a/drivers/clk/thead/clk-th1520-ap.c +++ b/drivers/clk/thead/clk-th1520-ap.c @@ -1266,6 +1266,41 @@ static CCU_GATE(CLK_MIPIDSI1_PIXCLK, mipi_dsi1_pixclk, "mipi-dsi1-pixclk", static CCU_GATE(CLK_HDMI_PIXCLK, hdmi_pixclk, "hdmi-pixclk", video_pll_clk_pd, 0x4, 0, 0); +static struct clk_fixed_factor usb_suspend_div_clk = { + .div = 24, + .mult = 1, + .hw.init = CLK_HW_INIT_PARENTS_DATA("usb-suspend-div", + osc_24m_clk, + &clk_fixed_factor_ops, + 0), +}; + +static const struct clk_parent_data usb_suspend_parents[] = { + { .hw = &usb_suspend_div_clk.hw }, +}; + +static CCU_GATE(CLK_MISCSYS_ACLK, miscsys_aclk, "miscsys-aclk", axi_aclk_pd, + 0x0, 0, CLK_IS_CRITICAL); + +static const struct clk_parent_data miscsys_aclk_pd[] = { + { .hw = &miscsys_aclk.gate.hw }, +}; + +static CCU_GATE(CLK_USB, usb_clk, "usb", miscsys_aclk_pd, 0x4, 0, + CLK_IS_CRITICAL); +static CCU_GATE(CLK_USB_CTL_REF, usb_ctl_ref_clk, "usb-ctl-ref", osc_24m_clk, + 0x4, 1, 0); +static CCU_GATE(CLK_USB_PHY_REF, usb_phy_ref_clk, "usb-phy-ref", osc_24m_clk, + 0x4, 2, 0); +static CCU_GATE(CLK_USB_SUSPEND, usb_suspend_clk, "usb-suspend", + usb_suspend_parents, 0x4, 3, 0); +static CCU_GATE(CLK_EMMC, emmc_clk, "emmc", perisys_ahb_hclk_pd, 0x8, 0, + 0); +static CCU_GATE(CLK_SDIO0, sdio0_clk, "sdio0", perisys_ahb_hclk_pd, 0xc, 0, + 0); +static CCU_GATE(CLK_SDIO1, sdio1_clk, "sdio1", perisys_ahb_hclk_pd, 0x10, 0, + 0); + static CLK_FIXED_FACTOR_HW(gmac_pll_clk_100m, "gmac-pll-clk-100m", &gmac_pll_clk.common.hw, 10, 1, 0); @@ -1410,6 +1445,17 @@ static struct ccu_gate *th1520_vo_gate_clks[] = { &hdmi_pixclk }; +static struct ccu_gate *th1520_misc_gate_clks[] = { + &miscsys_aclk, + &usb_clk, + &usb_ctl_ref_clk, + &usb_phy_ref_clk, + &usb_suspend_clk, + &emmc_clk, + &sdio0_clk, + &sdio1_clk +}; + static const struct regmap_config th1520_clk_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -1451,6 +1497,14 @@ static const struct th1520_plat_data th1520_vo_platdata = { .nr_gate_clks = ARRAY_SIZE(th1520_vo_gate_clks), }; +static const struct th1520_plat_data th1520_misc_platdata = { + .th1520_gate_clks = th1520_misc_gate_clks, + + .nr_clks = CLK_SDIO1 + 1, + + .nr_gate_clks = ARRAY_SIZE(th1520_misc_gate_clks), +}; + /* * Maintain clock rate of c910_bus_clk below TH1520_C910_BUS_MAX_RATE (750MHz) * when its parent, c910_clk, changes the rate. @@ -1609,6 +1663,12 @@ static int th1520_clk_probe(struct platform_device *pdev) return ret; } + if (plat_data == &th1520_ap_platdata) { + ret = devm_clk_hw_register(dev, &usb_suspend_div_clk.hw); + if (ret) + return ret; + } + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv); if (ret) return ret; @@ -1625,6 +1685,10 @@ static const struct of_device_id th1520_clk_match[] = { .compatible = "thead,th1520-clk-vo", .data = &th1520_vo_platdata, }, + { + .compatible = "thead,th1520-clk-misc", + .data = &th1520_misc_platdata, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, th1520_clk_match); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 227b9a4c612e8c..ea1a52e14b8397 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -164,6 +164,7 @@ source "drivers/phy/st/Kconfig" source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" +source "drivers/phy/thead/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/xilinx/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f49d83f00a3d87..4604522548c913 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -48,5 +48,6 @@ obj-$(CONFIG_GENERIC_PHY) += allwinner/ \ starfive/ \ sunplus/ \ tegra/ \ + thead/ \ ti/ \ xilinx/ diff --git a/drivers/phy/thead/Kconfig b/drivers/phy/thead/Kconfig new file mode 100644 index 00000000000000..14012db5973c4b --- /dev/null +++ b/drivers/phy/thead/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config PHY_TH1520_USB + tristate "USB PHY driver for T-Head TH1520 SoC" + depends on ARCH_THEAD || COMPILE_TEST + depends on COMMON_CLK + depends on HAS_IOMEM + depends on OF + depends on RESET_CONTROLLER + select GENERIC_PHY + default ARCH_THEAD + help + Enable support for the USB PHY on the T-Head TH1520 SoC. diff --git a/drivers/phy/thead/Makefile b/drivers/phy/thead/Makefile new file mode 100644 index 00000000000000..5b459bc7004bda --- /dev/null +++ b/drivers/phy/thead/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_PHY_TH1520_USB) += phy-th1520-usb.o diff --git a/drivers/phy/thead/phy-th1520-usb.c b/drivers/phy/thead/phy-th1520-usb.c new file mode 100644 index 00000000000000..c87bd779bbb74b --- /dev/null +++ b/drivers/phy/thead/phy-th1520-usb.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Authors: + * Icenowy Zheng + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_SYSCON_OFFSET 0xf000 + +/* All the below registers are in the USB syscon region */ +#define USB_CLK_GATE_STS 0x0 +#define USB_LOGIC_ANALYZER_TRACE_STS0 0x4 +#define USB_LOGIC_ANALYZER_TRACE_STS1 0x8 +#define USB_GPIO 0xc +#define USB_DEBUG_STS0 0x10 +#define USB_DEBUG_STS1 0x14 +#define USB_DEBUG_STS2 0x18 +#define USBCTL_CLK_CTRL0 0x1c +#define USBPHY_CLK_CTRL1 0x20 +#define USBPHY_TEST_CTRL0 0x24 +#define USBPHY_TEST_CTRL1 0x28 +#define USBPHY_TEST_CTRL2 0x2c +#define USBPHY_TEST_CTRL3 0x30 +#define USB_SSP_EN 0x34 +#define USB_HADDR_SEL 0x38 +#define USB_SYS 0x3c +#define USB_HOST_STATUS 0x40 +#define USB_HOST_CTRL 0x44 +#define USBPHY_HOST_CTRL 0x48 +#define USBPHY_HOST_STATUS 0x4c +#define USB_TEST_REG0 0x50 +#define USB_TEST_REG1 0x54 +#define USB_TEST_REG2 0x58 +#define USB_TEST_REG3 0x5c + +#define USB_SYS_COMMONONN BIT(0) + +#define USB_SSP_EN_REF_SSP_EN BIT(0) + +struct th1520_usb_phy { + struct platform_device *pdev; + struct phy *phy; + struct regmap *regmap; + struct clk *ref_clk; + struct reset_control *phy_reset; +}; + +static int th1520_usb_phy_init(struct phy *phy) +{ + struct th1520_usb_phy *th1520_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(th1520_phy->ref_clk); + if (ret) + return ret; + + ret = reset_control_assert(th1520_phy->phy_reset); + if (ret) + goto err_disable_clk; + + /* + * Do some initial PHY setup: + * - Set COMMONONN to allow the PHY to automatically power down. + * - Set REF_SSP_EN to enable feeding reference clock to SuperSpeed + * PHY clock PLL. + */ + regmap_set_bits(th1520_phy->regmap, USB_SYS, USB_SYS_COMMONONN); + regmap_set_bits(th1520_phy->regmap, USB_SSP_EN, USB_SSP_EN_REF_SSP_EN); + + ret = reset_control_deassert(th1520_phy->phy_reset); + if (ret) + goto err_disable_clk; + + udelay(10); + + return 0; + +err_disable_clk: + clk_disable_unprepare(th1520_phy->ref_clk); + return ret; +} + +static int th1520_usb_phy_exit(struct phy *phy) +{ + struct th1520_usb_phy *th1520_phy = phy_get_drvdata(phy); + int ret; + + ret = reset_control_assert(th1520_phy->phy_reset); + if (ret) + return ret; + + clk_disable_unprepare(th1520_phy->ref_clk); + + return 0; +} + +static const struct phy_ops th1520_usb_phy_ops = { + .init = th1520_usb_phy_init, + .exit = th1520_usb_phy_exit, + .owner = THIS_MODULE, +}; + +static const struct regmap_config phy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_TEST_REG3, +}; + +static int th1520_usb_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct th1520_usb_phy *th1520_phy; + struct reset_control *bus_reset; + void __iomem *base; + int ret; + + th1520_phy = devm_kzalloc(dev, sizeof(*th1520_phy), GFP_KERNEL); + if (!th1520_phy) + return -ENOMEM; + + th1520_phy->pdev = pdev; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + th1520_phy->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(th1520_phy->ref_clk)) + return PTR_ERR(th1520_phy->ref_clk); + + /* De-assert the bus reset and leave it that way */ + bus_reset = devm_reset_control_get_exclusive_deasserted(dev, "bus"); + if (IS_ERR(bus_reset)) + return PTR_ERR(bus_reset); + + th1520_phy->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(th1520_phy->phy_reset)) + return PTR_ERR(th1520_phy->phy_reset); + + /* + * Schematics of several boards (Lichee Module 4A/Milk-V Meles) + * describe this power rail as always-on. + */ + ret = devm_regulator_get_enable(dev, "avdd33-usb3"); + if (ret) + return ret; + + th1520_phy->regmap = devm_regmap_init_mmio_clk(dev, "bus", + base + USB_SYSCON_OFFSET, + &phy_regmap_config); + if (IS_ERR(th1520_phy->regmap)) + return dev_err_probe(dev, PTR_ERR(th1520_phy->regmap), + "Failed to init regmap\n"); + + th1520_phy->phy = devm_phy_create(dev, dev->of_node, &th1520_usb_phy_ops); + if (IS_ERR(th1520_phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(th1520_phy->phy); + } + + phy_set_drvdata(th1520_phy->phy, th1520_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id th1520_usb_phy_of_table[] = { + { .compatible = "thead,th1520-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, th1520_usb_phy_of_table); + +static struct platform_driver th1520_usb_phy_driver = { + .driver = { + .name = "th1520-usb-phy", + .of_match_table = th1520_usb_phy_of_table, + }, + .probe = th1520_usb_phy_probe, +}; + +module_platform_driver(th1520_usb_phy_driver); + +MODULE_DESCRIPTION("T-Head TH1520 USB PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/clock/thead,th1520-clk-ap.h b/include/dt-bindings/clock/thead,th1520-clk-ap.h index 68b35cc6120413..642c2a69a57971 100644 --- a/include/dt-bindings/clock/thead,th1520-clk-ap.h +++ b/include/dt-bindings/clock/thead,th1520-clk-ap.h @@ -128,4 +128,14 @@ #define CLK_MIPIDSI1_PIXCLK 29 #define CLK_HDMI_PIXCLK 30 +/* MISC clocks */ +#define CLK_MISCSYS_ACLK 0 +#define CLK_USB 1 +#define CLK_USB_CTL_REF 2 +#define CLK_USB_PHY_REF 3 +#define CLK_USB_SUSPEND 4 +#define CLK_EMMC 5 +#define CLK_SDIO0 6 +#define CLK_SDIO1 7 + #endif