Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions arch/riscv/boot/dts/spacemit/k3-com260.dts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "k3.dtsi"
#include "k3-pinctrl.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/mmc/spacemit-k1-sdhci.h>

Expand Down Expand Up @@ -60,6 +61,17 @@
vin-supply = <&p5v>;
status = "okay";
};

hub_reset: hub-reset {
compatible = "regulator-fixed";
regulator-name = "hub-reset";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
enable-active-high;
regulator-boot-on;
regulator-always-on;
gpio = <&gpio 1 21 GPIO_ACTIVE_HIGH>;
};
};

&resmem {
Expand Down Expand Up @@ -345,3 +357,34 @@
};
};
};

&usb3_portb_u2phy {
status = "okay";
};

&usb3_portb_u3phy {
status = "okay";
};

&usb3_portb {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
reset-on-resume;

/* VL817 USB2.0 hub (4 ports) */
hub_2_0: hub@1 {
compatible = "usb2109,2817";
reg = <1>;
peer-hub = <&hub_3_0>;
vdd-supply = <&hub_reset>;
};

/* VL817 USB3.0 hub (4 ports) */
hub_3_0: hub@2 {
compatible = "usb2109,817";
reg = <2>;
peer-hub = <&hub_2_0>;
};
};

42 changes: 41 additions & 1 deletion arch/riscv/boot/dts/spacemit/k3.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@
};

pll: clock-controller@d4090000 {
compatible = "spacemit,k3-pll";
compatible = "spacemit,k3-pll", "syscon", "simple-mfd";
reg = <0x0 0xd4090000 0x0 0x10000>;
clocks = <&vctcxo_24m>;
spacemit,mpmu = <&syscon_mpmu>;
Expand Down Expand Up @@ -910,5 +910,45 @@
reset-names = "sdh_axi", "sdh2";
status = "disabled";
};

usb3_portb_u2phy: phy@81500000 {
compatible = "spacemit,k3-usb2-phy";
reg = <0x0 0x81500000 0x0 0x200>;
#phy-cells = <0>;
status = "disabled";
};

usb3_portb_u3phy: phy@81f00000 {
compatible = "spacemit,k3-usb3-phy";
spacemit,syscon-apmu = <&syscon_apmu>;
spacemit,syscon-apb-spare = <&pll>;
reg = <0x0 0x81f00000 0x0 0x200>;
combo-usb-bit = <2>;
#phy-cells = <0>;
status = "disabled";
};

usb3_portb: usb3@81400000 {
compatible = "spacemit,k3-dwc3";
reg = <0x0 0x81400000 0x0 0x10000>;
spacemit,syscon-apmu = <&syscon_apmu 0x3c8>;
interrupts = <125 IRQ_TYPE_LEVEL_HIGH>,
<157 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "dwc3", "wakeup";
interrupt-parent = <&saplic>;
clocks = <&syscon_apmu CLK_APMU_USB3_PORTB_BUS>;
resets = <&syscon_apmu RESET_APMU_USB3_PORTB>;
phys = <&usb3_portb_u2phy>, <&usb3_portb_u3phy>;
phy-names = "usb2-phy", "usb3-phy";
phy_type = "utmi";
snps,dis_enblslpm_quirk;
snps,dis_u2_susphy_quirk;
snps,dis_u3_susphy_quirk;
snps,dis-del-phy-power-chg-quirk;
snps,dis-tx-ipgap-linecheck-quirk;
snps,parkmode-disable-ss-quirk;
dr_mode = "host";
status = "disabled";
};
};
};
1 change: 1 addition & 0 deletions drivers/phy/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,6 @@ source "drivers/phy/ti/Kconfig"
source "drivers/phy/intel/Kconfig"
source "drivers/phy/xilinx/Kconfig"
source "drivers/phy/synopsys/Kconfig"
source "drivers/phy/spacemit/Kconfig"

endmenu
1 change: 1 addition & 0 deletions drivers/phy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ obj-y += allwinner/ \
tegra/ \
ti/ \
xilinx/ \
spacemit/ \
synopsys/
23 changes: 23 additions & 0 deletions drivers/phy/spacemit/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Phy drivers for SpacemiT platforms
#
config PHY_SPACEMIT_K3_USB3
tristate "SpacemiT K3 USB 3.0 PHY support"
depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
depends on COMMON_CLK
depends on USB_COMMON
depends on TYPEC || TYPEC=n
select GENERIC_PHY
help
Enable this to support K3 USB 3.0 PHY driver.

config PHY_SPACEMIT_K1_USB2
tristate "SpacemiT K1/K3 USB 2.0 PHY support"
depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF
depends on COMMON_CLK
depends on USB_COMMON
select GENERIC_PHY
help
Enable this to support K1 USB 2.0 PHY driver. This driver takes care of
enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver.
3 changes: 3 additions & 0 deletions drivers/phy/spacemit/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

字母表排序

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再次感谢您的审核!已修改并重新提交,USB2.0 / 3.0 HOST 功能在 K3 开发板测试正常。

