// 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), };