* [PATCH 0/2] usb: add K1 USB2 PHY and generic DWC3 support
@ 2026-10-06 4:28 Dave Klotz
2026-10-06 4:28 ` [PATCH 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-06 4:28 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
This series adds USB host support for the SpacemiT K1: a driver for
the K1's USB 2.0 (UTMI) PHY, and a flat-node entry for the SoC's DWC3
controller in dwc3-generic, which already handles clocks, resets, PHYs
and the host child for other flat-node SoCs. No K1-specific glue is
needed. USB 3 additionally needs the posted combo PHY series [1].
The patches apply to next at bd2f5794 and to current next (ed869863).
dwc3-generic.c is not touched by the pending DWC3 resync [2], so they
apply there too; the test builds below carried that resync (v9).
Tested on the OrangePi R2S and RV2 on October 5 with next bd2f5794, the
posted K1 clock, MMC, PHY, PCIe and SPL fixes, the DWC3 resync v9 and
these two patches (plus local hub-scan logging and an xHCI stop-path
change that are under separate review and do not change enumeration):
- R2S: a USB 3.0 stick on the root port enumerates at 5 Gb/s. Each
check is two 64 MiB raw reads into differently prefilled buffers,
byte compare and SHA-256 against a FAT copy of the same data; the
initial three pairs, then one pair after each of three 'usb reset'
and after 'usb stop' / 'usb start', all passed at 5 Gb/s with no
host-halt warning.
- RV2: the same stick behind a Genesys USB 3 hub; same checks, same
results. In an earlier run the stick once failed to re-enumerate
after the second and third 'usb reset'; that did not reproduce with
hub scan logging enabled and is being looked at in the generic hub
scan separately. It is not specific to these two patches.
- On both boards the FreeBSD EFI loader read from the stick selects its
own partition unaided and boots the kernel on the stick with the
board's native root (eMMC on R2S, NVMe on RV2).
Sandbox builds and the existing DWC3 flat-node users are unaffected.
[1] https://lists.u-boot-project.org/pipermail/u-boot/2026-October/633775.html
[2] https://lists.u-boot-project.org/pipermail/u-boot/2026-October/633722.html
Dave Klotz (2):
phy: spacemit: add K1 USB 2.0 PHY driver
usb: dwc3: generic: add SpacemiT K1 support
MAINTAINERS | 1 +
drivers/phy/spacemit/Kconfig | 9 ++
drivers/phy/spacemit/Makefile | 1 +
drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
drivers/usb/dwc3/dwc3-generic.c | 13 +--
5 files changed, 191 insertions(+), 6 deletions(-)
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/2] phy: spacemit: add K1 USB 2.0 PHY driver
2026-10-06 4:28 [PATCH 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
@ 2026-10-06 4:28 ` Dave Klotz
2026-10-06 4:28 ` [PATCH 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
2026-10-06 12:31 ` [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2 siblings, 0 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-06 4:28 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
The UTMI PHY at c0a30000 ("spacemit,k1-usb2-phy") feeds the K1's DWC3
USB 3 controller. Port the Linux driver: enable the PHY clock, program
the PLL divider for the 24 MHz reference, wait for the PLL, then release
the PHY's resets and enable its clocks, the HS TX clock and the
automatic host-disconnect clear.
The divider has to be written before the PLL is polled. On the OrangePi
R2S under FreeBSD, a driver that polled first and never wrote it left
the PLL stopped: the clock enables did not latch (0x60ef read back as
0x60e9), the PHY gave the controller no UTMI clock and the xHCI reset
(USBCMD.HCRST) never completed. Warn if the enables do not latch.
Also apply the vendor's HS DAC current setting (+15 %), which the
FreeBSD driver that runs the R2S port does.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
MAINTAINERS | 1 +
drivers/phy/spacemit/Kconfig | 9 ++
drivers/phy/spacemit/Makefile | 1 +
drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
4 files changed, 184 insertions(+)
create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 9e09f9506c..0af42acfbc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1517,6 +1517,7 @@ M: Yixun Lan <dlan@kernel.org>
S: Maintained
T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-spacemit.git
F: arch/riscv/dts/k1-*-u-boot.dtsi
+F: drivers/phy/spacemit/phy-k1-usb2.c
N: spacemit
K: spacemit
diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
index 3c47601c1f..7146965d92 100644
--- a/drivers/phy/spacemit/Kconfig
+++ b/drivers/phy/spacemit/Kconfig
@@ -9,3 +9,12 @@ config PHY_SPACEMIT_K1_PCIE
ports B and C. The combo PHY must be enabled even if port A
is not used for PCIe, because it provides calibration values
required by the other ports.
+
+config PHY_SPACEMIT_K1_USB2
+ bool "SpacemiT K1 USB 2.0 PHY driver"
+ depends on PHY && CLK
+ help
+ Enable this to support the USB 2.0 (UTMI) PHY of the SpacemiT K1
+ SoC, used by its DWC3 USB 3 controller. The driver configures the
+ PLL for the 24 MHz reference clock, releases the internal resets
+ and enables the PHY clocks before the controller is started.
diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
index 3496c099cd..cad47d11bf 100644
--- a/drivers/phy/spacemit/Makefile
+++ b/drivers/phy/spacemit/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o
+obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o
diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c
new file mode 100644
index 0000000000..c4b438219b
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k1-usb2.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SpacemiT K1 USB 2.0 (UTMI) PHY driver
+ *
+ * Ported from the Linux SpacemiT K1 USB 2.0 PHY driver:
+ * Copyright (C) 2025 SpacemiT (Hangzhou) Technology Co. Ltd
+ * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
+ *
+ * The HS DAC current setting and the clock-enable check follow the FreeBSD
+ * spacemit_usb2phy driver (BSD-2-Clause), which brought USB up on the
+ * OrangePi R2S:
+ * Copyright (c) 2026 Daniel Shue <dgshue@gmail.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#define PHY_RST_MODE_CTRL 0x04
+#define PHY_PLL_RDY BIT(0)
+#define PHY_CLK_CDR_EN BIT(1)
+#define PHY_CLK_PLL_EN BIT(2)
+#define PHY_CLK_MAC_EN BIT(3)
+#define PHY_MAC_RSTN BIT(5)
+#define PHY_CDR_RSTN BIT(6)
+#define PHY_PLL_RSTN BIT(7)
+#define PHY_HS_LINE_TX_MODE BIT(13)
+#define PHY_FS_LINE_TX_MODE BIT(14)
+
+#define PHY_INIT_MODE_BITS (PHY_FS_LINE_TX_MODE | PHY_HS_LINE_TX_MODE)
+#define PHY_CLK_ENABLE_BITS (PHY_CLK_PLL_EN | PHY_CLK_CDR_EN | \
+ PHY_CLK_MAC_EN)
+#define PHY_DEASSERT_RST_BITS (PHY_PLL_RSTN | PHY_CDR_RSTN | \
+ PHY_MAC_RSTN)
+
+#define PHY_TX_HOST_CTRL 0x10
+#define PHY_HST_DISC_AUTO_CLR BIT(2)
+
+#define PHY_HSTXP_HW_CTRL 0x34
+#define PHY_HSTXP_RSTN BIT(2)
+#define PHY_CLK_HSTXP_EN BIT(3)
+#define PHY_HSTXP_MODE BIT(4)
+
+#define PHY_PLL_DIV_CFG 0x98
+#define PHY_FDIV_FRACT_8_15 GENMASK(7, 0)
+#define PHY_FDIV_FRACT_16_19 GENMASK(11, 8)
+#define PHY_FDIV_FRACT_20_21 BIT(12)
+#define PHY_FDIV_FRACT_0_1 GENMASK(14, 13)
+#define PHY_DIV_LOCAL_EN BIT(15)
+
+#define PHY_SEL_FREQ_24MHZ 0x01
+#define FDIV_REG_MASK (PHY_FDIV_FRACT_20_21 | PHY_FDIV_FRACT_16_19 | \
+ PHY_FDIV_FRACT_8_15)
+#define FDIV_REG_VAL 0x1ec4
+
+/* Vendor HS DAC current setting: +15 % drive, current regulator on */
+#define PHY_ANALOG_REG14_13 0xa4
+#define PHY_HSDAC_ISEL GENMASK(3, 0)
+#define PHY_HSDAC_ISEL_15_INC 0xc
+#define PHY_HSDAC_IREG_EN BIT(4)
+
+#define K1_USB2PHY_PLL_TIMEOUT_US 50000
+
+struct spacemit_usb2phy {
+ void __iomem *base;
+ struct clk clk;
+};
+
+static int spacemit_usb2phy_init(struct phy *phy)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
+ void __iomem *base = sphy->base;
+ u32 val;
+ int ret;
+
+ ret = clk_enable(&sphy->clk);
+ if (ret) {
+ dev_err(phy->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Let any controller reset settle before touching the PHY */
+ udelay(200);
+
+ /*
+ * Program the PLL divider for the 24 MHz reference first. Until it is
+ * written the PLL does not run, the clock enables below do not latch
+ * and the PHY gives the DWC3 no UTMI clock, so the xHCI reset
+ * (USBCMD.HCRST) never completes. Found on the OrangePi R2S under
+ * FreeBSD, whose earlier driver polled for the PLL before this write.
+ */
+ val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) |
+ FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) |
+ PHY_DIV_LOCAL_EN;
+ writel(val, base + PHY_PLL_DIV_CFG);
+
+ ret = readl_poll_timeout(base + PHY_RST_MODE_CTRL, val,
+ val & PHY_PLL_RDY, K1_USB2PHY_PLL_TIMEOUT_US);
+ if (ret) {
+ dev_err(phy->dev, "PLL lock timeout\n");
+ clk_disable(&sphy->clk);
+ return ret;
+ }
+
+ /* Release the PHY's internal resets and enable its clocks */
+ writel(PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS,
+ base + PHY_RST_MODE_CTRL);
+ val = readl(base + PHY_RST_MODE_CTRL);
+ if ((val & PHY_CLK_ENABLE_BITS) != PHY_CLK_ENABLE_BITS)
+ dev_warn(phy->dev, "clock enables did not latch (%#x)\n", val);
+
+ writel(PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE,
+ base + PHY_HSTXP_HW_CTRL);
+
+ clrsetbits_le32(base + PHY_ANALOG_REG14_13, PHY_HSDAC_ISEL,
+ FIELD_PREP(PHY_HSDAC_ISEL, PHY_HSDAC_ISEL_15_INC) |
+ PHY_HSDAC_IREG_EN);
+
+ /* Auto-clear the HS host disconnect state on reconnect */
+ setbits_le32(base + PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR);
+
+ return 0;
+}
+
+static int spacemit_usb2phy_exit(struct phy *phy)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
+
+ return clk_disable(&sphy->clk);
+}
+
+static const struct phy_ops spacemit_usb2phy_ops = {
+ .init = spacemit_usb2phy_init,
+ .exit = spacemit_usb2phy_exit,
+};
+
+static int spacemit_usb2phy_probe(struct udevice *dev)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(dev);
+ int ret;
+
+ sphy->base = dev_read_addr_ptr(dev);
+ if (!sphy->base)
+ return -EINVAL;
+
+ ret = clk_get_by_index(dev, 0, &sphy->clk);
+ if (ret) {
+ dev_err(dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id spacemit_usb2phy_ids[] = {
+ { .compatible = "spacemit,k1-usb2-phy" },
+ { }
+};
+
+U_BOOT_DRIVER(spacemit_k1_usb2_phy) = {
+ .name = "spacemit_k1_usb2_phy",
+ .id = UCLASS_PHY,
+ .of_match = spacemit_usb2phy_ids,
+ .probe = spacemit_usb2phy_probe,
+ .ops = &spacemit_usb2phy_ops,
+ .priv_auto = sizeof(struct spacemit_usb2phy),
+};
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 2/2] usb: dwc3: generic: add SpacemiT K1 support
2026-10-06 4:28 [PATCH 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-06 4:28 ` [PATCH 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
@ 2026-10-06 4:28 ` Dave Klotz
2026-10-06 5:15 ` Marek Vasut
2026-10-06 12:31 ` [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2 siblings, 1 reply; 17+ messages in thread
From: Dave Klotz @ 2026-10-06 4:28 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
The K1 describes its DWC3 controller as one flat device-tree node with
the controller registers, clocks, resets and PHYs. It has no separate
wrapper registers or controller subnode.
Use the existing flat-node callback so that the generic wrapper binds
its host or peripheral child to the same node. Rename the Rockchip
flat-node ops to reflect that the callback is shared; keep the existing
Rockchip matches on the same callback.
The generic glue handles the K1 controller clocks, resets and USB 3 PHY,
while the child uses the existing generic DWC3 host/peripheral path.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
drivers/usb/dwc3/dwc3-generic.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 2356b3bc0a..489bc8347a 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -515,7 +515,7 @@ static const struct dwc3_glue_ops qcom_flat_dt_ops = {
.glue_get_ctrl_dev = dwc3_flat_dt_get_ctrl_dev,
};
-static const struct dwc3_glue_ops rk_ops = {
+static const struct dwc3_glue_ops flat_dt_ops = {
.glue_get_ctrl_dev = dwc3_flat_dt_get_ctrl_dev,
};
@@ -718,12 +718,13 @@ static const struct udevice_id dwc3_glue_ids[] = {
{ .compatible = "ti,dwc3", .data = (ulong)&ti_ops },
{ .compatible = "ti,am437x-dwc3", .data = (ulong)&ti_ops },
{ .compatible = "ti,am654-dwc3" },
- { .compatible = "rockchip,rk3328-dwc3", .data = (ulong)&rk_ops },
+ { .compatible = "rockchip,rk3328-dwc3", .data = (ulong)&flat_dt_ops },
{ .compatible = "rockchip,rk3399-dwc3" },
- { .compatible = "rockchip,rk3528-dwc3", .data = (ulong)&rk_ops },
- { .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&rk_ops },
- { .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&rk_ops },
- { .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&rk_ops },
+ { .compatible = "rockchip,rk3528-dwc3", .data = (ulong)&flat_dt_ops },
+ { .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&flat_dt_ops },
+ { .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&flat_dt_ops },
+ { .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&flat_dt_ops },
+ { .compatible = "spacemit,k1-dwc3", .data = (ulong)&flat_dt_ops },
{ .compatible = "qcom,dwc3", .data = (ulong)&qcom_ops },
{ .compatible = "qcom,snps-dwc3", .data = (ulong)&qcom_flat_dt_ops },
{ .compatible = "fsl,imx8mp-dwc3", .data = (ulong)&imx8mp_ops },
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH 2/2] usb: dwc3: generic: add SpacemiT K1 support
2026-10-06 4:28 ` [PATCH 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
@ 2026-10-06 5:15 ` Marek Vasut
0 siblings, 0 replies; 17+ messages in thread
From: Marek Vasut @ 2026-10-06 5:15 UTC (permalink / raw)
To: Dave Klotz, Yixun Lan, Marek Vasut
Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
On 10/6/26 6:28 AM, Dave Klotz wrote:
> The K1 describes its DWC3 controller as one flat device-tree node with
> the controller registers, clocks, resets and PHYs. It has no separate
> wrapper registers or controller subnode.
>
> Use the existing flat-node callback so that the generic wrapper binds
> its host or peripheral child to the same node. Rename the Rockchip
> flat-node ops to reflect that the callback is shared; keep the existing
> Rockchip matches on the same callback.
>
> The generic glue handles the K1 controller clocks, resets and USB 3 PHY,
> while the child uses the existing generic DWC3 host/peripheral path.
>
> Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
> ---
> drivers/usb/dwc3/dwc3-generic.c | 13 +++++++------
> 1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
> index 2356b3bc0a..489bc8347a 100644
> --- a/drivers/usb/dwc3/dwc3-generic.c
> +++ b/drivers/usb/dwc3/dwc3-generic.c
> @@ -515,7 +515,7 @@ static const struct dwc3_glue_ops qcom_flat_dt_ops = {
> .glue_get_ctrl_dev = dwc3_flat_dt_get_ctrl_dev,
> };
>
> -static const struct dwc3_glue_ops rk_ops = {
> +static const struct dwc3_glue_ops flat_dt_ops = {
> .glue_get_ctrl_dev = dwc3_flat_dt_get_ctrl_dev,
> };
>
> @@ -718,12 +718,13 @@ static const struct udevice_id dwc3_glue_ids[] = {
> { .compatible = "ti,dwc3", .data = (ulong)&ti_ops },
> { .compatible = "ti,am437x-dwc3", .data = (ulong)&ti_ops },
> { .compatible = "ti,am654-dwc3" },
> - { .compatible = "rockchip,rk3328-dwc3", .data = (ulong)&rk_ops },
> + { .compatible = "rockchip,rk3328-dwc3", .data = (ulong)&flat_dt_ops },
> { .compatible = "rockchip,rk3399-dwc3" },
> - { .compatible = "rockchip,rk3528-dwc3", .data = (ulong)&rk_ops },
> - { .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&rk_ops },
> - { .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&rk_ops },
> - { .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&rk_ops },
> + { .compatible = "rockchip,rk3528-dwc3", .data = (ulong)&flat_dt_ops },
> + { .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&flat_dt_ops },
> + { .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&flat_dt_ops },
> + { .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&flat_dt_ops },
> + { .compatible = "spacemit,k1-dwc3", .data = (ulong)&flat_dt_ops },
The variable rename is unnecessary and makes the change harder to review.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support
2026-10-06 4:28 [PATCH 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-06 4:28 ` [PATCH 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-06 4:28 ` [PATCH 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
@ 2026-10-06 12:31 ` Dave Klotz
2026-10-06 12:32 ` [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
` (2 more replies)
2 siblings, 3 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-06 12:31 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
This series adds USB host support for the SpacemiT K1: a driver for
the K1's USB 2.0 (UTMI) PHY, and a flat-node entry for the SoC's DWC3
controller in dwc3-generic, which already handles clocks, resets, PHYs
and the host child for other flat-node SoCs. No K1-specific glue is
needed. USB 3 additionally needs the posted combo PHY series [1].
The patches apply to next at bd2f5794 and to current next
(ed869863c7e2), after the posted combo PHY series [1].
They also apply with the pending DWC3 resync [2], which does not
change dwc3-generic.c. The existing DWC3 flat-node users keep their
original rk_ops entries.
[1] https://lists.u-boot-project.org/pipermail/u-boot/2026-October/633775.html
[2] https://lists.u-boot-project.org/pipermail/u-boot/2026-October/633722.html
A targeted RISC-V build on current next (ed869863c7e2), with the
posted combo PHY prerequisites and these two patches, compiles the K1
USB2 PHY and generic DWC3 objects without compiler warnings. The
configuration checks pass.
Complete R2S and RV2 firmware builds on current next (ed869863c7e2),
with the maintained K1 prerequisites and these two patches, also pass,
and the compiled generic DWC3 objects retain rk_ops.
With that firmware on both boards, seven 64 MiB raw/FAT read pairs
pass SHA-256 and byte comparison at 5 Gb/s, including reads after each
of three USB resets and after a stop/start cycle. Automatic USB EFI
selection and boot of the USB-loaded FreeBSD kernel with the board's
native root also pass. Cold-boot, full USB-root and sustained testing
remain open.
Changes in v2:
- Drop the rk_ops rename from patch 2/2, as requested by Marek Vasut.
Add only the spacemit,k1-dwc3 compatible entry using the existing rk_ops.
- Patch 1/2 is unchanged.
Dave Klotz (2):
phy: spacemit: add K1 USB 2.0 PHY driver
usb: dwc3: generic: add SpacemiT K1 support
MAINTAINERS | 1 +
drivers/phy/spacemit/Kconfig | 9 ++
drivers/phy/spacemit/Makefile | 1 +
drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
drivers/usb/dwc3/dwc3-generic.c | 1 +
5 files changed, 185 insertions(+)
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver
2026-10-06 12:31 ` [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
@ 2026-10-06 12:32 ` Dave Klotz
2026-10-12 9:04 ` Yixun Lan
2026-10-12 9:37 ` Yixun Lan
2026-10-06 12:32 ` [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
2026-10-14 19:59 ` [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2 siblings, 2 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-06 12:32 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
The UTMI PHY at c0a30000 ("spacemit,k1-usb2-phy") feeds the K1's DWC3
USB 3 controller. Port the Linux driver: enable the PHY clock, program
the PLL divider for the 24 MHz reference, wait for the PLL, then release
the PHY's resets and enable its clocks, the HS TX clock and the
automatic host-disconnect clear.
The divider has to be written before the PLL is polled. On the OrangePi
R2S under FreeBSD, a driver that polled first and never wrote it left
the PLL stopped: the clock enables did not latch (0x60ef read back as
0x60e9), the PHY gave the controller no UTMI clock and the xHCI reset
(USBCMD.HCRST) never completed. Warn if the enables do not latch.
Also apply the vendor's HS DAC current setting (+15 %), which the
FreeBSD driver that runs the R2S port does.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
MAINTAINERS | 1 +
drivers/phy/spacemit/Kconfig | 9 ++
drivers/phy/spacemit/Makefile | 1 +
drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
4 files changed, 184 insertions(+)
create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 9e09f9506c..0af42acfbc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1517,6 +1517,7 @@ M: Yixun Lan <dlan@kernel.org>
S: Maintained
T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-spacemit.git
F: arch/riscv/dts/k1-*-u-boot.dtsi
+F: drivers/phy/spacemit/phy-k1-usb2.c
N: spacemit
K: spacemit
diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
index 3c47601c1f..7146965d92 100644
--- a/drivers/phy/spacemit/Kconfig
+++ b/drivers/phy/spacemit/Kconfig
@@ -9,3 +9,12 @@ config PHY_SPACEMIT_K1_PCIE
ports B and C. The combo PHY must be enabled even if port A
is not used for PCIe, because it provides calibration values
required by the other ports.
+
+config PHY_SPACEMIT_K1_USB2
+ bool "SpacemiT K1 USB 2.0 PHY driver"
+ depends on PHY && CLK
+ help
+ Enable this to support the USB 2.0 (UTMI) PHY of the SpacemiT K1
+ SoC, used by its DWC3 USB 3 controller. The driver configures the
+ PLL for the 24 MHz reference clock, releases the internal resets
+ and enables the PHY clocks before the controller is started.
diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
index 3496c099cd..cad47d11bf 100644
--- a/drivers/phy/spacemit/Makefile
+++ b/drivers/phy/spacemit/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o
+obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o
diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c
new file mode 100644
index 0000000000..c4b438219b
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k1-usb2.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SpacemiT K1 USB 2.0 (UTMI) PHY driver
+ *
+ * Ported from the Linux SpacemiT K1 USB 2.0 PHY driver:
+ * Copyright (C) 2025 SpacemiT (Hangzhou) Technology Co. Ltd
+ * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
+ *
+ * The HS DAC current setting and the clock-enable check follow the FreeBSD
+ * spacemit_usb2phy driver (BSD-2-Clause), which brought USB up on the
+ * OrangePi R2S:
+ * Copyright (c) 2026 Daniel Shue <dgshue@gmail.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#define PHY_RST_MODE_CTRL 0x04
+#define PHY_PLL_RDY BIT(0)
+#define PHY_CLK_CDR_EN BIT(1)
+#define PHY_CLK_PLL_EN BIT(2)
+#define PHY_CLK_MAC_EN BIT(3)
+#define PHY_MAC_RSTN BIT(5)
+#define PHY_CDR_RSTN BIT(6)
+#define PHY_PLL_RSTN BIT(7)
+#define PHY_HS_LINE_TX_MODE BIT(13)
+#define PHY_FS_LINE_TX_MODE BIT(14)
+
+#define PHY_INIT_MODE_BITS (PHY_FS_LINE_TX_MODE | PHY_HS_LINE_TX_MODE)
+#define PHY_CLK_ENABLE_BITS (PHY_CLK_PLL_EN | PHY_CLK_CDR_EN | \
+ PHY_CLK_MAC_EN)
+#define PHY_DEASSERT_RST_BITS (PHY_PLL_RSTN | PHY_CDR_RSTN | \
+ PHY_MAC_RSTN)
+
+#define PHY_TX_HOST_CTRL 0x10
+#define PHY_HST_DISC_AUTO_CLR BIT(2)
+
+#define PHY_HSTXP_HW_CTRL 0x34
+#define PHY_HSTXP_RSTN BIT(2)
+#define PHY_CLK_HSTXP_EN BIT(3)
+#define PHY_HSTXP_MODE BIT(4)
+
+#define PHY_PLL_DIV_CFG 0x98
+#define PHY_FDIV_FRACT_8_15 GENMASK(7, 0)
+#define PHY_FDIV_FRACT_16_19 GENMASK(11, 8)
+#define PHY_FDIV_FRACT_20_21 BIT(12)
+#define PHY_FDIV_FRACT_0_1 GENMASK(14, 13)
+#define PHY_DIV_LOCAL_EN BIT(15)
+
+#define PHY_SEL_FREQ_24MHZ 0x01
+#define FDIV_REG_MASK (PHY_FDIV_FRACT_20_21 | PHY_FDIV_FRACT_16_19 | \
+ PHY_FDIV_FRACT_8_15)
+#define FDIV_REG_VAL 0x1ec4
+
+/* Vendor HS DAC current setting: +15 % drive, current regulator on */
+#define PHY_ANALOG_REG14_13 0xa4
+#define PHY_HSDAC_ISEL GENMASK(3, 0)
+#define PHY_HSDAC_ISEL_15_INC 0xc
+#define PHY_HSDAC_IREG_EN BIT(4)
+
+#define K1_USB2PHY_PLL_TIMEOUT_US 50000
+
+struct spacemit_usb2phy {
+ void __iomem *base;
+ struct clk clk;
+};
+
+static int spacemit_usb2phy_init(struct phy *phy)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
+ void __iomem *base = sphy->base;
+ u32 val;
+ int ret;
+
+ ret = clk_enable(&sphy->clk);
+ if (ret) {
+ dev_err(phy->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Let any controller reset settle before touching the PHY */
+ udelay(200);
+
+ /*
+ * Program the PLL divider for the 24 MHz reference first. Until it is
+ * written the PLL does not run, the clock enables below do not latch
+ * and the PHY gives the DWC3 no UTMI clock, so the xHCI reset
+ * (USBCMD.HCRST) never completes. Found on the OrangePi R2S under
+ * FreeBSD, whose earlier driver polled for the PLL before this write.
+ */
+ val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) |
+ FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) |
+ PHY_DIV_LOCAL_EN;
+ writel(val, base + PHY_PLL_DIV_CFG);
+
+ ret = readl_poll_timeout(base + PHY_RST_MODE_CTRL, val,
+ val & PHY_PLL_RDY, K1_USB2PHY_PLL_TIMEOUT_US);
+ if (ret) {
+ dev_err(phy->dev, "PLL lock timeout\n");
+ clk_disable(&sphy->clk);
+ return ret;
+ }
+
+ /* Release the PHY's internal resets and enable its clocks */
+ writel(PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS,
+ base + PHY_RST_MODE_CTRL);
+ val = readl(base + PHY_RST_MODE_CTRL);
+ if ((val & PHY_CLK_ENABLE_BITS) != PHY_CLK_ENABLE_BITS)
+ dev_warn(phy->dev, "clock enables did not latch (%#x)\n", val);
+
+ writel(PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE,
+ base + PHY_HSTXP_HW_CTRL);
+
+ clrsetbits_le32(base + PHY_ANALOG_REG14_13, PHY_HSDAC_ISEL,
+ FIELD_PREP(PHY_HSDAC_ISEL, PHY_HSDAC_ISEL_15_INC) |
+ PHY_HSDAC_IREG_EN);
+
+ /* Auto-clear the HS host disconnect state on reconnect */
+ setbits_le32(base + PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR);
+
+ return 0;
+}
+
+static int spacemit_usb2phy_exit(struct phy *phy)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
+
+ return clk_disable(&sphy->clk);
+}
+
+static const struct phy_ops spacemit_usb2phy_ops = {
+ .init = spacemit_usb2phy_init,
+ .exit = spacemit_usb2phy_exit,
+};
+
+static int spacemit_usb2phy_probe(struct udevice *dev)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(dev);
+ int ret;
+
+ sphy->base = dev_read_addr_ptr(dev);
+ if (!sphy->base)
+ return -EINVAL;
+
+ ret = clk_get_by_index(dev, 0, &sphy->clk);
+ if (ret) {
+ dev_err(dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id spacemit_usb2phy_ids[] = {
+ { .compatible = "spacemit,k1-usb2-phy" },
+ { }
+};
+
+U_BOOT_DRIVER(spacemit_k1_usb2_phy) = {
+ .name = "spacemit_k1_usb2_phy",
+ .id = UCLASS_PHY,
+ .of_match = spacemit_usb2phy_ids,
+ .probe = spacemit_usb2phy_probe,
+ .ops = &spacemit_usb2phy_ops,
+ .priv_auto = sizeof(struct spacemit_usb2phy),
+};
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support
2026-10-06 12:31 ` [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-06 12:32 ` [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
@ 2026-10-06 12:32 ` Dave Klotz
2026-10-12 9:38 ` Marek Vasut
2026-10-12 12:27 ` Yixun Lan
2026-10-14 19:59 ` [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2 siblings, 2 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-06 12:32 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
The K1 describes its DWC3 controller as one flat device-tree node with
the controller registers, clocks, resets and PHYs. It has no separate
wrapper registers or controller subnode.
Use the existing flat-node callback through rk_ops so that the generic
wrapper binds its host or peripheral child to the same node.
The generic glue handles the K1 controller clocks, resets and USB 3 PHY,
while the child uses the existing generic DWC3 host/peripheral path.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
drivers/usb/dwc3/dwc3-generic.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 2356b3bc0a..cae627b587 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -724,6 +724,7 @@ static const struct udevice_id dwc3_glue_ids[] = {
{ .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&rk_ops },
{ .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&rk_ops },
{ .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&rk_ops },
+ { .compatible = "spacemit,k1-dwc3", .data = (ulong)&rk_ops },
{ .compatible = "qcom,dwc3", .data = (ulong)&qcom_ops },
{ .compatible = "qcom,snps-dwc3", .data = (ulong)&qcom_flat_dt_ops },
{ .compatible = "fsl,imx8mp-dwc3", .data = (ulong)&imx8mp_ops },
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver
2026-10-06 12:32 ` [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
@ 2026-10-12 9:04 ` Yixun Lan
2026-10-12 12:17 ` Yixun Lan
2026-10-12 9:37 ` Yixun Lan
1 sibling, 1 reply; 17+ messages in thread
From: Yixun Lan @ 2026-10-12 9:04 UTC (permalink / raw)
To: Dave Klotz; +Cc: Marek Vasut, u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
Hi Dave,
On 12:32 Tue 06 Oct , Dave Klotz wrote:
> The UTMI PHY at c0a30000 ("spacemit,k1-usb2-phy") feeds the K1's DWC3
> USB 3 controller. Port the Linux driver: enable the PHY clock, program
> the PLL divider for the 24 MHz reference, wait for the PLL, then release
> the PHY's resets and enable its clocks, the HS TX clock and the
> automatic host-disconnect clear.
>
..
> The divider has to be written before the PLL is polled. On the OrangePi
Right, the upstream kernel driver also did this way
> R2S under FreeBSD, a driver that polled first and never wrote it left
> the PLL stopped: the clock enables did not latch (0x60ef read back as
Sorry, I've not fully understood here, "polled first and never write"..
Could you elaborate more on this?
> 0x60e9), the PHY gave the controller no UTMI clock and the xHCI reset
> (USBCMD.HCRST) never completed. Warn if the enables do not latch.
Will comment at code below
>
> Also apply the vendor's HS DAC current setting (+15 %), which the
> FreeBSD driver that runs the R2S port does.
>
Any actual problem without above settings? I'm just curious,
as upstream code miss this part
> Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
> ---
> MAINTAINERS | 1 +
> drivers/phy/spacemit/Kconfig | 9 ++
> drivers/phy/spacemit/Makefile | 1 +
> drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
> 4 files changed, 184 insertions(+)
> create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9e09f9506c..0af42acfbc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1517,6 +1517,7 @@ M: Yixun Lan <dlan@kernel.org>
> S: Maintained
> T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-spacemit.git
> F: arch/riscv/dts/k1-*-u-boot.dtsi
> +F: drivers/phy/spacemit/phy-k1-usb2.c
> N: spacemit
> K: spacemit
>
> diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
> index 3c47601c1f..7146965d92 100644
> --- a/drivers/phy/spacemit/Kconfig
> +++ b/drivers/phy/spacemit/Kconfig
> @@ -9,3 +9,12 @@ config PHY_SPACEMIT_K1_PCIE
> ports B and C. The combo PHY must be enabled even if port A
> is not used for PCIe, because it provides calibration values
> required by the other ports.
> +
> +config PHY_SPACEMIT_K1_USB2
> + bool "SpacemiT K1 USB 2.0 PHY driver"
> + depends on PHY && CLK
> + help
> + Enable this to support the USB 2.0 (UTMI) PHY of the SpacemiT K1
> + SoC, used by its DWC3 USB 3 controller. The driver configures the
> + PLL for the 24 MHz reference clock, releases the internal resets
> + and enables the PHY clocks before the controller is started.
> diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
> index 3496c099cd..cad47d11bf 100644
> --- a/drivers/phy/spacemit/Makefile
> +++ b/drivers/phy/spacemit/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o
> +obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o
> diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c
> new file mode 100644
> index 0000000000..c4b438219b
> --- /dev/null
> +++ b/drivers/phy/spacemit/phy-k1-usb2.c
> @@ -0,0 +1,173 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SpacemiT K1 USB 2.0 (UTMI) PHY driver
> + *
> + * Ported from the Linux SpacemiT K1 USB 2.0 PHY driver:
> + * Copyright (C) 2025 SpacemiT (Hangzhou) Technology Co. Ltd
> + * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
> + *
> + * The HS DAC current setting and the clock-enable check follow the FreeBSD
> + * spacemit_usb2phy driver (BSD-2-Clause), which brought USB up on the
> + * OrangePi R2S:
> + * Copyright (c) 2026 Daniel Shue <dgshue@gmail.com>
> + */
> +
> +#include <clk.h>
> +#include <dm.h>
> +#include <generic-phy.h>
> +#include <asm/io.h>
> +#include <dm/device_compat.h>
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +
> +#define PHY_RST_MODE_CTRL 0x04
> +#define PHY_PLL_RDY BIT(0)
> +#define PHY_CLK_CDR_EN BIT(1)
> +#define PHY_CLK_PLL_EN BIT(2)
> +#define PHY_CLK_MAC_EN BIT(3)
> +#define PHY_MAC_RSTN BIT(5)
> +#define PHY_CDR_RSTN BIT(6)
> +#define PHY_PLL_RSTN BIT(7)
> +#define PHY_HS_LINE_TX_MODE BIT(13)
> +#define PHY_FS_LINE_TX_MODE BIT(14)
> +
> +#define PHY_INIT_MODE_BITS (PHY_FS_LINE_TX_MODE | PHY_HS_LINE_TX_MODE)
> +#define PHY_CLK_ENABLE_BITS (PHY_CLK_PLL_EN | PHY_CLK_CDR_EN | \
> + PHY_CLK_MAC_EN)
> +#define PHY_DEASSERT_RST_BITS (PHY_PLL_RSTN | PHY_CDR_RSTN | \
> + PHY_MAC_RSTN)
> +
> +#define PHY_TX_HOST_CTRL 0x10
> +#define PHY_HST_DISC_AUTO_CLR BIT(2)
> +
> +#define PHY_HSTXP_HW_CTRL 0x34
> +#define PHY_HSTXP_RSTN BIT(2)
> +#define PHY_CLK_HSTXP_EN BIT(3)
> +#define PHY_HSTXP_MODE BIT(4)
> +
> +#define PHY_PLL_DIV_CFG 0x98
> +#define PHY_FDIV_FRACT_8_15 GENMASK(7, 0)
> +#define PHY_FDIV_FRACT_16_19 GENMASK(11, 8)
> +#define PHY_FDIV_FRACT_20_21 BIT(12)
> +#define PHY_FDIV_FRACT_0_1 GENMASK(14, 13)
> +#define PHY_DIV_LOCAL_EN BIT(15)
> +
> +#define PHY_SEL_FREQ_24MHZ 0x01
> +#define FDIV_REG_MASK (PHY_FDIV_FRACT_20_21 | PHY_FDIV_FRACT_16_19 | \
> + PHY_FDIV_FRACT_8_15)
> +#define FDIV_REG_VAL 0x1ec4
> +
> +/* Vendor HS DAC current setting: +15 % drive, current regulator on */
> +#define PHY_ANALOG_REG14_13 0xa4
> +#define PHY_HSDAC_ISEL GENMASK(3, 0)
> +#define PHY_HSDAC_ISEL_15_INC 0xc
> +#define PHY_HSDAC_IREG_EN BIT(4)
> +
> +#define K1_USB2PHY_PLL_TIMEOUT_US 50000
> +
> +struct spacemit_usb2phy {
> + void __iomem *base;
> + struct clk clk;
> +};
> +
> +static int spacemit_usb2phy_init(struct phy *phy)
> +{
> + struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
> + void __iomem *base = sphy->base;
> + u32 val;
> + int ret;
> +
> + ret = clk_enable(&sphy->clk);
> + if (ret) {
> + dev_err(phy->dev, "failed to enable clock: %d\n", ret);
> + return ret;
> + }
> +
> + /* Let any controller reset settle before touching the PHY */
> + udelay(200);
> +
> + /*
> + * Program the PLL divider for the 24 MHz reference first. Until it is
> + * written the PLL does not run, the clock enables below do not latch
> + * and the PHY gives the DWC3 no UTMI clock, so the xHCI reset
> + * (USBCMD.HCRST) never completes. Found on the OrangePi R2S under
> + * FreeBSD, whose earlier driver polled for the PLL before this write.
> + */
> + val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) |
> + FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) |
> + PHY_DIV_LOCAL_EN;
> + writel(val, base + PHY_PLL_DIV_CFG);
> +
> + ret = readl_poll_timeout(base + PHY_RST_MODE_CTRL, val,
> + val & PHY_PLL_RDY, K1_USB2PHY_PLL_TIMEOUT_US);
> + if (ret) {
> + dev_err(phy->dev, "PLL lock timeout\n");
> + clk_disable(&sphy->clk);
> + return ret;
> + }
> +
> + /* Release the PHY's internal resets and enable its clocks */
> + writel(PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS,
> + base + PHY_RST_MODE_CTRL);
..
> + val = readl(base + PHY_RST_MODE_CTRL);
> + if ((val & PHY_CLK_ENABLE_BITS) != PHY_CLK_ENABLE_BITS)
> + dev_warn(phy->dev, "clock enables did not latch (%#x)\n", val);
So read back and check.. if this situation is happening, a warning
message won't save life.
But, I need to check if the "enable bits" actually reflect the clock
enabling status from the hardware perspective.. but even leave that alone,
I think using a poll() instead of a readl() is a more proper way to go?
usually it will take few time for the hardware to settle down..
> +
> + writel(PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE,
> + base + PHY_HSTXP_HW_CTRL);
> +
> + clrsetbits_le32(base + PHY_ANALOG_REG14_13, PHY_HSDAC_ISEL,
> + FIELD_PREP(PHY_HSDAC_ISEL, PHY_HSDAC_ISEL_15_INC) |
> + PHY_HSDAC_IREG_EN);
> +
> + /* Auto-clear the HS host disconnect state on reconnect */
> + setbits_le32(base + PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR);
> +
> + return 0;
> +}
--
Yixun Lan (dlan)
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver
2026-10-06 12:32 ` [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-12 9:04 ` Yixun Lan
@ 2026-10-12 9:37 ` Yixun Lan
1 sibling, 0 replies; 17+ messages in thread
From: Yixun Lan @ 2026-10-12 9:37 UTC (permalink / raw)
To: Dave Klotz; +Cc: Marek Vasut, u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
Hi Dave,
On 12:32 Tue 06 Oct , Dave Klotz wrote:
> The UTMI PHY at c0a30000 ("spacemit,k1-usb2-phy") feeds the K1's DWC3
> USB 3 controller. Port the Linux driver: enable the PHY clock, program
> the PLL divider for the 24 MHz reference, wait for the PLL, then release
> the PHY's resets and enable its clocks, the HS TX clock and the
> automatic host-disconnect clear.
>
> The divider has to be written before the PLL is polled. On the OrangePi
> R2S under FreeBSD, a driver that polled first and never wrote it left
> the PLL stopped: the clock enables did not latch (0x60ef read back as
> 0x60e9), the PHY gave the controller no UTMI clock and the xHCI reset
> (USBCMD.HCRST) never completed. Warn if the enables do not latch.
>
> Also apply the vendor's HS DAC current setting (+15 %), which the
> FreeBSD driver that runs the R2S port does.
>
> Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
> ---
> MAINTAINERS | 1 +
> drivers/phy/spacemit/Kconfig | 9 ++
> drivers/phy/spacemit/Makefile | 1 +
> drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
> 4 files changed, 184 insertions(+)
> create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
>
..
> +
> +static const struct udevice_id spacemit_usb2phy_ids[] = {
> + { .compatible = "spacemit,k1-usb2-phy" },
Since K3 SoC share this IP with minor difference which we don't
care in U-Boot, and also I've tested, it works fine on K3 SoC,
so please add in next version, thanks
{ .compatible = "spacemit,k3-usb2-phy" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(spacemit_k1_usb2_phy) = {
> + .name = "spacemit_k1_usb2_phy",
> + .id = UCLASS_PHY,
> + .of_match = spacemit_usb2phy_ids,
> + .probe = spacemit_usb2phy_probe,
> + .ops = &spacemit_usb2phy_ops,
> + .priv_auto = sizeof(struct spacemit_usb2phy),
> +};
>
--
Yixun Lan (dlan)
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support
2026-10-06 12:32 ` [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
@ 2026-10-12 9:38 ` Marek Vasut
2026-10-12 12:27 ` Yixun Lan
1 sibling, 0 replies; 17+ messages in thread
From: Marek Vasut @ 2026-10-12 9:38 UTC (permalink / raw)
To: Dave Klotz, Yixun Lan; +Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
On 10/6/26 2:32 PM, Dave Klotz wrote:
> The K1 describes its DWC3 controller as one flat device-tree node with
> the controller registers, clocks, resets and PHYs. It has no separate
> wrapper registers or controller subnode.
>
> Use the existing flat-node callback through rk_ops so that the generic
> wrapper binds its host or peripheral child to the same node.
>
> The generic glue handles the K1 controller clocks, resets and USB 3 PHY,
> while the child uses the existing generic DWC3 host/peripheral path.
>
> Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
Reviewed-by: Marek Vasut <marek.vasut+usb@mailbox.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver
2026-10-12 9:04 ` Yixun Lan
@ 2026-10-12 12:17 ` Yixun Lan
0 siblings, 0 replies; 17+ messages in thread
From: Yixun Lan @ 2026-10-12 12:17 UTC (permalink / raw)
To: Dave Klotz; +Cc: Marek Vasut, u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
Hi Dave,
On 09:04 Mon 12 Oct , Yixun Lan wrote:
> Hi Dave,
>
> On 12:32 Tue 06 Oct , Dave Klotz wrote:
> > The UTMI PHY at c0a30000 ("spacemit,k1-usb2-phy") feeds the K1's DWC3
> > USB 3 controller. Port the Linux driver: enable the PHY clock, program
> > the PLL divider for the 24 MHz reference, wait for the PLL, then release
> > the PHY's resets and enable its clocks, the HS TX clock and the
> > automatic host-disconnect clear.
> >
> ..
> > The divider has to be written before the PLL is polled. On the OrangePi
> Right, the upstream kernel driver also did this way
>
> > R2S under FreeBSD, a driver that polled first and never wrote it left
> > the PLL stopped: the clock enables did not latch (0x60ef read back as
> Sorry, I've not fully understood here, "polled first and never write"..
> Could you elaborate more on this?
>
> > 0x60e9), the PHY gave the controller no UTMI clock and the xHCI reset
> > (USBCMD.HCRST) never completed. Warn if the enables do not latch.
> Will comment at code below
>
> >
> > Also apply the vendor's HS DAC current setting (+15 %), which the
> > FreeBSD driver that runs the R2S port does.
> >
> Any actual problem without above settings? I'm just curious,
> as upstream code miss this part
>
> > Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
> > ---
> > MAINTAINERS | 1 +
> > drivers/phy/spacemit/Kconfig | 9 ++
> > drivers/phy/spacemit/Makefile | 1 +
> > drivers/phy/spacemit/phy-k1-usb2.c | 173 +++++++++++++++++++++++++++++
> > 4 files changed, 184 insertions(+)
> > create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
> >
> > +
..
> > + /* Release the PHY's internal resets and enable its clocks */
> > + writel(PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS,
> > + base + PHY_RST_MODE_CTRL);
> ..
> > + val = readl(base + PHY_RST_MODE_CTRL);
> > + if ((val & PHY_CLK_ENABLE_BITS) != PHY_CLK_ENABLE_BITS)
> > + dev_warn(phy->dev, "clock enables did not latch (%#x)\n", val);
> So read back and check.. if this situation is happening, a warning
> message won't save life.
>
> But, I need to check if the "enable bits" actually reflect the clock
> enabling status from the hardware perspective.. but even leave that alone,
> I think using a poll() instead of a readl() is a more proper way to go?
> usually it will take few time for the hardware to settle down..
>
I've asked SpacemiT internal to check, and got this info:
Setting "enable bits" will take effect immediately, it doesn't reflect
the clock status, so this "readl() & check" isn't useful, drop it
If you still see problem, then probably need investigate other way..
> > +
> > + writel(PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE,
> > + base + PHY_HSTXP_HW_CTRL);
> > +
> > + clrsetbits_le32(base + PHY_ANALOG_REG14_13, PHY_HSDAC_ISEL,
> > + FIELD_PREP(PHY_HSDAC_ISEL, PHY_HSDAC_ISEL_15_INC) |
> > + PHY_HSDAC_IREG_EN);
> > +
> > + /* Auto-clear the HS host disconnect state on reconnect */
> > + setbits_le32(base + PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR);
> > +
> > + return 0;
> > +}
--
Yixun Lan (dlan)
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support
2026-10-06 12:32 ` [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
2026-10-12 9:38 ` Marek Vasut
@ 2026-10-12 12:27 ` Yixun Lan
2026-10-14 19:59 ` Dave Klotz
1 sibling, 1 reply; 17+ messages in thread
From: Yixun Lan @ 2026-10-12 12:27 UTC (permalink / raw)
To: Dave Klotz; +Cc: Marek Vasut, u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
Hi Dave,
On 12:32 Tue 06 Oct , Dave Klotz wrote:
> The K1 describes its DWC3 controller as one flat device-tree node with
> the controller registers, clocks, resets and PHYs. It has no separate
> wrapper registers or controller subnode.
>
> Use the existing flat-node callback through rk_ops so that the generic
> wrapper binds its host or peripheral child to the same node.
>
> The generic glue handles the K1 controller clocks, resets and USB 3 PHY,
> while the child uses the existing generic DWC3 host/peripheral path.
>
https://github.com/torvalds/linux/commit/764c2e6e60bf17910d84e7179fee14129e053b96
"usb: dwc3: Add optional VBUS regulator support to SpacemiT K1"
Can you check above (for board OrangePi R2S), see if need to implement
similar support in U-Boot? I'd accept this patch if the regulator is
enabled by default, can leave us to improve later..
> Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
> ---
> drivers/usb/dwc3/dwc3-generic.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
> index 2356b3bc0a..cae627b587 100644
> --- a/drivers/usb/dwc3/dwc3-generic.c
> +++ b/drivers/usb/dwc3/dwc3-generic.c
> @@ -724,6 +724,7 @@ static const struct udevice_id dwc3_glue_ids[] = {
> { .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&rk_ops },
> { .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&rk_ops },
> { .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&rk_ops },
> + { .compatible = "spacemit,k1-dwc3", .data = (ulong)&rk_ops },
> { .compatible = "qcom,dwc3", .data = (ulong)&qcom_ops },
> { .compatible = "qcom,snps-dwc3", .data = (ulong)&qcom_flat_dt_ops },
> { .compatible = "fsl,imx8mp-dwc3", .data = (ulong)&imx8mp_ops },
>
--
Yixun Lan (dlan)
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support
2026-10-12 12:27 ` Yixun Lan
@ 2026-10-14 19:59 ` Dave Klotz
0 siblings, 0 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-14 19:59 UTC (permalink / raw)
To: Yixun Lan; +Cc: Marek Vasut, u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit
Hi Yixun,
On Mon, Oct 12, 2026 at 12:27 UTC, Yixun Lan wrote:
> https://github.com/torvalds/linux/commit/764c2e6e60bf17910d84e7179fee14129e053b96
> "usb: dwc3: Add optional VBUS regulator support to SpacemiT K1"
>
> Can you check above (for board OrangePi R2S), see if need to implement
> similar support in U-Boot? I'd accept this patch if the regulator is
> enabled by default, can leave us to improve later..
On the R2S the USB 3 port's VBUS is switched. The DT describes it as
vcc5v0_usb, a fixed regulator enabled by GPIO 126 (active high), with
no regulator-always-on or regulator-boot-on, and &usb_dwc3 has
vbus-supply = <&vcc5v0_usb>. U-Boot's dts/upstream copy of
k1-orangepi-r2s.dts has the same. So as far as the DT is concerned it
is not on by default. I don't have a schematic, so I can't say what
the pin does out of reset.
U-Boot doesn't need an equivalent of 764c2e6e, though.
dwc3_generic_host_probe() already gets "vbus-supply" and calls
regulator_set_enable_if_allowed() before xhci_register(), and
dwc3_generic_host_remove() turns it off again. With rk_ops the host
child is bound to the usb@c0a00000 node itself, so it finds the
property. A board without vbus-supply is unaffected, as with Linux's
optional regulator. The board needs CONFIG_DM_REGULATOR_FIXED,
which v3 enables in spacemit_k1_defconfig along with the USB host
options; DM_REGULATOR and SPACEMIT_GPIO are already there.
My v2 tests on the R2S ran that way: the firmware had
CONFIG_DM_REGULATOR_FIXED=y and the upstream R2S DT, and USB storage
enumerated and read at 5 Gb/s. I didn't log the regulator enable
itself. Under FreeBSD on the same board, the port only saw a device
once GPIO 126 was driven high, so I'd expect U-Boot to depend on it
as well.
So 2/2 doesn't need a change for VBUS. v3 will carry Marek's tag, your
1/2 comments, and a new patch enabling USB host in spacemit_k1_defconfig.
Thanks,
Dave
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support
2026-10-06 12:31 ` [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-06 12:32 ` [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-06 12:32 ` [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
@ 2026-10-14 19:59 ` Dave Klotz
2026-10-14 20:00 ` [PATCH v3 1/3] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
` (2 more replies)
2 siblings, 3 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-14 19:59 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut
Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit, Eric Chung,
Huan Zhou, Guodong Xu, Raymond Mao
This series adds USB host support for the SpacemiT K1: a driver for
the K1's USB 2.0 (UTMI) PHY, which the K3 shares, a flat-node entry
for the K1's DWC3 controller in dwc3-generic, which already handles
clocks, resets, PHYs, VBUS and the host child for other flat-node SoCs,
and the defconfig options to use them.
The v2 USB 3 tests also used my combo PHY series [1]. This series
doesn't depend on it, and USB 3 on main without it is untested.
Yixun asked what "polled first and never write" meant in v2 1/2. It
described an earlier FreeBSD driver, not this patch. That driver waited
for PLL_RDY without ever writing PHY_PLL_DIV_CFG, so on the OrangePi
R2S the PLL never locked and the xHCI reset never completed. This
driver writes the divider first, as Linux does, so v3 keeps only that
reason in the commit message.
On VBUS (v2 2/2): the dwc3-generic host driver already gets
vbus-supply and enables it before registering the xHCI controller,
which is what Linux commit 764c2e6e60bf adds for the K1. The R2S
device tree describes VBUS as a GPIO-switched fixed regulator, so the
board needs CONFIG_DM_REGULATOR_FIXED, which 3/3 enables, but no code.
Details are in my reply to v2 2/2.
Changes in v3:
- 1/3: drop the read-back check of the clock enable bits, drop the HS
DAC +15 % setting and match "spacemit,k3-usb2-phy".
- 2/3: add Marek's Reviewed-by; also match "spacemit,k3-dwc3", which
shares the K1's flat-node layout (not tested on K3).
- 3/3: new; enable USB host and DM_REGULATOR_FIXED in
spacemit_k1_defconfig.
- Rebase onto main.
Testing: v3 has only been build-tested. A RISC-V build of main with
the updated spacemit_k1_defconfig compiles the changed driver files
with W=1 and no warnings, and builds SPL and U-Boot proper (the FIT
image needs OpenSBI's fw_dynamic.bin, as before). checkpatch reports
no issues.
v3 has not been run on hardware. The v2 R2S and RV2 USB results were
taken with the HS DAC setting that v3 drops, so they do not cover v3.
[1] https://lists.u-boot-project.org/pipermail/u-boot/2026-October/633775.html
Dave Klotz (3):
phy: spacemit: add K1 USB 2.0 PHY driver
usb: dwc3: generic: add SpacemiT K1 and K3 support
configs: spacemit_k1: enable USB host
MAINTAINERS | 1 +
configs/spacemit_k1_defconfig | 8 ++
drivers/phy/spacemit/Kconfig | 10 ++
drivers/phy/spacemit/Makefile | 1 +
drivers/phy/spacemit/phy-k1-usb2.c | 153 +++++++++++++++++++++++++++++
drivers/usb/dwc3/dwc3-generic.c | 2 +
6 files changed, 175 insertions(+)
create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
base-commit: 32ebe922e574a733d01c3f106fa77d9176bfecad
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 1/3] phy: spacemit: add K1 USB 2.0 PHY driver
2026-10-14 19:59 ` [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
@ 2026-10-14 20:00 ` Dave Klotz
2026-10-14 20:00 ` [PATCH v3 2/3] usb: dwc3: generic: add SpacemiT K1 and K3 support Dave Klotz
2026-10-14 20:00 ` [PATCH v3 3/3] configs: spacemit_k1: enable USB host Dave Klotz
2 siblings, 0 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-14 20:00 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut
Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit, Eric Chung,
Huan Zhou, Guodong Xu, Raymond Mao
The UTMI PHY at c0a30000 ("spacemit,k1-usb2-phy") feeds the K1's DWC3
USB 3 controller. Port the Linux driver: enable the PHY clock, program
the PLL divider for the 24 MHz reference, wait for the PLL, then release
the PHY's resets and enable its clocks, the HS TX clock and the
automatic host-disconnect clear.
As in Linux, the divider is written before waiting for PLL_RDY, because
the PLL does not lock until the divider is programmed.
The K3 has the same PHY ("spacemit,k3-usb2-phy"). Linux handles the two
differently only in its disconnect callback, which U-Boot does not use,
so match the K3 compatible as well.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
Notes:
Changes in v3:
- Drop the read-back of the clock enable bits after releasing the
resets. SpacemiT confirmed the bits do not reflect the clock state
(Yixun Lan).
- Drop the HS DAC current setting (+15 %). It came from the vendor
driver by way of the FreeBSD port, I have no evidence that it fixes
anything, and the Linux driver does not set it.
- Match "spacemit,k3-usb2-phy"; Yixun Lan reports it works on K3.
- Drop the FreeBSD copyright and history from the header and commit
message, as none of the FreeBSD-specific code remains.
MAINTAINERS | 1 +
drivers/phy/spacemit/Kconfig | 10 ++
drivers/phy/spacemit/Makefile | 1 +
drivers/phy/spacemit/phy-k1-usb2.c | 153 +++++++++++++++++++++++++++++
4 files changed, 165 insertions(+)
create mode 100644 drivers/phy/spacemit/phy-k1-usb2.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 28d090d3c3..1e05da13cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1528,6 +1528,7 @@ M: Yixun Lan <dlan@kernel.org>
S: Maintained
T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-spacemit.git
F: arch/riscv/dts/k1-*-u-boot.dtsi
+F: drivers/phy/spacemit/phy-k1-usb2.c
N: spacemit
K: spacemit
diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig
index 3c47601c1f..16e9ece073 100644
--- a/drivers/phy/spacemit/Kconfig
+++ b/drivers/phy/spacemit/Kconfig
@@ -9,3 +9,13 @@ config PHY_SPACEMIT_K1_PCIE
ports B and C. The combo PHY must be enabled even if port A
is not used for PCIe, because it provides calibration values
required by the other ports.
+
+config PHY_SPACEMIT_K1_USB2
+ bool "SpacemiT K1 USB 2.0 PHY driver"
+ depends on PHY && CLK
+ help
+ Enable this to support the USB 2.0 (UTMI) PHY of the SpacemiT K1
+ and K3 SoCs, used by their DWC3 USB 3 controllers. The driver
+ configures the PLL for the 24 MHz reference clock, releases the
+ internal resets and enables the PHY clocks before the controller
+ is started.
diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile
index 3496c099cd..cad47d11bf 100644
--- a/drivers/phy/spacemit/Makefile
+++ b/drivers/phy/spacemit/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o
+obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o
diff --git a/drivers/phy/spacemit/phy-k1-usb2.c b/drivers/phy/spacemit/phy-k1-usb2.c
new file mode 100644
index 0000000000..cff72506fc
--- /dev/null
+++ b/drivers/phy/spacemit/phy-k1-usb2.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SpacemiT K1/K3 USB 2.0 (UTMI) PHY driver
+ *
+ * Ported from the Linux SpacemiT K1 USB 2.0 PHY driver:
+ * Copyright (C) 2025 SpacemiT (Hangzhou) Technology Co. Ltd
+ * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#define PHY_RST_MODE_CTRL 0x04
+#define PHY_PLL_RDY BIT(0)
+#define PHY_CLK_CDR_EN BIT(1)
+#define PHY_CLK_PLL_EN BIT(2)
+#define PHY_CLK_MAC_EN BIT(3)
+#define PHY_MAC_RSTN BIT(5)
+#define PHY_CDR_RSTN BIT(6)
+#define PHY_PLL_RSTN BIT(7)
+#define PHY_HS_LINE_TX_MODE BIT(13)
+#define PHY_FS_LINE_TX_MODE BIT(14)
+
+#define PHY_INIT_MODE_BITS (PHY_FS_LINE_TX_MODE | PHY_HS_LINE_TX_MODE)
+#define PHY_CLK_ENABLE_BITS (PHY_CLK_PLL_EN | PHY_CLK_CDR_EN | \
+ PHY_CLK_MAC_EN)
+#define PHY_DEASSERT_RST_BITS (PHY_PLL_RSTN | PHY_CDR_RSTN | \
+ PHY_MAC_RSTN)
+
+#define PHY_TX_HOST_CTRL 0x10
+#define PHY_HST_DISC_AUTO_CLR BIT(2)
+
+#define PHY_HSTXP_HW_CTRL 0x34
+#define PHY_HSTXP_RSTN BIT(2)
+#define PHY_CLK_HSTXP_EN BIT(3)
+#define PHY_HSTXP_MODE BIT(4)
+
+#define PHY_PLL_DIV_CFG 0x98
+#define PHY_FDIV_FRACT_8_15 GENMASK(7, 0)
+#define PHY_FDIV_FRACT_16_19 GENMASK(11, 8)
+#define PHY_FDIV_FRACT_20_21 BIT(12)
+#define PHY_FDIV_FRACT_0_1 GENMASK(14, 13)
+#define PHY_DIV_LOCAL_EN BIT(15)
+
+#define PHY_SEL_FREQ_24MHZ 0x01
+#define FDIV_REG_MASK (PHY_FDIV_FRACT_20_21 | PHY_FDIV_FRACT_16_19 | \
+ PHY_FDIV_FRACT_8_15)
+#define FDIV_REG_VAL 0x1ec4
+
+#define K1_USB2PHY_PLL_TIMEOUT_US 50000
+
+struct spacemit_usb2phy {
+ void __iomem *base;
+ struct clk clk;
+};
+
+static int spacemit_usb2phy_init(struct phy *phy)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
+ void __iomem *base = sphy->base;
+ u32 val;
+ int ret;
+
+ ret = clk_enable(&sphy->clk);
+ if (ret) {
+ dev_err(phy->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Let any controller reset settle before touching the PHY */
+ udelay(200);
+
+ /*
+ * Program the PLL divider for the 24 MHz reference before waiting
+ * for the PLL: it does not lock until the divider is written.
+ */
+ val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) |
+ FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) |
+ PHY_DIV_LOCAL_EN;
+ writel(val, base + PHY_PLL_DIV_CFG);
+
+ ret = readl_poll_timeout(base + PHY_RST_MODE_CTRL, val,
+ val & PHY_PLL_RDY, K1_USB2PHY_PLL_TIMEOUT_US);
+ if (ret) {
+ dev_err(phy->dev, "PLL lock timeout\n");
+ clk_disable(&sphy->clk);
+ return ret;
+ }
+
+ /* Release the PHY's internal resets and enable its clocks */
+ writel(PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS,
+ base + PHY_RST_MODE_CTRL);
+
+ writel(PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE,
+ base + PHY_HSTXP_HW_CTRL);
+
+ /* Auto-clear the HS host disconnect state on reconnect */
+ setbits_le32(base + PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR);
+
+ return 0;
+}
+
+static int spacemit_usb2phy_exit(struct phy *phy)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(phy->dev);
+
+ return clk_disable(&sphy->clk);
+}
+
+static const struct phy_ops spacemit_usb2phy_ops = {
+ .init = spacemit_usb2phy_init,
+ .exit = spacemit_usb2phy_exit,
+};
+
+static int spacemit_usb2phy_probe(struct udevice *dev)
+{
+ struct spacemit_usb2phy *sphy = dev_get_priv(dev);
+ int ret;
+
+ sphy->base = dev_read_addr_ptr(dev);
+ if (!sphy->base)
+ return -EINVAL;
+
+ ret = clk_get_by_index(dev, 0, &sphy->clk);
+ if (ret) {
+ dev_err(dev, "failed to get clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id spacemit_usb2phy_ids[] = {
+ { .compatible = "spacemit,k1-usb2-phy" },
+ { .compatible = "spacemit,k3-usb2-phy" },
+ { }
+};
+
+U_BOOT_DRIVER(spacemit_k1_usb2_phy) = {
+ .name = "spacemit_k1_usb2_phy",
+ .id = UCLASS_PHY,
+ .of_match = spacemit_usb2phy_ids,
+ .probe = spacemit_usb2phy_probe,
+ .ops = &spacemit_usb2phy_ops,
+ .priv_auto = sizeof(struct spacemit_usb2phy),
+};
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 2/3] usb: dwc3: generic: add SpacemiT K1 and K3 support
2026-10-14 19:59 ` [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-14 20:00 ` [PATCH v3 1/3] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
@ 2026-10-14 20:00 ` Dave Klotz
2026-10-14 20:00 ` [PATCH v3 3/3] configs: spacemit_k1: enable USB host Dave Klotz
2 siblings, 0 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-14 20:00 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut
Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit, Eric Chung,
Huan Zhou, Guodong Xu, Raymond Mao
The K1 describes its DWC3 controller as one flat device-tree node with
the controller registers, clocks, resets and PHYs. It has no separate
wrapper registers or controller subnode.
Use the existing flat-node callback through rk_ops so that the generic
wrapper binds its host or peripheral child to the same node.
The generic glue handles the K1 controller clocks, resets and USB 3 PHY,
while the child uses the existing generic DWC3 host/peripheral path.
The K3 uses the same flat-node layout, so match "spacemit,k3-dwc3"
as well.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
Reviewed-by: Marek Vasut <marek.vasut+usb@mailbox.org>
---
Notes:
Changes in v3:
- Add Marek Vasut's Reviewed-by.
- Also match "spacemit,k3-dwc3": the K3 uses the same flat-node
layout (dts/upstream k3.dtsi), and 1/3 now matches its USB 2.0 PHY.
Not tested on K3 hardware.
drivers/usb/dwc3/dwc3-generic.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 3f6e3d4085..2406f84461 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -753,6 +753,8 @@ static const struct udevice_id dwc3_glue_ids[] = {
{ .compatible = "rockchip,rk3568-dwc3", .data = (ulong)&rk_ops },
{ .compatible = "rockchip,rk3576-dwc3", .data = (ulong)&rk_ops },
{ .compatible = "rockchip,rk3588-dwc3", .data = (ulong)&rk_ops },
+ { .compatible = "spacemit,k1-dwc3", .data = (ulong)&rk_ops },
+ { .compatible = "spacemit,k3-dwc3", .data = (ulong)&rk_ops },
{ .compatible = "qcom,dwc3", .data = (ulong)&qcom_ops },
{ .compatible = "qcom,snps-dwc3", .data = (ulong)&qcom_flat_dt_ops },
{ .compatible = "fsl,imx8mp-dwc3", .data = (ulong)&imx8mp_ops },
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 3/3] configs: spacemit_k1: enable USB host
2026-10-14 19:59 ` [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-14 20:00 ` [PATCH v3 1/3] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-14 20:00 ` [PATCH v3 2/3] usb: dwc3: generic: add SpacemiT K1 and K3 support Dave Klotz
@ 2026-10-14 20:00 ` Dave Klotz
2 siblings, 0 replies; 17+ messages in thread
From: Dave Klotz @ 2026-10-14 20:00 UTC (permalink / raw)
To: Yixun Lan, Marek Vasut
Cc: u-boot, Quentin Schulz, Tom Rini, u-boot-spacemit, Eric Chung,
Huan Zhou, Guodong Xu, Raymond Mao
Enable the K1 USB 2.0 PHY, the DWC3 generic glue with xHCI, USB
storage and the usb command, so the K1 boards can boot from or read
USB devices with the default configuration.
Enable DM_REGULATOR_FIXED as well: on the OrangePi R2S, VBUS for the
USB 3 port is a GPIO-switched fixed regulator, which dwc3-generic
enables through vbus-supply before registering the xHCI controller.
Signed-off-by: Dave Klotz <DaveWK225@protonmail.com>
---
Notes:
Changes in v3:
- New patch.
configs/spacemit_k1_defconfig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/configs/spacemit_k1_defconfig b/configs/spacemit_k1_defconfig
index 39c490438d..8ee9baa13b 100644
--- a/configs/spacemit_k1_defconfig
+++ b/configs/spacemit_k1_defconfig
@@ -61,6 +61,7 @@ CONFIG_CMD_GPIO=y
CONFIG_CMD_MMC=y
CONFIG_CMD_PCI=y
CONFIG_CMD_SPI=y
+CONFIG_CMD_USB=y
CONFIG_CMD_EXT4=y
CONFIG_CMD_FAT=y
CONFIG_EFI_PARTITION=y
@@ -92,6 +93,7 @@ CONFIG_NVME_PCI=y
CONFIG_PCIE_DW_SPACEMIT=y
CONFIG_PHY=y
CONFIG_PHY_SPACEMIT_K1_PCIE=y
+CONFIG_PHY_SPACEMIT_K1_USB2=y
CONFIG_PINCTRL=y
CONFIG_PINCONF=y
CONFIG_PINCTRL_SPACEMIT_K1=y
@@ -99,6 +101,7 @@ CONFIG_DM_PMIC=y
CONFIG_PMIC_SPACEMIT_P1=y
CONFIG_DM_REGULATOR=y
CONFIG_SPL_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_FIXED=y
CONFIG_DM_REGULATOR_SPACEMIT_P1=y
CONFIG_DEBUG_UART_NS16550=y
CONFIG_DEBUG_UART_SHIFT=2
@@ -108,4 +111,9 @@ CONFIG_SYS_NS16550_MEM32=y
CONFIG_SPI=y
CONFIG_FSL_QSPI=y
CONFIG_TIMER_EARLY=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_GENERIC=y
+CONFIG_USB_STORAGE=y
CONFIG_SPL_LIB_RATIONAL=y
^ permalink raw reply related [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-10-14 20:00 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-06 4:28 [PATCH 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-06 4:28 ` [PATCH 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-06 4:28 ` [PATCH 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
2026-10-06 5:15 ` Marek Vasut
2026-10-06 12:31 ` [PATCH v2 0/2] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-06 12:32 ` [PATCH v2 1/2] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-12 9:04 ` Yixun Lan
2026-10-12 12:17 ` Yixun Lan
2026-10-12 9:37 ` Yixun Lan
2026-10-06 12:32 ` [PATCH v2 2/2] usb: dwc3: generic: add SpacemiT K1 support Dave Klotz
2026-10-12 9:38 ` Marek Vasut
2026-10-12 12:27 ` Yixun Lan
2026-10-14 19:59 ` Dave Klotz
2026-10-14 19:59 ` [PATCH v3 0/3] usb: add K1 USB2 PHY and generic DWC3 support Dave Klotz
2026-10-14 20:00 ` [PATCH v3 1/3] phy: spacemit: add K1 USB 2.0 PHY driver Dave Klotz
2026-10-14 20:00 ` [PATCH v3 2/3] usb: dwc3: generic: add SpacemiT K1 and K3 support Dave Klotz
2026-10-14 20:00 ` [PATCH v3 3/3] configs: spacemit_k1: enable USB host Dave Klotz
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.