obj-$(CONFIG_PHY_SPACEMIT_K3_USB3) += phy-k3-usb3.o
210 changes: 210 additions & 0 deletions drivers/phy/spacemit/phy-k1-usb2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/usb/of.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)
/*
* hs line state sel (Bit 13):
* - 1 (Default): Internal HS line state is set to 01 when usb_hs_tx_en is valid.
* - 0: Internal HS line state is always driven by usb_hs_lstate.
*
* fs line state sel (Bit 14):
* - 1 (Default): FS line state is determined by the output data
* (usb_fs_datain/b).
* - 0: FS line state is always determined by the input data (dmo/dpo).
*/
#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) /* autoclear hs host disc when re-connect */

#define PHY_HSTXP_HW_CTRL 0x34
#define PHY_HSTXP_RSTN BIT(2) /* generate reset for clock hstxp */
#define PHY_CLK_HSTXP_EN BIT(3) /* clock hstxp enable */
#define PHY_HSTXP_MODE BIT(4) /* 0: force en_txp to be 1; 1: no force */

#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) /* fdiv_reg<21>, <20>, bit21 == bit20 */
/*
* freq_sel<1:0>
* if ref clk freq=24.0MHz-->freq_sel<2:0> == 3b'001, then internal divider value == 80
*/
#define PHY_FDIV_FRACT_0_1 GENMASK(14, 13)
/*
* pll divider value selection
* 1: divider value will choose internal default value ,dependent on freq_sel<1:0>
* 0: divider value will be over ride by fdiv_reg<21:0>
*/
#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 /* 0x100 selects 24MHz, rest are default */

#define K1_USB2PHY_RESET_TIME_MS 50

struct spacemit_usb2phy {
struct phy *phy;
struct clk *clk;
struct regmap *regmap_base;
};

static const struct regmap_config phy_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0x200,
};

static int spacemit_usb2phy_init(struct phy *phy)
{
struct spacemit_usb2phy *sphy = phy_get_drvdata(phy);
struct regmap *map = sphy->regmap_base;
u32 val;
int ret;

ret = clk_enable(sphy->clk);
if (ret) {
dev_err(&phy->dev, "failed to enable clock\n");
clk_disable(sphy->clk);
return ret;
}

/*
* make sure the usb controller is not under reset process before
* any configuration
*/
usleep_range(150, 200);

/* 24M ref clk */
val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) |
FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) |
PHY_DIV_LOCAL_EN;
regmap_write(map, PHY_PLL_DIV_CFG, val);

ret = regmap_read_poll_timeout(map, PHY_RST_MODE_CTRL, val,
(val & PHY_PLL_RDY),
500, K1_USB2PHY_RESET_TIME_MS * 1000);
if (ret) {
dev_err(&phy->dev, "wait PLLREADY timeout\n");
clk_disable(sphy->clk);
return ret;
}

/* release usb2 phy internal reset and enable clock gating */
val = (PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS);
regmap_write(map, PHY_RST_MODE_CTRL, val);

val = (PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE);
regmap_write(map, PHY_HSTXP_HW_CTRL, val);

/* auto clear host disc */
regmap_update_bits(map, PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR,
PHY_HST_DISC_AUTO_CLR);

return 0;
}

static int spacemit_usb2phy_exit(struct phy *phy)
{
struct spacemit_usb2phy *sphy = phy_get_drvdata(phy);

clk_disable(sphy->clk);

return 0;
}

static const struct phy_ops spacemit_k1_usb2phy_ops = {
.init = spacemit_usb2phy_init,
.exit = spacemit_usb2phy_exit,
.owner = THIS_MODULE,
};

static const struct phy_ops spacemit_k3_usb2phy_ops = {
.init = spacemit_usb2phy_init,
.exit = spacemit_usb2phy_exit,
.owner = THIS_MODULE,
};

static int spacemit_usb2phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct spacemit_usb2phy *sphy;
const struct phy_ops *ops;
void __iomem *base;

sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
if (!sphy)
return -ENOMEM;

ops = device_get_match_data(dev);

sphy->clk = devm_clk_get_optional_prepared(&pdev->dev, NULL);
if (IS_ERR(sphy->clk))
return dev_err_probe(dev, PTR_ERR(sphy->clk), "Failed to get clock\n");

base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);

sphy->regmap_base = devm_regmap_init_mmio(dev, base, &phy_regmap_config);
if (IS_ERR(sphy->regmap_base))
return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n");

sphy->phy = devm_phy_create(dev, NULL, ops);
if (IS_ERR(sphy->phy))
return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n");

phy_set_drvdata(sphy->phy, sphy);
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 spacemit_usb2phy_dt_match[] = {
{ .compatible = "spacemit,k1-usb2-phy", .data = &spacemit_k1_usb2phy_ops },
{ .compatible = "spacemit,k3-usb2-phy", .data = &spacemit_k3_usb2phy_ops },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spacemit_usb2phy_dt_match);

static struct platform_driver spacemit_usb2_phy_driver = {
.probe = spacemit_usb2phy_probe,
.driver = {
.name = "spacemit-usb2-phy",
.of_match_table = spacemit_usb2phy_dt_match,
},
};
module_platform_driver(spacemit_usb2_phy_driver);

MODULE_DESCRIPTION("Spacemit USB 2.0 PHY driver");
MODULE_LICENSE("GPL");
Loading
Loading