From b85fe01d7df253a86aa61e0a6ed88971ffe44aac Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Tue, 11 Mar 2025 20:57:43 +0000 Subject: [PATCH 01/10] reset: rzg2l-usbphy-ctrl: Add new driver Add a new driver to control the USB 2.0 PHY reset controller on the Renesas RZ/G2L and related SoCs. Signed-off-by: Paul Barker Reviewed-by: Marek Vasut --- drivers/reset/Kconfig | 9 ++ drivers/reset/Makefile | 1 + drivers/reset/reset-rzg2l-usbphy-ctrl.c | 113 ++++++++++++++++++++++++ include/renesas/rzg2l-usbphy.h | 17 ++++ 4 files changed, 140 insertions(+) create mode 100644 drivers/reset/reset-rzg2l-usbphy-ctrl.c create mode 100644 include/renesas/rzg2l-usbphy.h diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index fe5c1214f57..80e83a40bdf 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -235,4 +235,13 @@ config RESET_AT91 This enables the Reset Controller driver support for Microchip/Atmel SoCs. Mainly used to expose assert/deassert methods to other drivers that require it. + +config RESET_RZG2L_USBPHY_CTRL + bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control" + depends on DM_RESET + help + Enable support for controlling USB 2.0 PHY resets on the Renesas + RZ/G2L SoC. This is required for USB 2.0 functionality to work on this + SoC. + endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index d99a78c9828..9d438a755b3 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -33,3 +33,4 @@ obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o obj-$(CONFIG_RESET_DRA7) += reset-dra7.o obj-$(CONFIG_RESET_AT91) += reset-at91.o obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o +obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c new file mode 100644 index 00000000000..afd647e00b1 --- /dev/null +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RESET 0x000 + +#define RESET_SEL_PLLRESET BIT(12) +#define RESET_PLLRESET BIT(8) + +#define RESET_SEL_P2RESET BIT(5) +#define RESET_SEL_P1RESET BIT(4) +#define RESET_PHYRST_2 BIT(1) +#define RESET_PHYRST_1 BIT(0) + +#define PHY_RESET_MASK (RESET_PHYRST_1 | RESET_PHYRST_2) + +#define NUM_PORTS 2 + +static int rzg2l_usbphy_ctrl_assert(struct reset_ctl *reset_ctl) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev); + u32 val; + + val = readl(priv->regs + RESET); + val |= reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1; + + /* If both ports are in reset, we can also place the PLL into reset. */ + if ((val & PHY_RESET_MASK) == PHY_RESET_MASK) + val |= RESET_PLLRESET; + + writel(val, priv->regs + RESET); + return 0; +} + +static int rzg2l_usbphy_ctrl_deassert(struct reset_ctl *reset_ctl) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev); + u32 val = reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1; + + /* If either port is out of reset, the PLL must also be out of reset. */ + val |= RESET_PLLRESET; + + clrbits_le32(priv->regs + RESET, val); + return 0; +} + +static int rzg2l_usbphy_ctrl_of_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + if (args->args[0] >= NUM_PORTS) + return -EINVAL; + + reset_ctl->id = args->args[0]; + return 0; +} + +struct reset_ops rzg2l_usbphy_ctrl_ops = { + .rst_assert = rzg2l_usbphy_ctrl_assert, + .rst_deassert = rzg2l_usbphy_ctrl_deassert, + .of_xlate = rzg2l_usbphy_ctrl_of_xlate, +}; + +static int rzg2l_usbphy_ctrl_probe(struct udevice *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev); + struct reset_ctl rst; + int ret; + + priv->regs = dev_read_addr(dev); + + ret = reset_get_by_index(dev, 0, &rst); + if (ret < 0) { + dev_err(dev, "failed to get reset line: %d\n", ret); + return ret; + } + + ret = reset_deassert(&rst); + if (ret < 0) { + dev_err(dev, "failed to de-assert reset line: %d\n", ret); + return ret; + } + + /* put pll and phy into reset state */ + setbits_le32(priv->regs + RESET, + RESET_SEL_PLLRESET | RESET_PLLRESET | + RESET_SEL_P1RESET | RESET_PHYRST_1 | + RESET_SEL_P2RESET | RESET_PHYRST_2); + + return 0; +} + +static const struct udevice_id rzg2l_usbphy_ctrl_ids[] = { + { .compatible = "renesas,rzg2l-usbphy-ctrl", }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(rzg2l_usbphy_ctrl) = { + .name = "rzg2l_usbphy_ctrl", + .id = UCLASS_RESET, + .of_match = rzg2l_usbphy_ctrl_ids, + .probe = rzg2l_usbphy_ctrl_probe, + .ops = &rzg2l_usbphy_ctrl_ops, + .priv_auto = sizeof(struct rzg2l_usbphy_ctrl_priv), +}; diff --git a/include/renesas/rzg2l-usbphy.h b/include/renesas/rzg2l-usbphy.h new file mode 100644 index 00000000000..1a46b585f17 --- /dev/null +++ b/include/renesas/rzg2l-usbphy.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RZ/G2L USB PHY common definitions + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + */ + +#ifndef RENESAS_RZG2L_USBPHY_H +#define RENESAS_RZG2L_USBPHY_H + +#include + +struct rzg2l_usbphy_ctrl_priv { + fdt_addr_t regs; +}; + +#endif /* RENESAS_RZG2L_USBPHY_H */ From e210e38a90016d9663bded428861118c8c4a24ea Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Tue, 11 Mar 2025 20:57:44 +0000 Subject: [PATCH 02/10] regulator: rzg2l-usbphy: Add new driver Add a new regulator driver to control the USB VBUS supply on the Renesas RZ/G2L and related SoCs. Reviewed-by: Marek Vasut Signed-off-by: Paul Barker --- drivers/power/regulator/Kconfig | 8 ++++ drivers/power/regulator/Makefile | 1 + .../power/regulator/rzg2l-usbphy-regulator.c | 42 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 drivers/power/regulator/rzg2l-usbphy-regulator.c diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 958f337c7e7..8f102a92c23 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -478,3 +478,11 @@ config DM_REGULATOR_TPS65219 features for REGULATOR TPS65219 and the family of TPS65219 PMICs. TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs. The driver implements get/set api for value and enable. + +config REGULATOR_RZG2L_USBPHY + bool "Enable driver for RZ/G2L USB PHY VBUS supply" + depends on DM_REGULATOR + help + Enable this option to support controlling the VBUS supply in + the USB PHY peripheral of the Renesas RZ/G2L SoC. This option + is required in order to use the USB OTG port. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index ca6c89d13b5..4382d4b3ab9 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o obj-$(CONFIG_$(XPL_)DM_REGULATOR_ANATOP) += anatop_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS65219) += tps65219_regulator.o +obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o diff --git a/drivers/power/regulator/rzg2l-usbphy-regulator.c b/drivers/power/regulator/rzg2l-usbphy-regulator.c new file mode 100644 index 00000000000..451f04c140e --- /dev/null +++ b/drivers/power/regulator/rzg2l-usbphy-regulator.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include +#include +#include +#include + +#define VBENCTL 0x03c +#define VBENCTL_VBUS_SEL BIT(0) + +static int rzg2l_usbphy_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); + + if (enable) + clrbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL); + else + setbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL); + + return 0; +} + +static int rzg2l_usbphy_regulator_get_enable(struct udevice *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); + + return !!readl(priv->regs + VBENCTL) & VBENCTL_VBUS_SEL; +} + +static const struct dm_regulator_ops rzg2l_usbphy_regulator_ops = { + .get_enable = rzg2l_usbphy_regulator_get_enable, + .set_enable = rzg2l_usbphy_regulator_set_enable, +}; + +U_BOOT_DRIVER(rzg2l_usbphy_regulator) = { + .name = "rzg2l_usbphy_regulator", + .id = UCLASS_REGULATOR, + .ops = &rzg2l_usbphy_regulator_ops, +}; From e6cc00a56e7d33a43401212065052a6e2a5a8d2b Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Tue, 11 Mar 2025 20:57:45 +0000 Subject: [PATCH 03/10] reset: rzg2l-usbphy-ctrl: Connect up vbus regulator Bind the USB VBUS regulator driver under the USB PHY reset driver for the Renesas RZ/G2L and related SoCs. This additional bind is needed as the corresponding device tree node does not contain a compatible string. Reviewed-by: Marek Vasut Signed-off-by: Paul Barker --- drivers/reset/Kconfig | 1 + drivers/reset/reset-rzg2l-usbphy-ctrl.c | 29 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 80e83a40bdf..5edbb3c25b4 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -239,6 +239,7 @@ config RESET_AT91 config RESET_RZG2L_USBPHY_CTRL bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control" depends on DM_RESET + select REGULATOR_RZG2L_USBPHY help Enable support for controlling USB 2.0 PHY resets on the Renesas RZ/G2L SoC. This is required for USB 2.0 functionality to work on this diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index afd647e00b1..622d7b9cf4f 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -103,10 +104,38 @@ static const struct udevice_id rzg2l_usbphy_ctrl_ids[] = { { /* sentinel */ } }; +static int rzg2l_usbphy_ctrl_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + node = ofnode_find_subnode(dev_ofnode(dev), "regulator-vbus"); + if (!ofnode_valid(node)) { + dev_err(dev, "Failed to find vbus regulator devicetree node\n"); + return -ENOENT; + } + + drv = lists_driver_lookup_name("rzg2l_usbphy_regulator"); + if (!drv) { + dev_err(dev, "Failed to find vbus regulator driver\n"); + return -ENOENT; + } + + ret = device_bind(dev, drv, dev->name, NULL, node, NULL); + if (ret) { + dev_err(dev, "Failed to bind vbus regulator: %d\n", ret); + return ret; + } + + return 0; +} + U_BOOT_DRIVER(rzg2l_usbphy_ctrl) = { .name = "rzg2l_usbphy_ctrl", .id = UCLASS_RESET, .of_match = rzg2l_usbphy_ctrl_ids, + .bind = rzg2l_usbphy_ctrl_bind, .probe = rzg2l_usbphy_ctrl_probe, .ops = &rzg2l_usbphy_ctrl_ops, .priv_auto = sizeof(struct rzg2l_usbphy_ctrl_priv), From 5f7d61122f04f1f04f686e8b95984f1f9523d847 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Tue, 11 Mar 2025 20:57:46 +0000 Subject: [PATCH 04/10] phy: rcar: Support RZ/G2L USB PHY Extend the existing Renesas R-Car Gen3 USB 2.0 PHY driver to support the RZ/G2L and related SoCs. Also enable this driver by default for the RZ/G2L SoC family. Reviewed-by: Marek Vasut Signed-off-by: Paul Barker --- drivers/phy/Kconfig | 4 +- drivers/phy/phy-rcar-gen3.c | 81 ++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index f940648fe58..d3fe90d939e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -163,8 +163,8 @@ config PHY_RCAR_GEN2 config PHY_RCAR_GEN3 tristate "Renesas R-Car Gen3 USB PHY" - depends on PHY && RCAR_GEN3 && CLK && DM_REGULATOR - default y if RCAR_GEN3 + depends on PHY && CLK && DM_REGULATOR && (RCAR_GEN3 || RZG2L) + default y if (RCAR_GEN3 || RZG2L) help Support for the Renesas R-Car Gen3 USB PHY. This driver operates the PHY connected to EHCI USB module and controls USB OTG operation. diff --git a/drivers/phy/phy-rcar-gen3.c b/drivers/phy/phy-rcar-gen3.c index 8c004eaf4c6..d06861c4f79 100644 --- a/drivers/phy/phy-rcar-gen3.c +++ b/drivers/phy/phy-rcar-gen3.c @@ -55,6 +55,7 @@ /* VBCTRL */ #define USB2_VBCTRL_DRVVBUSSEL BIT(8) +#define USB2_VBCTRL_VBOUT BIT(0) /* LINECTRL1 */ #define USB2_LINECTRL1_DPRPD_EN BIT(19) @@ -68,6 +69,13 @@ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) +/* RZ/G2L specific */ +#define USB2_OBINT_IDCHG_EN BIT(0) +#define USB2_LINECTRL1_USB2_IDMON BIT(0) + +/* Device flags */ +#define RCAR_GEN3_PHY_NO_ADPCTRL BIT(0) + struct rcar_gen3_phy { fdt_addr_t regs; struct clk clk; @@ -122,15 +130,50 @@ static int rcar_gen3_phy_phy_power_off(struct phy *phy) return regulator_set_enable(priv->vbus_supply, false); } -static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, - int submode) +static bool rcar_gen3_phy_check_id(struct phy *phy) { const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD; struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); - u32 adpctrl; + ulong flags = dev_get_driver_data(phy->dev); + u32 val; + + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + val = readl(priv->regs + USB2_LINECTRL1); + return !!(val & USB2_LINECTRL1_USB2_IDMON); + } + + val = readl(priv->regs + USB2_ADPCTRL); + return (val & adpdevmask) == adpdevmask; +} + +static void rcar_gen3_phy_set_vbus(struct phy *phy, bool enable) +{ + struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); + ulong flags = dev_get_driver_data(phy->dev); + u32 bits = USB2_ADPCTRL_DRVVBUS; + u64 reg = USB2_ADPCTRL; + + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + bits = USB2_VBCTRL_VBOUT; + reg = USB2_VBCTRL; + } + + if (enable) + setbits_le32(priv->regs + reg, bits); + else + clrbits_le32(priv->regs + reg, bits); +} + +static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); + ulong flags = dev_get_driver_data(phy->dev); if (mode == PHY_MODE_USB_OTG) { if (submode) { + u32 obint_enable_bits; + /* OTG submode is used as initialization indicator */ writel(USB2_INT_ENABLE_UCOM_INTEN | USB2_INT_ENABLE_USBH_INTB_EN | @@ -138,13 +181,16 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, priv->regs + USB2_INT_ENABLE); setbits_le32(priv->regs + USB2_VBCTRL, USB2_VBCTRL_DRVVBUSSEL); - writel(USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG, - priv->regs + USB2_OBINTSTA); - setbits_le32(priv->regs + USB2_OBINTEN, - USB2_OBINT_SESSVLDCHG | - USB2_OBINT_IDDIGCHG); - setbits_le32(priv->regs + USB2_ADPCTRL, - USB2_ADPCTRL_IDPULLUP); + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + obint_enable_bits = USB2_OBINT_IDCHG_EN; + } else { + obint_enable_bits = USB2_OBINT_SESSVLDCHG | + USB2_OBINT_IDDIGCHG; + setbits_le32(priv->regs + USB2_ADPCTRL, + USB2_ADPCTRL_IDPULLUP); + } + writel(obint_enable_bits, priv->regs + USB2_OBINTSTA); + setbits_le32(priv->regs + USB2_OBINTEN, obint_enable_bits); clrsetbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD | @@ -154,8 +200,7 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, USB2_LINECTRL1_DMRPD_EN); } - adpctrl = readl(priv->regs + USB2_ADPCTRL); - if ((adpctrl & adpdevmask) == adpdevmask) + if (rcar_gen3_phy_check_id(phy)) mode = PHY_MODE_USB_DEVICE; else mode = PHY_MODE_USB_HOST; @@ -165,13 +210,13 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, clrbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI); setbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); - setbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS); + rcar_gen3_phy_set_vbus(phy, true); } else if (mode == PHY_MODE_USB_DEVICE) { setbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI); clrsetbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD, USB2_LINECTRL1_DM_RPD); - clrbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS); + rcar_gen3_phy_set_vbus(phy, false); } else { dev_err(phy->dev, "Unknown mode %d\n", mode); return -EINVAL; @@ -226,7 +271,13 @@ static int rcar_gen3_phy_remove(struct udevice *dev) } static const struct udevice_id rcar_gen3_phy_of_match[] = { - { .compatible = "renesas,rcar-gen3-usb2-phy", }, + { + .compatible = "renesas,rcar-gen3-usb2-phy", + }, + { + .compatible = "renesas,rzg2l-usb2-phy", + .data = RCAR_GEN3_PHY_NO_ADPCTRL, + }, { }, }; From e2c060588681d87aaa12ccbccf2cb2135249efc4 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Tue, 11 Mar 2025 20:57:47 +0000 Subject: [PATCH 05/10] renesas_rzg2l_smarc_defconfig: Enable USB support Enable support for USB 2.0, USB 1.1 and USB storage devices on the Renesas RZ/G2L EVK. Also enable the 'usb' command to support USB scanning and debugging. Signed-off-by: Paul Barker Reviewed-by: Marek Vasut --- configs/renesas_rzg2l_smarc_defconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configs/renesas_rzg2l_smarc_defconfig b/configs/renesas_rzg2l_smarc_defconfig index 7a1224b3f07..b1d970b6b7e 100644 --- a/configs/renesas_rzg2l_smarc_defconfig +++ b/configs/renesas_rzg2l_smarc_defconfig @@ -26,6 +26,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_PART=y +CONFIG_CMD_USB=y CONFIG_CMD_PMIC=y CONFIG_CMD_EXT2=y CONFIG_CMD_EXT4=y @@ -54,5 +55,12 @@ CONFIG_PMIC_RAA215300=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_GPIO=y +CONFIG_RESET_RZG2L_USBPHY_CTRL=y CONFIG_SYSRESET=y CONFIG_SYSRESET_RAA215300=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_GENERIC=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_GENERIC=y +CONFIG_USB_STORAGE=y From 667ab63f931bc16b38c1ce87f57a4914c1c7bcfa Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Tue, 4 Mar 2025 20:07:09 +0000 Subject: [PATCH 06/10] net: ravb: Fix error handling in ravb_probe In ravb_probe(), we were missing a couple of things in the error handling path: * We must unregister the MDIO bus before freeing the corresponding struct mii_dev instance to avoid the potential for use-after-free bugs. * We must free the resources acquired by clk_get_bulk() even if the clocks have not yet been enabled. Fixes: 8ae51b6f324e ("net: ravb: Add Renesas Ethernet RAVB driver") Signed-off-by: Paul Barker Reviewed-by: Marek Vasut --- drivers/net/ravb.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index c39bef17b79..539fd37ee59 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -592,7 +592,7 @@ static int ravb_probe(struct udevice *dev) ret = clk_get_bulk(dev, ð->clks); if (ret < 0) - goto err_mdio_alloc; + goto err_clk_get; mdiodev = mdio_alloc(); if (!mdiodev) { @@ -614,23 +614,25 @@ static int ravb_probe(struct udevice *dev) /* Bring up PHY */ ret = clk_enable_bulk(ð->clks); if (ret) - goto err_mdio_register; + goto err_clk_enable; ret = ravb_reset(dev); if (ret) - goto err_mdio_reset; + goto err_clk_enable; ret = ravb_phy_config(dev); if (ret) - goto err_mdio_reset; + goto err_clk_enable; return 0; -err_mdio_reset: - clk_release_bulk(ð->clks); +err_clk_enable: + mdio_unregister(mdiodev); err_mdio_register: mdio_free(mdiodev); err_mdio_alloc: + clk_release_bulk(ð->clks); +err_clk_get: unmap_physmem(eth->iobase, MAP_NOCACHE); return ret; } From 985dc81a1d962a04ad1084bc4fe912492c8fe463 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Fri, 28 Feb 2025 12:47:52 +0000 Subject: [PATCH 07/10] net: phy: Port set/clear bits from Linux To simply porting phy drivers from Linux to U-Boot, define phy_set_bits() and phy_clear_bits() functions with a similar API to those used in Linux. The U-Boot versions of these functions include the `devad` argument which is not present in the Linux versions, to keep them aligned with the other phy functions in U-Boot. Reviewed-by: Marek Vasut Signed-off-by: Paul Barker --- include/phy.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/phy.h b/include/phy.h index 36785031eeb..36354aaf774 100644 --- a/include/phy.h +++ b/include/phy.h @@ -333,6 +333,30 @@ int gen10g_startup(struct phy_device *phydev); int gen10g_shutdown(struct phy_device *phydev); int gen10g_discover_mmds(struct phy_device *phydev); +/** + * phy_set_bits - Convenience function for setting bits in a PHY register + * @phydev: the phy_device struct + * @devad: The MMD to read from + * @regnum: register number to write + * @val: bits to set + */ +static inline int phy_set_bits(struct phy_device *phydev, int devad, u32 regnum, u16 val) +{ + return phy_modify(phydev, devad, regnum, 0, val); +} + +/** + * phy_clear_bits - Convenience function for clearing bits in a PHY register + * @phydev: the phy_device struct + * @devad: The MMD to write to + * @regnum: register number to write + * @val: bits to clear + */ +static inline int phy_clear_bits(struct phy_device *phydev, int devad, u32 regnum, u16 val) +{ + return phy_modify(phydev, devad, regnum, val, 0); +} + /** * U_BOOT_PHY_DRIVER() - Declare a new U-Boot driver * @__name: name of the driver From fbc35394560396c6d52f0360b4fbd899ae2de643 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Fri, 28 Feb 2025 12:47:53 +0000 Subject: [PATCH 08/10] net: phy: ksz90x1: Handle ksz9131 LED errata Micrel KSZ9131 PHY LED behavior is not correct when configured in Individual Mode, LED1 (Activity LED) is in the ON state when there is no-link. Workaround this by setting bit 9 of register 0x1e after verifying that the LED configuration is Individual Mode. This issue is described in KSZ9131RNX Silicon Errata DS80000693B [*] and according to that it will not be corrected in a future silicon revision. [*] https://ww1.microchip.com/downloads/en/DeviceDoc/KSZ9131RNX-Silicon-Errata-and-Data-Sheet-Clarification-80000863B.pdf Based on commit 0316c7e66bbd in the Linux kernel. Tested-by: Quentin Schulz # RK3588 Tiger Signed-off-by: Paul Barker --- drivers/net/phy/micrel_ksz90x1.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index c48ae6e88f3..ce7f2e2cc44 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -389,6 +389,12 @@ U_BOOT_PHY_DRIVER(ksz9031) = { #define KSZ9131RN_DLL_ENABLE_DELAY 0 #define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) +#define KSZ9131RN_COMMON_CTRL 0 +#define KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE BIT(4) + +#define KSZ9131RN_LED_ERRATA_REG 0x1e +#define KSZ9131RN_LED_ERRATA_BIT BIT(9) + static int ksz9131_config_rgmii_delay(struct phy_device *phydev) { struct phy_driver *drv = phydev->drv; @@ -436,6 +442,28 @@ static int ksz9131_config_rgmii_delay(struct phy_device *phydev) return ret; } +/* Silicon Errata DS80000693B + * + * When LEDs are configured in Individual Mode, LED1 is ON in a no-link + * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves + * according to the datasheet (off if there is no link). + */ +static int ksz9131_led_errata(struct phy_device *phydev) +{ + int reg; + + reg = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_COMMON_CTRL); + if (reg < 0) + return reg; + + if (!(reg & KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE)) + return 0; + + return phy_set_bits(phydev, MDIO_DEVAD_NONE, KSZ9131RN_LED_ERRATA_REG, + KSZ9131RN_LED_ERRATA_BIT); +} + static int ksz9131_config(struct phy_device *phydev) { int ret; @@ -446,6 +474,10 @@ static int ksz9131_config(struct phy_device *phydev) return ret; } + ret = ksz9131_led_errata(phydev); + if (ret < 0) + return ret; + /* add an option to disable the gigabit feature of this PHY */ if (env_get("disable_giga")) { unsigned features; From 28e85996ffe0f300a24505f6ede6695ce36daab5 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Fri, 28 Feb 2025 12:47:54 +0000 Subject: [PATCH 09/10] net: phy: ksz90x1: Load skew values from device tree Various signal skew values may be set in the device tree for the ksz9131 Ethernet PHY. For example, the RZ/G2L board requires non-default values for rxc-skew-psec & txc-skew-psec. This is based on the ksz9131 phy driver in Linux v6.11. Reviewed-by: Marek Vasut Signed-off-by: Paul Barker --- drivers/net/phy/micrel_ksz90x1.c | 115 +++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index ce7f2e2cc44..727c416fb25 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -395,6 +395,117 @@ U_BOOT_PHY_DRIVER(ksz9031) = { #define KSZ9131RN_LED_ERRATA_REG 0x1e #define KSZ9131RN_LED_ERRATA_BIT BIT(9) +#define KSZ9131RN_CONTROL_PAD_SKEW 4 +#define KSZ9131RN_RX_DATA_PAD_SKEW 5 +#define KSZ9131RN_TX_DATA_PAD_SKEW 6 +#define KSZ9131RN_CLK_PAD_SKEW 8 + +#define KSZ9131RN_SKEW_5BIT_MAX 2400 +#define KSZ9131RN_SKEW_4BIT_MAX 800 +#define KSZ9131RN_OFFSET 700 +#define KSZ9131RN_STEP 100 + +static int ksz9131_of_load_skew_values(struct phy_device *phydev, + ofnode of_node, + u16 reg, size_t field_sz, + const char *field[], u8 numfields) +{ + int val[4] = {-(1 + KSZ9131RN_OFFSET), -(2 + KSZ9131RN_OFFSET), + -(3 + KSZ9131RN_OFFSET), -(4 + KSZ9131RN_OFFSET)}; + int skewval, skewmax = 0; + int matches = 0; + u16 maxval; + u16 newval; + u16 mask; + int i; + + /* psec properties in dts should mean x pico seconds */ + if (field_sz == 5) + skewmax = KSZ9131RN_SKEW_5BIT_MAX; + else + skewmax = KSZ9131RN_SKEW_4BIT_MAX; + + for (i = 0; i < numfields; i++) + if (!ofnode_read_s32(of_node, field[i], &skewval)) { + if (skewval < -KSZ9131RN_OFFSET) + skewval = -KSZ9131RN_OFFSET; + else if (skewval > skewmax) + skewval = skewmax; + + val[i] = skewval + KSZ9131RN_OFFSET; + matches++; + } + + if (!matches) + return 0; + + if (matches < numfields) + newval = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg); + else + newval = 0; + + maxval = (field_sz == 4) ? 0xf : 0x1f; + for (i = 0; i < numfields; i++) + if (val[i] != -(i + 1 + KSZ9131RN_OFFSET)) { + mask = 0xffff; + mask ^= maxval << (field_sz * i); + newval = (newval & mask) | + (((val[i] / KSZ9131RN_STEP) & maxval) + << (field_sz * i)); + } + + return phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg, newval); +} + +static int ksz9131_of_load_all_skew_values(struct phy_device *phydev) +{ + const char *control_skews[2] = { "txen-skew-psec", "rxdv-skew-psec" }; + const char *clk_skews[2] = { "rxc-skew-psec", "txc-skew-psec" }; + const char *rx_data_skews[4] = { + "rxd0-skew-psec", "rxd1-skew-psec", + "rxd2-skew-psec", "rxd3-skew-psec" + }; + const char *tx_data_skews[4] = { + "txd0-skew-psec", "txd1-skew-psec", + "txd2-skew-psec", "txd3-skew-psec" + }; + struct ofnode_phandle_args phandle_args; + int ret; + + /* + * Silently ignore failure here as the device tree is not required to + * contain a phy node. + */ + if (dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL, 0, 0, + &phandle_args)) + return 0; + + if (!ofnode_valid(phandle_args.node)) + return 0; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_CLK_PAD_SKEW, 5, + clk_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_CONTROL_PAD_SKEW, 4, + control_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_RX_DATA_PAD_SKEW, 4, + rx_data_skews, 4); + if (ret < 0) + return ret; + + return ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_TX_DATA_PAD_SKEW, 4, + tx_data_skews, 4); +} + static int ksz9131_config_rgmii_delay(struct phy_device *phydev) { struct phy_driver *drv = phydev->drv; @@ -474,6 +585,10 @@ static int ksz9131_config(struct phy_device *phydev) return ret; } + ret = ksz9131_of_load_all_skew_values(phydev); + if (ret < 0) + return ret; + ret = ksz9131_led_errata(phydev); if (ret < 0) return ret; From 95d10669c0bd7c7211d7fcdffc5993b16898124b Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Fri, 28 Feb 2025 12:47:55 +0000 Subject: [PATCH 10/10] net: phy: ksz90x1: Simplify ksz9131_config_rgmii_delay We can call phy_modify_mmd() instead of manually calling drv->readext() and drv->writeext(). Reviewed-by: Marek Vasut Signed-off-by: Paul Barker --- drivers/net/phy/micrel_ksz90x1.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index 727c416fb25..ee8eae1efd9 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -508,8 +508,7 @@ static int ksz9131_of_load_all_skew_values(struct phy_device *phydev) static int ksz9131_config_rgmii_delay(struct phy_device *phydev) { - struct phy_driver *drv = phydev->drv; - u16 rxcdll_val, txcdll_val, val; + u16 rxcdll_val, txcdll_val; int ret; switch (phydev->interface) { @@ -533,24 +532,15 @@ static int ksz9131_config_rgmii_delay(struct phy_device *phydev) return 0; } - val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_RXC_DLL_CTRL); - val &= ~KSZ9131RN_DLL_CTRL_BYPASS; - val |= rxcdll_val; - ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_RXC_DLL_CTRL, val); - if (ret) + ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + rxcdll_val); + if (ret < 0) return ret; - val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_TXC_DLL_CTRL); - - val &= ~KSZ9131RN_DLL_CTRL_BYPASS; - val |= txcdll_val; - ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_TXC_DLL_CTRL, val); - - return ret; + return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + txcdll_val); } /* Silicon Errata DS80000693B