mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-24 22:36:05 +00:00
Merge branch 'next' of git://source.denx.de/u-boot-usb into next
- Add USB support on Starfive JH7110
This commit is contained in:
commit
6a5917fba1
15 changed files with 665 additions and 0 deletions
|
@ -63,6 +63,11 @@ enum gpio_state {
|
||||||
GPIO_DIN_MASK << GPIO_SHIFT(gpi), \
|
GPIO_DIN_MASK << GPIO_SHIFT(gpi), \
|
||||||
((gpio + 2) & 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) \
|
#define SYS_IOMUX_SET_DS(gpio, ds) \
|
||||||
clrsetbits_le32(JH7110_SYS_IOMUX + GPIO_CONFIG + gpio * 4, \
|
clrsetbits_le32(JH7110_SYS_IOMUX + GPIO_CONFIG + gpio * 4, \
|
||||||
GPIO_DS_MASK, (ds) << GPIO_DS_SHIFT)
|
GPIO_DS_MASK, (ds) << GPIO_DS_SHIFT)
|
||||||
|
|
|
@ -103,6 +103,9 @@ void board_init_f(ulong dummy)
|
||||||
JH7110_CLK_CPU_ROOT_MASK,
|
JH7110_CLK_CPU_ROOT_MASK,
|
||||||
BIT(JH7110_CLK_CPU_ROOT_SHIFT));
|
BIT(JH7110_CLK_CPU_ROOT_SHIFT));
|
||||||
|
|
||||||
|
/* Set USB overcurrent overflow pin disable */
|
||||||
|
SYS_IOMUX_DIN_DISABLED(2);
|
||||||
|
|
||||||
ret = spl_board_init_f();
|
ret = spl_board_init_f();
|
||||||
if (ret) {
|
if (ret) {
|
||||||
debug("spl_board_init_f init failed: %d\n", ret);
|
debug("spl_board_init_f init failed: %d\n", ret);
|
||||||
|
|
|
@ -72,6 +72,7 @@ CONFIG_SYS_EEPROM_SIZE=512
|
||||||
CONFIG_SYS_EEPROM_PAGE_WRITE_BITS=4
|
CONFIG_SYS_EEPROM_PAGE_WRITE_BITS=4
|
||||||
CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS=5
|
CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS=5
|
||||||
CONFIG_CMD_MEMINFO=y
|
CONFIG_CMD_MEMINFO=y
|
||||||
|
# CONFIG_CMD_BIND is not set
|
||||||
CONFIG_CMD_I2C=y
|
CONFIG_CMD_I2C=y
|
||||||
CONFIG_CMD_PCI=y
|
CONFIG_CMD_PCI=y
|
||||||
CONFIG_CMD_USB=y
|
CONFIG_CMD_USB=y
|
||||||
|
@ -118,6 +119,9 @@ CONFIG_NVME_PCI=y
|
||||||
CONFIG_DM_PCI_COMPAT=y
|
CONFIG_DM_PCI_COMPAT=y
|
||||||
CONFIG_PCI_REGION_MULTI_ENTRY=y
|
CONFIG_PCI_REGION_MULTI_ENTRY=y
|
||||||
CONFIG_PCIE_STARFIVE_JH7110=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_PINCTRL=y
|
||||||
CONFIG_PINCONF=y
|
CONFIG_PINCONF=y
|
||||||
CONFIG_SPL_PINCTRL=y
|
CONFIG_SPL_PINCTRL=y
|
||||||
|
@ -133,13 +137,19 @@ CONFIG_CADENCE_QSPI=y
|
||||||
CONFIG_SYSRESET=y
|
CONFIG_SYSRESET=y
|
||||||
CONFIG_TIMER_EARLY=y
|
CONFIG_TIMER_EARLY=y
|
||||||
CONFIG_USB=y
|
CONFIG_USB=y
|
||||||
|
CONFIG_DM_USB_GADGET=y
|
||||||
CONFIG_USB_XHCI_HCD=y
|
CONFIG_USB_XHCI_HCD=y
|
||||||
CONFIG_USB_XHCI_PCI=y
|
CONFIG_USB_XHCI_PCI=y
|
||||||
CONFIG_USB_EHCI_HCD=y
|
CONFIG_USB_EHCI_HCD=y
|
||||||
CONFIG_USB_EHCI_PCI=y
|
CONFIG_USB_EHCI_PCI=y
|
||||||
CONFIG_USB_OHCI_HCD=y
|
CONFIG_USB_OHCI_HCD=y
|
||||||
CONFIG_USB_OHCI_PCI=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_KEYBOARD=y
|
||||||
|
CONFIG_USB_GADGET=y
|
||||||
# CONFIG_WATCHDOG is not set
|
# CONFIG_WATCHDOG is not set
|
||||||
# CONFIG_WATCHDOG_AUTOSTART is not set
|
# CONFIG_WATCHDOG_AUTOSTART is not set
|
||||||
CONFIG_WDT=y
|
CONFIG_WDT=y
|
||||||
|
|
|
@ -309,5 +309,6 @@ source "drivers/phy/cadence/Kconfig"
|
||||||
source "drivers/phy/ti/Kconfig"
|
source "drivers/phy/ti/Kconfig"
|
||||||
source "drivers/phy/qcom/Kconfig"
|
source "drivers/phy/qcom/Kconfig"
|
||||||
source "drivers/phy/renesas/Kconfig"
|
source "drivers/phy/renesas/Kconfig"
|
||||||
|
source "drivers/phy/starfive/Kconfig"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -44,3 +44,4 @@ obj-y += cadence/
|
||||||
obj-y += ti/
|
obj-y += ti/
|
||||||
obj-y += qcom/
|
obj-y += qcom/
|
||||||
obj-y += renesas/
|
obj-y += renesas/
|
||||||
|
obj-y += starfive/
|
||||||
|
|
21
drivers/phy/starfive/Kconfig
Normal file
21
drivers/phy/starfive/Kconfig
Normal file
|
@ -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
|
7
drivers/phy/starfive/Makefile
Normal file
7
drivers/phy/starfive/Makefile
Normal file
|
@ -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
|
239
drivers/phy/starfive/phy-jh7110-pcie.c
Normal file
239
drivers/phy/starfive/phy-jh7110-pcie.c
Normal file
|
@ -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),
|
||||||
|
};
|
9
drivers/phy/starfive/phy-jh7110-usb-syscon.h
Normal file
9
drivers/phy/starfive/phy-jh7110-usb-syscon.h
Normal file
|
@ -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
|
162
drivers/phy/starfive/phy-jh7110-usb2.c
Normal file
162
drivers/phy/starfive/phy-jh7110-usb2.c
Normal file
|
@ -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),
|
||||||
|
};
|
|
@ -49,6 +49,13 @@ config SPL_USB_CDNS3_HOST
|
||||||
Host controller is compliant with XHCI so it will use
|
Host controller is compliant with XHCI so it will use
|
||||||
standard XHCI driver.
|
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
|
config USB_CDNS3_TI
|
||||||
tristate "Cadence USB3 support on TI platforms"
|
tristate "Cadence USB3 support on TI platforms"
|
||||||
default USB_CDNS3
|
default USB_CDNS3
|
||||||
|
|
|
@ -8,4 +8,5 @@ cdns3-$(CONFIG_$(XPL_)USB_CDNS3_GADGET) += gadget.o ep0.o
|
||||||
|
|
||||||
cdns3-$(CONFIG_$(XPL_)USB_CDNS3_HOST) += host.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
|
obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
|
||||||
|
|
182
drivers/usb/cdns3/cdns3-starfive.c
Normal file
182
drivers/usb/cdns3/cdns3-starfive.c
Normal file
|
@ -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,
|
||||||
|
};
|
|
@ -410,6 +410,9 @@ int cdns3_bind(struct udevice *parent)
|
||||||
name = ofnode_get_name(node);
|
name = ofnode_get_name(node);
|
||||||
dr_mode = usb_get_dr_mode(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) {
|
switch (dr_mode) {
|
||||||
#if defined(CONFIG_SPL_USB_HOST) || \
|
#if defined(CONFIG_SPL_USB_HOST) || \
|
||||||
(!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST))
|
(!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST))
|
||||||
|
|
|
@ -217,15 +217,19 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns)
|
||||||
int cdns3_drd_update_mode(struct cdns3 *cdns)
|
int cdns3_drd_update_mode(struct cdns3 *cdns)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int mode;
|
||||||
|
|
||||||
switch (cdns->dr_mode) {
|
switch (cdns->dr_mode) {
|
||||||
case USB_DR_MODE_PERIPHERAL:
|
case USB_DR_MODE_PERIPHERAL:
|
||||||
|
mode = PHY_MODE_USB_DEVICE;
|
||||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
|
ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
|
||||||
break;
|
break;
|
||||||
case USB_DR_MODE_HOST:
|
case USB_DR_MODE_HOST:
|
||||||
|
mode = PHY_MODE_USB_HOST;
|
||||||
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
|
ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
|
||||||
break;
|
break;
|
||||||
case USB_DR_MODE_OTG:
|
case USB_DR_MODE_OTG:
|
||||||
|
mode = PHY_MODE_USB_OTG;
|
||||||
ret = cdns3_init_otg_mode(cdns);
|
ret = cdns3_init_otg_mode(cdns);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -234,6 +238,16 @@ int cdns3_drd_update_mode(struct cdns3 *cdns)
|
||||||
return -EINVAL;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue