mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-12-18 16:01:32 +01:00
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:
parent
152ff5696b
commit
3d50dba3a3
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
225
drivers/phy/renesas/r8a78000-mp-phy.c
Normal file
225
drivers/phy/renesas/r8a78000-mp-phy.c
Normal 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),
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user