1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2022 Baylibre, SAS. 4 * Author: Jerome Brunet <jbrunet@baylibre.com> 5 * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org> 6 */ 7 8#include <dm.h> 9#include <errno.h> 10#include <log.h> 11#include <miiphy.h> 12#include <asm/io.h> 13#include <linux/bitfield.h> 14#include <linux/delay.h> 15 16#define ETH_REG2 0x0 17#define REG2_PHYID GENMASK(21, 0) 18#define EPHY_GXL_ID 0x110181 19#define REG2_LEDACT GENMASK(23, 22) 20#define REG2_LEDLINK GENMASK(25, 24) 21#define REG2_DIV4SEL BIT(27) 22#define REG2_ADCBYPASS BIT(30) 23#define REG2_CLKINSEL BIT(31) 24#define ETH_REG3 0x4 25#define REG3_ENH BIT(3) 26#define REG3_CFGMODE GENMASK(6, 4) 27#define REG3_AUTOMDIX BIT(7) 28#define REG3_PHYADDR GENMASK(12, 8) 29#define REG3_PWRUPRST BIT(21) 30#define REG3_PWRDOWN BIT(22) 31#define REG3_LEDPOL BIT(23) 32#define REG3_PHYMDI BIT(26) 33#define REG3_CLKINEN BIT(29) 34#define REG3_PHYIP BIT(30) 35#define REG3_PHYEN BIT(31) 36#define ETH_REG4 0x8 37#define REG4_PWRUPRSTSIG BIT(0) 38 39#define MESON_GXL_MDIO_EXTERNAL_ID 0 40#define MESON_GXL_MDIO_INTERNAL_ID 1 41 42struct mdio_mux_meson_gxl_priv { 43 phys_addr_t regs; 44}; 45 46static int meson_gxl_enable_internal_mdio(struct mdio_mux_meson_gxl_priv *priv) 47{ 48 u32 val; 49 50 /* Setup the internal phy */ 51 val = (REG3_ENH | 52 FIELD_PREP(REG3_CFGMODE, 0x7) | 53 REG3_AUTOMDIX | 54 FIELD_PREP(REG3_PHYADDR, 8) | 55 REG3_LEDPOL | 56 REG3_PHYMDI | 57 REG3_CLKINEN | 58 REG3_PHYIP); 59 60 writel(REG4_PWRUPRSTSIG, priv->regs + ETH_REG4); 61 writel(val, priv->regs + ETH_REG3); 62 mdelay(10); 63 64 /* NOTE: The HW kept the phy id configurable at runtime. 65 * The id below is arbitrary. It is the one used in the vendor code. 66 * The only constraint is that it must match the one in 67 * drivers/net/phy/meson-gxl.c to properly match the PHY. 68 */ 69 writel(FIELD_PREP(REG2_PHYID, EPHY_GXL_ID), 70 priv->regs + ETH_REG2); 71 72 /* Enable the internal phy */ 73 val |= REG3_PHYEN; 74 writel(val, priv->regs + ETH_REG3); 75 writel(0, priv->regs + ETH_REG4); 76 77 /* The phy needs a bit of time to power up */ 78 mdelay(10); 79 80 return 0; 81} 82 83static int meson_gxl_enable_external_mdio(struct mdio_mux_meson_gxl_priv *priv) 84{ 85 /* Reset the mdio bus mux to the external phy */ 86 writel(0, priv->regs + ETH_REG3); 87 88 return 0; 89} 90 91static int mdio_mux_meson_gxl_select(struct udevice *mux, int cur, int sel) 92{ 93 struct mdio_mux_meson_gxl_priv *priv = dev_get_priv(mux); 94 95 debug("%s: %x -> %x\n", __func__, (u32)cur, (u32)sel); 96 97 /* if last selection didn't change we're good to go */ 98 if (cur == sel) 99 return 0; 100 101 switch (sel) { 102 case MESON_GXL_MDIO_EXTERNAL_ID: 103 return meson_gxl_enable_external_mdio(priv); 104 case MESON_GXL_MDIO_INTERNAL_ID: 105 return meson_gxl_enable_internal_mdio(priv); 106 default: 107 return -EINVAL; 108 } 109 110 return 0; 111} 112 113static const struct mdio_mux_ops mdio_mux_meson_gxl_ops = { 114 .select = mdio_mux_meson_gxl_select, 115}; 116 117static int mdio_mux_meson_gxl_probe(struct udevice *dev) 118{ 119 struct mdio_mux_meson_gxl_priv *priv = dev_get_priv(dev); 120 121 priv->regs = dev_read_addr(dev); 122 123 return 0; 124} 125 126static const struct udevice_id mdio_mux_meson_gxl_ids[] = { 127 { .compatible = "amlogic,gxl-mdio-mux" }, 128 { } 129}; 130 131U_BOOT_DRIVER(mdio_mux_meson_gxl) = { 132 .name = "mdio_mux_meson_gxl", 133 .id = UCLASS_MDIO_MUX, 134 .of_match = mdio_mux_meson_gxl_ids, 135 .probe = mdio_mux_meson_gxl_probe, 136 .ops = &mdio_mux_meson_gxl_ops, 137 .priv_auto = sizeof(struct mdio_mux_meson_gxl_priv), 138}; 139