brintos

brintos / linux-shallow public Read only

0
0
Text · 4.0 KiB · 8955402 Raw
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