163 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2022 Baylibre, SAS.3 * Author: Jerome Brunet <jbrunet@baylibre.com>4 */5 6#include <linux/bitfield.h>7#include <linux/delay.h>8#include <linux/clk.h>9#include <linux/io.h>10#include <linux/mdio-mux.h>11#include <linux/module.h>12#include <linux/platform_device.h>13 14#define ETH_REG2 0x015#define REG2_PHYID GENMASK(21, 0)16#define EPHY_GXL_ID 0x11018117#define REG2_LEDACT GENMASK(23, 22)18#define REG2_LEDLINK GENMASK(25, 24)19#define REG2_DIV4SEL BIT(27)20#define REG2_ADCBYPASS BIT(30)21#define REG2_CLKINSEL BIT(31)22#define ETH_REG3 0x423#define REG3_ENH BIT(3)24#define REG3_CFGMODE GENMASK(6, 4)25#define REG3_AUTOMDIX BIT(7)26#define REG3_PHYADDR GENMASK(12, 8)27#define REG3_PWRUPRST BIT(21)28#define REG3_PWRDOWN BIT(22)29#define REG3_LEDPOL BIT(23)30#define REG3_PHYMDI BIT(26)31#define REG3_CLKINEN BIT(29)32#define REG3_PHYIP BIT(30)33#define REG3_PHYEN BIT(31)34#define ETH_REG4 0x835#define REG4_PWRUPRSTSIG BIT(0)36 37#define MESON_GXL_MDIO_EXTERNAL_ID 038#define MESON_GXL_MDIO_INTERNAL_ID 139 40struct gxl_mdio_mux {41 void __iomem *regs;42 void *mux_handle;43};44 45static void gxl_enable_internal_mdio(struct gxl_mdio_mux *priv)46{47 u32 val;48 49 /* Setup the internal phy */50 val = (REG3_ENH |51 FIELD_PREP(REG3_CFGMODE, 0x7) |52 REG3_AUTOMDIX |53 FIELD_PREP(REG3_PHYADDR, 8) |54 REG3_LEDPOL |55 REG3_PHYMDI |56 REG3_CLKINEN |57 REG3_PHYIP);58 59 writel(REG4_PWRUPRSTSIG, priv->regs + ETH_REG4);60 writel(val, priv->regs + ETH_REG3);61 mdelay(10);62 63 /* NOTE: The HW kept the phy id configurable at runtime.64 * The id below is arbitrary. It is the one used in the vendor code.65 * The only constraint is that it must match the one in66 * drivers/net/phy/meson-gxl.c to properly match the PHY.67 */68 writel(FIELD_PREP(REG2_PHYID, EPHY_GXL_ID),69 priv->regs + ETH_REG2);70 71 /* Enable the internal phy */72 val |= REG3_PHYEN;73 writel(val, priv->regs + ETH_REG3);74 writel(0, priv->regs + ETH_REG4);75 76 /* The phy needs a bit of time to power up */77 mdelay(10);78}79 80static void gxl_enable_external_mdio(struct gxl_mdio_mux *priv)81{82 /* Reset the mdio bus mux to the external phy */83 writel(0, priv->regs + ETH_REG3);84}85 86static int gxl_mdio_switch_fn(int current_child, int desired_child,87 void *data)88{89 struct gxl_mdio_mux *priv = dev_get_drvdata(data);90 91 if (current_child == desired_child)92 return 0;93 94 switch (desired_child) {95 case MESON_GXL_MDIO_EXTERNAL_ID:96 gxl_enable_external_mdio(priv);97 break;98 case MESON_GXL_MDIO_INTERNAL_ID:99 gxl_enable_internal_mdio(priv);100 break;101 default:102 return -EINVAL;103 }104 105 return 0;106}107 108static const struct of_device_id gxl_mdio_mux_match[] = {109 { .compatible = "amlogic,gxl-mdio-mux", },110 {},111};112MODULE_DEVICE_TABLE(of, gxl_mdio_mux_match);113 114static int gxl_mdio_mux_probe(struct platform_device *pdev)115{116 struct device *dev = &pdev->dev;117 struct gxl_mdio_mux *priv;118 struct clk *rclk;119 int ret;120 121 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);122 if (!priv)123 return -ENOMEM;124 platform_set_drvdata(pdev, priv);125 126 priv->regs = devm_platform_ioremap_resource(pdev, 0);127 if (IS_ERR(priv->regs))128 return PTR_ERR(priv->regs);129 130 rclk = devm_clk_get_enabled(dev, "ref");131 if (IS_ERR(rclk))132 return dev_err_probe(dev, PTR_ERR(rclk),133 "failed to get reference clock\n");134 135 ret = mdio_mux_init(dev, dev->of_node, gxl_mdio_switch_fn,136 &priv->mux_handle, dev, NULL);137 if (ret)138 dev_err_probe(dev, ret, "mdio multiplexer init failed\n");139 140 return ret;141}142 143static void gxl_mdio_mux_remove(struct platform_device *pdev)144{145 struct gxl_mdio_mux *priv = platform_get_drvdata(pdev);146 147 mdio_mux_uninit(priv->mux_handle);148}149 150static struct platform_driver gxl_mdio_mux_driver = {151 .probe = gxl_mdio_mux_probe,152 .remove_new = gxl_mdio_mux_remove,153 .driver = {154 .name = "gxl-mdio-mux",155 .of_match_table = gxl_mdio_mux_match,156 },157};158module_platform_driver(gxl_mdio_mux_driver);159 160MODULE_DESCRIPTION("Amlogic GXL MDIO multiplexer driver");161MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");162MODULE_LICENSE("GPL");163