From: Neil Armstrong Date: Wed, 13 Dec 2023 09:30:12 +0000 (+0100) Subject: net: Add Amlogic GXL MDIO Mux driver X-Git-Url: http://git.dujemihanovic.xyz/img/sics.gif?a=commitdiff_plain;h=b4534ddefff07a083c84c11d944460ed528df435;p=u-boot.git net: Add Amlogic GXL MDIO Mux driver Port the mdio-mux-meson-gxl.c Linux driver introduced in [1], and adapt it to U-Boot. This driver is needed to boot U-Boot with Linux DT since v6.4, since it switched the MDIO mux from the mmio to a proper GXL driver. [1] 9a24e1ff4326 ("net: mdio: add amlogic gxl mdio mux support") Link: https://lore.kernel.org/r/20231213-u-boot-gxl-mdio-mux-v2-1-c56bb02a75ea@linaro.org Signed-off-by: Neil Armstrong --- diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 23ad2c29d0..b2d7b49976 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -980,4 +980,11 @@ config MDIO_MUX_MESON_G12A This driver is used for the MDIO mux found on the Amlogic G12A & compatible SoCs. +config MDIO_MUX_MESON_GXL + bool "MDIO MUX for Amlogic Meson GXL SoCs" + depends on DM_MDIO_MUX + help + This driver is used for the MDIO mux found on the Amlogic GXL & compatible + SoCs. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f9aed1646c..6677366ebd 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o obj-$(CONFIG_MDIO_MUX_MESON_G12A) += mdio_mux_meson_g12a.o +obj-$(CONFIG_MDIO_MUX_MESON_GXL) += mdio_mux_meson_gxl.o obj-$(CONFIG_MDIO_MUX_MMIOREG) += mdio_mux_mmioreg.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o diff --git a/drivers/net/mdio_mux_meson_gxl.c b/drivers/net/mdio_mux_meson_gxl.c new file mode 100644 index 0000000000..8ef3ae598b --- /dev/null +++ b/drivers/net/mdio_mux_meson_gxl.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Baylibre, SAS. + * Author: Jerome Brunet + * Copyright (c) 2023 Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ETH_REG2 0x0 +#define REG2_PHYID GENMASK(21, 0) +#define EPHY_GXL_ID 0x110181 +#define REG2_LEDACT GENMASK(23, 22) +#define REG2_LEDLINK GENMASK(25, 24) +#define REG2_DIV4SEL BIT(27) +#define REG2_ADCBYPASS BIT(30) +#define REG2_CLKINSEL BIT(31) +#define ETH_REG3 0x4 +#define REG3_ENH BIT(3) +#define REG3_CFGMODE GENMASK(6, 4) +#define REG3_AUTOMDIX BIT(7) +#define REG3_PHYADDR GENMASK(12, 8) +#define REG3_PWRUPRST BIT(21) +#define REG3_PWRDOWN BIT(22) +#define REG3_LEDPOL BIT(23) +#define REG3_PHYMDI BIT(26) +#define REG3_CLKINEN BIT(29) +#define REG3_PHYIP BIT(30) +#define REG3_PHYEN BIT(31) +#define ETH_REG4 0x8 +#define REG4_PWRUPRSTSIG BIT(0) + +#define MESON_GXL_MDIO_EXTERNAL_ID 0 +#define MESON_GXL_MDIO_INTERNAL_ID 1 + +struct mdio_mux_meson_gxl_priv { + phys_addr_t regs; +}; + +static int meson_gxl_enable_internal_mdio(struct mdio_mux_meson_gxl_priv *priv) +{ + u32 val; + + /* Setup the internal phy */ + val = (REG3_ENH | + FIELD_PREP(REG3_CFGMODE, 0x7) | + REG3_AUTOMDIX | + FIELD_PREP(REG3_PHYADDR, 8) | + REG3_LEDPOL | + REG3_PHYMDI | + REG3_CLKINEN | + REG3_PHYIP); + + writel(REG4_PWRUPRSTSIG, priv->regs + ETH_REG4); + writel(val, priv->regs + ETH_REG3); + mdelay(10); + + /* NOTE: The HW kept the phy id configurable at runtime. + * The id below is arbitrary. It is the one used in the vendor code. + * The only constraint is that it must match the one in + * drivers/net/phy/meson-gxl.c to properly match the PHY. + */ + writel(FIELD_PREP(REG2_PHYID, EPHY_GXL_ID), + priv->regs + ETH_REG2); + + /* Enable the internal phy */ + val |= REG3_PHYEN; + writel(val, priv->regs + ETH_REG3); + writel(0, priv->regs + ETH_REG4); + + /* The phy needs a bit of time to power up */ + mdelay(10); + + return 0; +} + +static int meson_gxl_enable_external_mdio(struct mdio_mux_meson_gxl_priv *priv) +{ + /* Reset the mdio bus mux to the external phy */ + writel(0, priv->regs + ETH_REG3); + + return 0; +} + +static int mdio_mux_meson_gxl_select(struct udevice *mux, int cur, int sel) +{ + struct mdio_mux_meson_gxl_priv *priv = dev_get_priv(mux); + + debug("%s: %x -> %x\n", __func__, (u32)cur, (u32)sel); + + /* if last selection didn't change we're good to go */ + if (cur == sel) + return 0; + + switch (sel) { + case MESON_GXL_MDIO_EXTERNAL_ID: + return meson_gxl_enable_external_mdio(priv); + case MESON_GXL_MDIO_INTERNAL_ID: + return meson_gxl_enable_internal_mdio(priv); + default: + return -EINVAL; + } + + return 0; +} + +static const struct mdio_mux_ops mdio_mux_meson_gxl_ops = { + .select = mdio_mux_meson_gxl_select, +}; + +static int mdio_mux_meson_gxl_probe(struct udevice *dev) +{ + struct mdio_mux_meson_gxl_priv *priv = dev_get_priv(dev); + + priv->regs = dev_read_addr(dev); + + return 0; +} + +static const struct udevice_id mdio_mux_meson_gxl_ids[] = { + { .compatible = "amlogic,gxl-mdio-mux" }, + { } +}; + +U_BOOT_DRIVER(mdio_mux_meson_gxl) = { + .name = "mdio_mux_meson_gxl", + .id = UCLASS_MDIO_MUX, + .of_match = mdio_mux_meson_gxl_ids, + .probe = mdio_mux_meson_gxl_probe, + .ops = &mdio_mux_meson_gxl_ops, + .priv_auto = sizeof(struct mdio_mux_meson_gxl_priv), +};