From: Thanh Quan Date: Mon, 27 Oct 2025 16:52:21 +0000 (+0100) Subject: phy: renesas: Add Multi-Protocol PHY driver for R-Car X5H X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3d50dba3a3ccd71085ecce37c56fd1d7b2502b92;p=thirdparty%2Fu-boot.git phy: renesas: Add Multi-Protocol PHY driver for R-Car X5H Add PHY driver for Multi-Protocol PHY present on Renesas R-Car X5H R8A78000 SoC. Currently, the PHY driver only supports configuring the MPPHY for ethernet operation. Signed-off-by: Thanh Quan Signed-off-by: Phong Hoang Signed-off-by: Hai Pham #Fix License-Identifier Signed-off-by: Marek Vasut [Marek: Clean up macros, indent, clock and reset handling in probe, rename the driver and add r8a78000- into compatible string, update commit message.] --- diff --git a/drivers/phy/renesas/Kconfig b/drivers/phy/renesas/Kconfig index cd3f127c986..affbee0500c 100644 --- a/drivers/phy/renesas/Kconfig +++ b/drivers/phy/renesas/Kconfig @@ -13,3 +13,9 @@ config PHY_R8A78000_ETHERNET_PCS depends on RCAR_64 && PHY help Support for Ethernet PCS found on Renesas R-Car X5H SoCs. + +config PHY_R8A78000_MP_PHY + tristate "Renesas R-Car X5H Multi-Protocol PHY driver" + depends on RCAR_64 && PHY + help + Support for Multi-Protocol PHY on Renesas R-Car X5H SoCs. diff --git a/drivers/phy/renesas/Makefile b/drivers/phy/renesas/Makefile index d6520eeed7e..12585c21f58 100644 --- a/drivers/phy/renesas/Makefile +++ b/drivers/phy/renesas/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_PHY_R8A779F0_ETHERNET_SERDES) += r8a779f0-ether-serdes.o obj-$(CONFIG_PHY_R8A78000_ETHERNET_PCS) += r8a78000-ether-pcs.o +obj-$(CONFIG_PHY_R8A78000_MP_PHY) += r8a78000-mp-phy.o diff --git a/drivers/phy/renesas/r8a78000-mp-phy.c b/drivers/phy/renesas/r8a78000-mp-phy.c new file mode 100644 index 00000000000..fba130a65a1 --- /dev/null +++ b/drivers/phy/renesas/r8a78000-mp-phy.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Renesas Multi-Protocol PHY device driver + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Common registers */ +#define MPPHY_CMNCNT1 0x80000 +#define MPPHY_CMNCNT2 0x80004 +#define MPPHY_PCS0REG1 0x85000 +#define MPPHY_PCS0REG5 0x85010 + +/* Channel register base and offsets */ +#define MPPHY_CHAN_BASE(ch) (0x81000 + (ch) * 0x1000) +#define MPPHY_PXTEST_OFFSET 0x00C +#define MPPHY_RXCNT_OFFSET 0x038 +#define MPPHY_SRAMCNT_OFFSET 0x040 +#define MPPHY_REFCLK_OFFSET 0x014 +#define MPPHY_CNTXT1_OFFSET 0x004 +#define MPPHY_CNTXT2_OFFSET 0x008 +#define MPPHY_TXREQ_OFFSET 0x044 + +/* Channel specific registers */ +#define MPPHY_PXTEST(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_PXTEST_OFFSET) +#define MPPHY_PXRXCNT(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_RXCNT_OFFSET) +#define MPPHY_PXSRAMCNT(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_SRAMCNT_OFFSET) +#define MPPHY_PXREFCLK(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_REFCLK_OFFSET) +#define MPPHY_PXCNTXT1(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_CNTXT1_OFFSET) +#define MPPHY_PXCNTXT2(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_CNTXT2_OFFSET) +#define MPPHY_PXTXREQ(ch) (MPPHY_CHAN_BASE(ch) + MPPHY_TXREQ_OFFSET) + +/* Channel enable bit masks for MPPHY_CMNCNT1 register */ +#define MPPHY_CMNCNT1_CH_MASK(ch) (0xFF << ((ch) * 8)) + +/* Channel enable bits for MPPHY_CMNCNT1 register */ +#define MPPHY_CMNCNT1_CH_EN(ch) ((ch) == 0 ? BIT(1) : BIT((ch) * 8)) + +/* PCS0REG5 register mask and values for each channel */ +#define MPPHY_PCS0REG5_CH(ch) (0x03 << (24 + (ch) * 2)) + +/* PCS0REG1 register bits */ +#define MPPHY_PCS0REG1_VAL 0x00010000 + +/* PXTEST register bit */ +#define MPPHY_PXTEST_BIT BIT(0) + +/* PXRXCNT register reset value */ +#define MPPHY_PXRXCNT_RESET_VAL 0x202 + +/* PXSRAMCNT register bits */ +#define MPPHY_PXSRAMCNT_BYPASS BIT(0) +#define MPPHY_PXSRAMCNT_BIT3 BIT(3) +#define BOOTLOAD_BYPASS_MODE 0x3 +#define SRAM_BYPASS_MODE 0xc +#define SRAM_EXT_LD_DONE 0x10 +#define SRAM_INIT_DONE 0x20 + +#define SRAM_CONTROL_SET_BIT \ + (BOOTLOAD_BYPASS_MODE | SRAM_BYPASS_MODE | \ + SRAM_EXT_LD_DONE | SRAM_INIT_DONE) + +/* CMNCNT1/2 clock settings */ +#define MPPHY_CMNCNT2_CLK_CH(ch) (0x30003 << ((ch) * 4)) + +/* PXREFCLK register value */ +#define MPPHY_PXREFCLK_VAL 0x35 + +/* PXTXREQ register value */ +#define MPPHY_PXTXREQ_VAL 0x8 + +/* Context settings */ +#define MPPHY_CNTXT1_VALUE 0x02010002 +#define MPPHY_CNTXT2_VALUE 0x02020202 /* For channels 1-3 */ +#define MPPHY_CNTXT2_CH0_VALUE 0x02020201 /* Special for channel 0 */ + +/* struct mpphy_priv - Private data for the MPPHY driver */ +struct mp_phy_priv { + struct phy *phy; + struct clk_bulk clks; + struct reset_ctl_bulk resets; + void __iomem *base; +}; + +static int mp_phy_init(struct phy *phy) +{ + struct mp_phy_priv *priv = dev_get_priv(phy->dev); + + if (phy->id > 3) { + printf("Invalid channel ID: %ld\n", phy->id); + return -EINVAL; + } + + clrsetbits_le32(priv->base + MPPHY_CMNCNT1, MPPHY_CMNCNT1_CH_MASK(phy->id), + MPPHY_CMNCNT1_CH_EN(phy->id)); + setbits_le32(priv->base + MPPHY_PCS0REG5, MPPHY_PCS0REG5_CH(phy->id)); + setbits_le32(priv->base + MPPHY_PCS0REG1, MPPHY_PCS0REG1_VAL); + setbits_le32(priv->base + MPPHY_PXTEST(phy->id), MPPHY_PXTEST_BIT); + clrbits_le32(priv->base + MPPHY_PCS0REG5, MPPHY_PCS0REG5_CH(phy->id)); + clrbits_le32(priv->base + MPPHY_PCS0REG1, MPPHY_PCS0REG1_VAL); + clrbits_le32(priv->base + MPPHY_PXTEST(phy->id), MPPHY_PXTEST_BIT); + + /* Set PHY RX/TX reset and SRAM bypass mode */ + writel(MPPHY_PXRXCNT_RESET_VAL, priv->base + MPPHY_PXRXCNT(phy->id)); + writel(MPPHY_PXSRAMCNT_BYPASS, priv->base + MPPHY_PXSRAMCNT(phy->id)); + setbits_le32(priv->base + MPPHY_PXSRAMCNT(phy->id), MPPHY_PXSRAMCNT_BIT3); + + /* Clock supply settings */ + setbits_le32(priv->base + MPPHY_CMNCNT2, MPPHY_CMNCNT2_CLK_CH(phy->id)); + + setbits_le32(priv->base + MPPHY_PXREFCLK(phy->id), MPPHY_PXREFCLK_VAL); + + /* Release PHY RX/TX reset */ + writel(0x0, priv->base + MPPHY_PXRXCNT(phy->id)); + + /* Setting Context Restore Registers and select PHY2/PHY3 protocol */ + writel(MPPHY_CNTXT1_VALUE, priv->base + MPPHY_PXCNTXT1(phy->id)); + writel((phy->id == 0) ? MPPHY_CNTXT2_CH0_VALUE : MPPHY_CNTXT2_VALUE, + priv->base + MPPHY_PXCNTXT2(phy->id)); + writel(MPPHY_PXTXREQ_VAL, priv->base + MPPHY_PXTXREQ(phy->id)); + + return 0; +} + +static int mp_phy_late_init(struct phy *phy) +{ + struct mp_phy_priv *priv = dev_get_priv(phy->dev); + + writel(SRAM_CONTROL_SET_BIT, priv->base + MPPHY_PXSRAMCNT(phy->id)); + + return 0; +} + +static int mp_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + /* Current source code only supports Ethernet */ + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + return 0; +} + +static int mp_phy_of_xlate(struct phy *phy, struct ofnode_phandle_args *args) +{ + if (args->args_count > 2) + return -EINVAL; + + /* Set channel ID from first argument if available */ + if (args->args_count) + phy->id = args->args[0]; + else + phy->id = 0; + + return 0; +} + +static const struct phy_ops mp_phy_ops = { + .init = mp_phy_init, + .power_on = mp_phy_late_init, + .set_mode = mp_phy_set_mode, + .of_xlate = mp_phy_of_xlate, +}; + +static int mp_phy_probe(struct udevice *dev) +{ + struct mp_phy_priv *priv = dev_get_priv(dev); + int ret; + + /* Get base address from device tree */ + priv->base = dev_read_addr_ptr(dev); + if (!priv->base) + return -EINVAL; + + ret = clk_get_bulk(dev, &priv->clks); + if (ret < 0) + return ret; + + ret = clk_enable_bulk(&priv->clks); + if (ret) + goto err_clk_enable; + + ret = reset_get_bulk(dev, &priv->resets); + if (ret) + goto err_reset_get; + + ret = reset_assert_bulk(&priv->resets); + if (ret) + goto err_reset_assert; + + ret = reset_deassert_bulk(&priv->resets); + if (ret) + goto err_reset_assert; + + return 0; + +err_reset_assert: + reset_release_bulk(&priv->resets); +err_reset_get: + clk_disable_bulk(&priv->clks); +err_clk_enable: + clk_release_bulk(&priv->clks); + return ret; +} + +static const struct udevice_id mp_phy_ids[] = { + { .compatible = "renesas,r8a78000-multi-protocol-phy" }, + { } +}; + +U_BOOT_DRIVER(renesas_mpphy) = { + .name = "renesas_mpphy", + .id = UCLASS_PHY, + .of_match = mp_phy_ids, + .probe = mp_phy_probe, + .ops = &mp_phy_ops, + .priv_auto = sizeof(struct mp_phy_priv), +};