diff options
-rw-r--r-- | arch/riscv/include/asm/arch-jh7110/gpio.h | 5 | ||||
-rw-r--r-- | board/starfive/visionfive2/spl.c | 3 | ||||
-rw-r--r-- | configs/starfive_visionfive2_defconfig | 10 | ||||
-rw-r--r-- | drivers/phy/Kconfig | 1 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/starfive/Kconfig | 21 | ||||
-rw-r--r-- | drivers/phy/starfive/Makefile | 7 | ||||
-rw-r--r-- | drivers/phy/starfive/phy-jh7110-pcie.c | 239 | ||||
-rw-r--r-- | drivers/phy/starfive/phy-jh7110-usb-syscon.h | 9 | ||||
-rw-r--r-- | drivers/phy/starfive/phy-jh7110-usb2.c | 162 | ||||
-rw-r--r-- | drivers/usb/cdns3/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/cdns3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/cdns3/cdns3-starfive.c | 182 | ||||
-rw-r--r-- | drivers/usb/cdns3/core.c | 3 | ||||
-rw-r--r-- | drivers/usb/cdns3/drd.c | 14 |
15 files changed, 665 insertions, 0 deletions
diff --git a/arch/riscv/include/asm/arch-jh7110/gpio.h b/arch/riscv/include/asm/arch-jh7110/gpio.h index 90aa2f8a9ed..be2a1e0d1c8 100644 --- a/arch/riscv/include/asm/arch-jh7110/gpio.h +++ b/arch/riscv/include/asm/arch-jh7110/gpio.h @@ -63,6 +63,11 @@ enum gpio_state { GPIO_DIN_MASK << GPIO_SHIFT(gpi), \ ((gpio + 2) & GPIO_DIN_MASK) << GPIO_SHIFT(gpi)) +#define SYS_IOMUX_DIN_DISABLED(gpi)\ + clrsetbits_le32(JH7110_SYS_IOMUX + GPIO_DIN + GPIO_OFFSET(gpi), \ + GPIO_DIN_MASK << GPIO_SHIFT(gpi), \ + ((0x1) & GPIO_DIN_MASK) << GPIO_SHIFT(gpi)) + #define SYS_IOMUX_SET_DS(gpio, ds) \ clrsetbits_le32(JH7110_SYS_IOMUX + GPIO_CONFIG + gpio * 4, \ GPIO_DS_MASK, (ds) << GPIO_DS_SHIFT) diff --git a/board/starfive/visionfive2/spl.c b/board/starfive/visionfive2/spl.c index 22afd76c6b9..1538d6aec73 100644 --- a/board/starfive/visionfive2/spl.c +++ b/board/starfive/visionfive2/spl.c @@ -103,6 +103,9 @@ void board_init_f(ulong dummy) JH7110_CLK_CPU_ROOT_MASK, BIT(JH7110_CLK_CPU_ROOT_SHIFT)); + /* Set USB overcurrent overflow pin disable */ + SYS_IOMUX_DIN_DISABLED(2); + ret = spl_board_init_f(); if (ret) { debug("spl_board_init_f init failed: %d\n", ret); diff --git a/configs/starfive_visionfive2_defconfig b/configs/starfive_visionfive2_defconfig index 3a90f1f3fa1..6a5b247c4b5 100644 --- a/configs/starfive_visionfive2_defconfig +++ b/configs/starfive_visionfive2_defconfig @@ -72,6 +72,7 @@ CONFIG_SYS_EEPROM_SIZE=512 CONFIG_SYS_EEPROM_PAGE_WRITE_BITS=4 CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS=5 CONFIG_CMD_MEMINFO=y +# CONFIG_CMD_BIND is not set CONFIG_CMD_I2C=y CONFIG_CMD_PCI=y CONFIG_CMD_USB=y @@ -118,6 +119,9 @@ CONFIG_NVME_PCI=y CONFIG_DM_PCI_COMPAT=y CONFIG_PCI_REGION_MULTI_ENTRY=y CONFIG_PCIE_STARFIVE_JH7110=y +CONFIG_PHY=y +CONFIG_PHY_STARFIVE_JH7110_PCIE=y +CONFIG_PHY_STARFIVE_JH7110_USB2=y CONFIG_PINCTRL=y CONFIG_PINCONF=y CONFIG_SPL_PINCTRL=y @@ -133,13 +137,19 @@ CONFIG_CADENCE_QSPI=y CONFIG_SYSRESET=y CONFIG_TIMER_EARLY=y CONFIG_USB=y +CONFIG_DM_USB_GADGET=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_PCI=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_PCI=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_PCI=y +CONFIG_USB_CDNS3=y +CONFIG_USB_CDNS3_GADGET=y +CONFIG_USB_CDNS3_HOST=y +# CONFIG_USB_CDNS3_TI is not set CONFIG_USB_KEYBOARD=y +CONFIG_USB_GADGET=y # CONFIG_WATCHDOG is not set # CONFIG_WATCHDOG_AUTOSTART is not set CONFIG_WDT=y diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e12347e8a03..f940648fe58 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -309,5 +309,6 @@ source "drivers/phy/cadence/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/qcom/Kconfig" source "drivers/phy/renesas/Kconfig" +source "drivers/phy/starfive/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c35f9294dd9..ce4ea28b299 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -44,3 +44,4 @@ obj-y += cadence/ obj-y += ti/ obj-y += qcom/ obj-y += renesas/ +obj-y += starfive/ diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 00000000000..d11338ed484 --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,21 @@ +# +# PHY drivers for Starfive platforms +# + +menu "Starfive PHY driver" + +config PHY_STARFIVE_JH7110_PCIE + bool "Starfive JH7110 PCIe 2.0 PHY driver" + depends on PHY + help + Enable this to support the Starfive JH7110 PCIE 2.0/USB 3.0 PHY. + Generic PHY driver JH7110 USB 3.0/ PCIe 2.0. + +config PHY_STARFIVE_JH7110_USB2 + bool "Starfive JH7110 USB 2.0 PHY driver" + depends on PHY + help + Enable this to support the Starfive JH7110 USB 2.0 PHY. + Generic PHY driver JH7110 USB 2.0. + +endmenu diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 00000000000..82f25aa21b7 --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2023 Starfive +# + +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB2) += phy-jh7110-usb2.o diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 00000000000..a30582821d9 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * Author: Minda Chen <minda.chen@starfivetech.com> + */ +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <soc.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#include "phy-jh7110-usb-syscon.h" + +#define PCIE_KVCO_LEVEL_OFF 0x28 +#define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c +#define PCIE_USB3_PHY_SS_MODE BIT(4) +#define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80 +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +#define PCIE_USB3_PHY_MODE 0x1 +#define PCIE_BUS_WIDTH 0x2 +#define PCIE_USB3_PHY_ENABLE 0x1 +#define PCIE_USB3_PHY_SPLIT 0x1 + +struct jh7110_pcie_phy { + struct phy *phy; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + void __iomem *regs; + struct regmap_field *phy_mode; + struct regmap_field *bus_width; + struct regmap_field *usb3_phy_en; + struct regmap_field *usb_split; + enum phy_mode mode; +}; + +static int phy_pcie_mode_set(struct jh7110_pcie_phy *data, bool usb_mode) +{ + unsigned int phy_mode, width, usb3_phy, ss_mode, split; + + /* default is PCIe mode */ + if (!data->stg_syscon || !data->sys_syscon) { + if (usb_mode) { + dev_err(data->phy->dev, "doesn't support USB3 mode\n"); + return -EINVAL; + } + return 0; + } + + if (usb_mode) { + phy_mode = PCIE_USB3_PHY_MODE; + width = 0; + usb3_phy = PCIE_USB3_PHY_ENABLE; + ss_mode = PCIE_USB3_PHY_SS_MODE; + split = 0; + } else { + phy_mode = 0; + width = PCIE_BUS_WIDTH; + usb3_phy = 0; + ss_mode = 0; + split = PCIE_USB3_PHY_SPLIT; + } + + regmap_field_write(data->phy_mode, phy_mode); + regmap_field_write(data->bus_width, width); + regmap_field_write(data->usb3_phy_en, usb3_phy); + clrsetbits_le32(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF, + PCIE_USB3_PHY_SS_MODE, ss_mode); + regmap_field_write(data->usb_split, split); + + return 0; +} + +static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct udevice *dev = phy->dev; + struct jh7110_pcie_phy *pcie_phy = dev_get_priv(dev); + int ret; + + if (mode == pcie_phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + ret = phy_pcie_mode_set(pcie_phy, 1); + if (ret) + return ret; + break; + case PHY_MODE_PCIE: + phy_pcie_mode_set(pcie_phy, 0); + break; + default: + return -EINVAL; + } + + dev_dbg(phy->dev, "Changing PHY mode to %d\n", mode); + pcie_phy->mode = mode; + + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .set_mode = jh7110_pcie_phy_set_mode, +}; + +static int phy_stg_regfield_init(struct udevice *dev, int mode, int usb3) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct reg_field phy_mode = REG_FIELD(mode, 20, 21); + struct reg_field bus_width = REG_FIELD(usb3, 2, 3); + struct reg_field usb3_phy_en = REG_FIELD(usb3, 4, 4); + + phy->phy_mode = devm_regmap_field_alloc(dev, phy->stg_syscon, phy_mode); + if (IS_ERR(phy->phy_mode)) { + dev_err(dev, "PHY mode reg field init failed\n"); + return PTR_ERR(phy->phy_mode); + } + + phy->bus_width = devm_regmap_field_alloc(dev, phy->stg_syscon, bus_width); + if (IS_ERR(phy->bus_width)) { + dev_err(dev, "PHY bus width reg field init failed\n"); + return PTR_ERR(phy->bus_width); + } + + phy->usb3_phy_en = devm_regmap_field_alloc(dev, phy->stg_syscon, usb3_phy_en); + if (IS_ERR(phy->usb3_phy_en)) { + dev_err(dev, "USB3 PHY enable field init failed\n"); + return PTR_ERR(phy->bus_width); + } + + return 0; +} + +static int phy_sys_regfield_init(struct udevice *dev, int split) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct reg_field usb_split = REG_FIELD(split, USB_PDRSTN_SPLIT_BIT, USB_PDRSTN_SPLIT_BIT); + + phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); + if (IS_ERR(phy->usb_split)) { + dev_err(dev, "USB split field init failed\n"); + return PTR_ERR(phy->usb_split); + } + + return 0; +} + +static int starfive_pcie_phy_get_syscon(struct udevice *dev) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct ofnode_phandle_args sys_phandle, stg_phandle; + int ret; + + /* get corresponding syscon phandle */ + ret = dev_read_phandle_with_args(dev, "starfive,sys-syscon", NULL, 1, 0, + &sys_phandle); + + if (ret < 0) { + dev_err(dev, "Can't get sys cfg phandle: %d\n", ret); + return ret; + } + + ret = dev_read_phandle_with_args(dev, "starfive,stg-syscon", NULL, 2, 0, + &stg_phandle); + + if (ret < 0) { + dev_err(dev, "Can't get stg cfg phandle: %d\n", ret); + return ret; + } + + phy->sys_syscon = syscon_node_to_regmap(sys_phandle.node); + /* get syscon register offset */ + if (!IS_ERR(phy->sys_syscon)) { + ret = phy_sys_regfield_init(dev, SYSCON_USB_PDRSTN_REG_OFFSET); + if (ret) + return ret; + } else { + phy->sys_syscon = NULL; + } + + phy->stg_syscon = syscon_node_to_regmap(stg_phandle.node); + if (!IS_ERR(phy->stg_syscon)) + return phy_stg_regfield_init(dev, stg_phandle.args[0], + stg_phandle.args[1]); + else + phy->stg_syscon = NULL; + + return 0; +} + +int jh7110_pcie_phy_probe(struct udevice *dev) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + int rc; + + phy->regs = dev_read_addr_ptr(dev); + if (!phy->regs) + return -EINVAL; + + rc = starfive_pcie_phy_get_syscon(dev); + if (rc) + return rc; + + phy_kvco_gain_set(phy); + + return 0; +} + +static const struct udevice_id jh7110_pcie_phy[] = { + { .compatible = "starfive,jh7110-pcie-phy"}, + {}, +}; + +U_BOOT_DRIVER(jh7110_pcie_phy) = { + .name = "jh7110_pcie_phy", + .id = UCLASS_PHY, + .of_match = jh7110_pcie_phy, + .probe = jh7110_pcie_phy_probe, + .ops = &jh7110_pcie_phy_ops, + .priv_auto = sizeof(struct jh7110_pcie_phy), +}; diff --git a/drivers/phy/starfive/phy-jh7110-usb-syscon.h b/drivers/phy/starfive/phy-jh7110-usb-syscon.h new file mode 100644 index 00000000000..0eb66f0d859 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb-syscon.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef PHY_JH7110_USB_SYSCON_H_ +#define PHY_JH7110_USB_SYSCON_H_ + +#define SYSCON_USB_PDRSTN_REG_OFFSET 0x18 +#define USB_PDRSTN_SPLIT_BIT 17 + +#endif diff --git a/drivers/phy/starfive/phy-jh7110-usb2.c b/drivers/phy/starfive/phy-jh7110-usb2.c new file mode 100644 index 00000000000..1a28381e0df --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb2.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <soc.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#include "phy-jh7110-usb-syscon.h" + +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) +#define USB_PHY_CLK_RATE 125000000 + +struct jh7110_usb2_phy { + struct phy *phy; + struct regmap *sys_syscon; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125m; + struct regmap_field *usb_split; + enum phy_mode mode; +}; + +static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set) +{ + /* Host mode enable the LS speed keep-alive signal */ + clrsetbits_le32(phy->regs + USB_LS_KEEPALIVE_OFF, + USB_LS_KEEPALIVE_ENABLE, + set ? USB_LS_KEEPALIVE_ENABLE : 0); +} + +static int usb2_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + + if (mode == usb2_phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + dev_dbg(dev, "Changing PHY to %d\n", mode); + usb2_phy->mode = mode; + usb2_set_ls_keepalive(usb2_phy, (mode != PHY_MODE_USB_DEVICE)); + break; + default: + return -EINVAL; + } + + /* set default split usb 2.0 only mode */ + regmap_field_write(usb2_phy->usb_split, true); + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + int ret; + + ret = clk_set_rate(usb2_phy->usb_125m_clk, USB_PHY_CLK_RATE); + if (ret < 0) { + dev_err(dev, "Failed to set 125m clock\n"); + return ret; + } + + return clk_prepare_enable(usb2_phy->app_125m); +} + +static int jh7110_usb2_phy_exit(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + + clk_disable_unprepare(usb2_phy->app_125m); + + return 0; +} + +struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = usb2_phy_set_mode, +}; + +int jh7110_usb2_phy_probe(struct udevice *dev) +{ + struct jh7110_usb2_phy *phy = dev_get_priv(dev); + ofnode node; + struct reg_field usb_split; + int ret; + + phy->regs = dev_read_addr_ptr(dev); + if (!phy->regs) + return -EINVAL; + + node = ofnode_by_compatible(ofnode_null(), "starfive,jh7110-sys-syscon"); + if (!ofnode_valid(node)) { + dev_err(dev, "Can't get syscon dev node\n"); + return -ENODEV; + } + + phy->sys_syscon = syscon_node_to_regmap(node); + if (IS_ERR(phy->sys_syscon)) { + dev_err(dev, "Can't get syscon regmap: %d\n", ret); + return PTR_ERR(phy->sys_syscon); + } + + usb_split.reg = SYSCON_USB_PDRSTN_REG_OFFSET; + usb_split.lsb = USB_PDRSTN_SPLIT_BIT; + usb_split.msb = USB_PDRSTN_SPLIT_BIT; + phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); + if (IS_ERR(phy->usb_split)) { + dev_err(dev, "USB split field init failed\n"); + return PTR_ERR(phy->usb_split); + } + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) { + dev_err(dev, "Failed to get 125m clock\n"); + return PTR_ERR(phy->usb_125m_clk); + } + + phy->app_125m = devm_clk_get(dev, "app_125m"); + if (IS_ERR(phy->app_125m)) { + dev_err(dev, "Failed to get app 125m clock\n"); + return PTR_ERR(phy->app_125m); + } + + return 0; +} + +static const struct udevice_id jh7110_usb2_phy[] = { + { .compatible = "starfive,jh7110-usb-phy"}, + {}, +}; + +U_BOOT_DRIVER(jh7110_usb2_phy) = { + .name = "jh7110_usb2_phy", + .id = UCLASS_PHY, + .of_match = jh7110_usb2_phy, + .probe = jh7110_usb2_phy_probe, + .ops = &jh7110_usb2_phy_ops, + .priv_auto = sizeof(struct jh7110_usb2_phy), +}; diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 35b61497d9c..1d5e4afac6c 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -49,6 +49,13 @@ config SPL_USB_CDNS3_HOST Host controller is compliant with XHCI so it will use standard XHCI driver. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on Starfive platforms" + default y if STARFIVE_JH7110 + help + Say 'Y' here if you are building for Starfive platforms + that contain Cadence USB3 controller core. E.g.: JH7110. + config USB_CDNS3_TI tristate "Cadence USB3 support on TI platforms" default USB_CDNS3 diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index d6047856091..1f00f23f704 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -8,4 +8,5 @@ cdns3-$(CONFIG_$(XPL_)USB_CDNS3_GADGET) += gadget.o ep0.o cdns3-$(CONFIG_$(XPL_)USB_CDNS3_HOST) += host.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 00000000000..78ceb831b19 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/usb/otg.h> +#include <malloc.h> +#include <reset.h> +#include <regmap.h> +#include <syscon.h> + +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_MISC_CFG_MASK GENMASK(23, 20) +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_PLL_EN BIT(22) +#define USB_REFCLK_MODE BIT(23) + +struct cdns_starfive { + struct udevice *dev; + struct regmap *stg_syscon; + struct reset_ctl_bulk resets; + struct clk_bulk clks; + u32 stg_usb_mode; + enum usb_dr_mode mode; +}; + +static void cdns_mode_init(struct cdns_starfive *data, enum usb_dr_mode mode) +{ + unsigned int strap, suspendm; + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_MISC_CFG_MASK, + USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); + + switch (mode) { + case USB_DR_MODE_HOST: + strap = USB_STRAP_HOST; + suspendm = USB_SUSPENDM_HOST; + break; + case USB_DR_MODE_PERIPHERAL: + strap = USB_STRAP_DEVICE; + suspendm = 0; + break; + default: + return; + } + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_SUSPENDM_MASK | USB_STRAP_MASK, + strap | suspendm); +} + +static void cdns_clk_rst_deinit(struct cdns_starfive *data) +{ + reset_assert_bulk(&data->resets); + clk_disable_bulk(&data->clks); +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + ret = clk_get_bulk(data->dev, &data->clks); + if (ret) + return ret; + + ret = reset_get_bulk(data->dev, &data->resets); + if (ret) + goto err_clk; + + ret = clk_enable_bulk(&data->clks); + if (ret) { + dev_err(data->dev, "clk enable failed: %d\n", ret); + goto err_en_clk; + } + + ret = reset_deassert_bulk(&data->resets); + if (ret) { + dev_err(data->dev, "reset deassert failed: %d\n", ret); + goto err_reset; + } + + return 0; + +err_reset: + clk_disable_bulk(&data->clks); +err_en_clk: + reset_release_bulk(&data->resets); +err_clk: + clk_release_bulk(&data->clks); + + return ret; +} + +static int cdns_starfive_get_syscon(struct cdns_starfive *data) +{ + struct ofnode_phandle_args phandle; + int ret; + + ret = dev_read_phandle_with_args(data->dev, "starfive,stg-syscon", NULL, 1, 0, + &phandle); + if (ret < 0) { + dev_err(data->dev, "Can't get stg cfg phandle: %d\n", ret); + return ret; + } + + data->stg_syscon = syscon_node_to_regmap(phandle.node); + if (IS_ERR(data->stg_syscon)) { + dev_err(data->dev, "fail to get regmap: %d\n", (int)PTR_ERR(data->stg_syscon)); + return PTR_ERR(data->stg_syscon); + } + + data->stg_usb_mode = phandle.args[0]; + + return 0; +} + +static int cdns_starfive_probe(struct udevice *dev) +{ + struct cdns_starfive *data = dev_get_plat(dev); + enum usb_dr_mode dr_mode; + int ret; + + data->dev = dev; + + ret = cdns_starfive_get_syscon(data); + if (ret) + return ret; + + dr_mode = usb_get_dr_mode(dev_ofnode(dev)); + + data->mode = dr_mode; + ret = cdns_clk_rst_init(data); + if (ret) { + dev_err(data->dev, "clk reset failed: %d\n", ret); + return ret; + } + cdns_mode_init(data, dr_mode); + + return 0; +} + +static int cdns_starfive_remove(struct udevice *dev) +{ + struct cdns_starfive *data = dev_get_plat(dev); + + cdns_clk_rst_deinit(data); + return 0; +} + +static const struct udevice_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + {}, +}; + +U_BOOT_DRIVER(cdns_starfive) = { + .name = "cdns-starfive", + .id = UCLASS_NOP, + .of_match = cdns_starfive_of_match, + .bind = cdns3_bind, + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .plat_auto = sizeof(struct cdns_starfive), + .flags = DM_FLAG_OS_PREPARE, +}; diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4cfd38ec245..4434dc15bec 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -410,6 +410,9 @@ int cdns3_bind(struct udevice *parent) name = ofnode_get_name(node); dr_mode = usb_get_dr_mode(node); + if (dr_mode == USB_DR_MODE_UNKNOWN) + dr_mode = usb_get_dr_mode(dev_ofnode(parent)); + switch (dr_mode) { #if defined(CONFIG_SPL_USB_HOST) || \ (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST)) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 47874fec29e..cbb13342343 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -217,15 +217,19 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns) int cdns3_drd_update_mode(struct cdns3 *cdns) { int ret = 0; + int mode; switch (cdns->dr_mode) { case USB_DR_MODE_PERIPHERAL: + mode = PHY_MODE_USB_DEVICE; ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL); break; case USB_DR_MODE_HOST: + mode = PHY_MODE_USB_HOST; ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST); break; case USB_DR_MODE_OTG: + mode = PHY_MODE_USB_OTG; ret = cdns3_init_otg_mode(cdns); break; default: @@ -234,6 +238,16 @@ int cdns3_drd_update_mode(struct cdns3 *cdns) return -EINVAL; } + ret = generic_phy_set_mode(&cdns->usb2_phy, mode, 0); + if (ret) { + dev_err(cdns->dev, "Set usb 2.0 PHY mode failed %d\n", ret); + return ret; + } + + ret = generic_phy_set_mode(&cdns->usb3_phy, mode, 0); + if (ret) + dev_err(cdns->dev, "Set usb 3.0 PHY mode failed %d\n", ret); + return ret; } |