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 <thanh.quan.xn@renesas.com>
Signed-off-by: Phong Hoang <phong.hoang.wz@renesas.com>
Signed-off-by: Hai Pham <hai.pham.ud@renesas.com> #Fix License-Identifier
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
[Marek: Clean up macros, indent, clock and reset handling in probe,
        rename the driver and add r8a78000- into compatible string,
	update commit message.]
This commit is contained in:
Thanh Quan 2025-10-27 17:52:21 +01:00 committed by Marek Vasut
parent 152ff5696b
commit 3d50dba3a3
3 changed files with 232 additions and 0 deletions

View File

@ -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.

View File

@ -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

View File

@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Renesas Multi-Protocol PHY device driver
*
* Copyright (C) 2025 Renesas Electronics Corporation
*/
#include <asm/io.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <generic-phy.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <reset.h>
/* 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),
};