162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019 Baylibre, SAS. 362306a36Sopenharmony_ci * Author: Jerome Brunet <jbrunet@baylibre.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/iopoll.h> 1362306a36Sopenharmony_ci#include <linux/mdio-mux.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/phy.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define ETH_PLL_STS 0x40 1962306a36Sopenharmony_ci#define ETH_PLL_CTL0 0x44 2062306a36Sopenharmony_ci#define PLL_CTL0_LOCK_DIG BIT(30) 2162306a36Sopenharmony_ci#define PLL_CTL0_RST BIT(29) 2262306a36Sopenharmony_ci#define PLL_CTL0_EN BIT(28) 2362306a36Sopenharmony_ci#define PLL_CTL0_SEL BIT(23) 2462306a36Sopenharmony_ci#define PLL_CTL0_N GENMASK(14, 10) 2562306a36Sopenharmony_ci#define PLL_CTL0_M GENMASK(8, 0) 2662306a36Sopenharmony_ci#define PLL_LOCK_TIMEOUT 1000000 2762306a36Sopenharmony_ci#define PLL_MUX_NUM_PARENT 2 2862306a36Sopenharmony_ci#define ETH_PLL_CTL1 0x48 2962306a36Sopenharmony_ci#define ETH_PLL_CTL2 0x4c 3062306a36Sopenharmony_ci#define ETH_PLL_CTL3 0x50 3162306a36Sopenharmony_ci#define ETH_PLL_CTL4 0x54 3262306a36Sopenharmony_ci#define ETH_PLL_CTL5 0x58 3362306a36Sopenharmony_ci#define ETH_PLL_CTL6 0x5c 3462306a36Sopenharmony_ci#define ETH_PLL_CTL7 0x60 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define ETH_PHY_CNTL0 0x80 3762306a36Sopenharmony_ci#define EPHY_G12A_ID 0x33010180 3862306a36Sopenharmony_ci#define ETH_PHY_CNTL1 0x84 3962306a36Sopenharmony_ci#define PHY_CNTL1_ST_MODE GENMASK(2, 0) 4062306a36Sopenharmony_ci#define PHY_CNTL1_ST_PHYADD GENMASK(7, 3) 4162306a36Sopenharmony_ci#define EPHY_DFLT_ADD 8 4262306a36Sopenharmony_ci#define PHY_CNTL1_MII_MODE GENMASK(15, 14) 4362306a36Sopenharmony_ci#define EPHY_MODE_RMII 0x1 4462306a36Sopenharmony_ci#define PHY_CNTL1_CLK_EN BIT(16) 4562306a36Sopenharmony_ci#define PHY_CNTL1_CLKFREQ BIT(17) 4662306a36Sopenharmony_ci#define PHY_CNTL1_PHY_ENB BIT(18) 4762306a36Sopenharmony_ci#define ETH_PHY_CNTL2 0x88 4862306a36Sopenharmony_ci#define PHY_CNTL2_USE_INTERNAL BIT(5) 4962306a36Sopenharmony_ci#define PHY_CNTL2_SMI_SRC_MAC BIT(6) 5062306a36Sopenharmony_ci#define PHY_CNTL2_RX_CLK_EPHY BIT(9) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define MESON_G12A_MDIO_EXTERNAL_ID 0 5362306a36Sopenharmony_ci#define MESON_G12A_MDIO_INTERNAL_ID 1 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct g12a_mdio_mux { 5662306a36Sopenharmony_ci void __iomem *regs; 5762306a36Sopenharmony_ci void *mux_handle; 5862306a36Sopenharmony_ci struct clk *pll; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct g12a_ephy_pll { 6262306a36Sopenharmony_ci void __iomem *base; 6362306a36Sopenharmony_ci struct clk_hw hw; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define g12a_ephy_pll_to_dev(_hw) \ 6762306a36Sopenharmony_ci container_of(_hw, struct g12a_ephy_pll, hw) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic unsigned long g12a_ephy_pll_recalc_rate(struct clk_hw *hw, 7062306a36Sopenharmony_ci unsigned long parent_rate) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); 7362306a36Sopenharmony_ci u32 val, m, n; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci val = readl(pll->base + ETH_PLL_CTL0); 7662306a36Sopenharmony_ci m = FIELD_GET(PLL_CTL0_M, val); 7762306a36Sopenharmony_ci n = FIELD_GET(PLL_CTL0_N, val); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return parent_rate * m / n; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int g12a_ephy_pll_enable(struct clk_hw *hw) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); 8562306a36Sopenharmony_ci u32 val = readl(pll->base + ETH_PLL_CTL0); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Apply both enable an reset */ 8862306a36Sopenharmony_ci val |= PLL_CTL0_RST | PLL_CTL0_EN; 8962306a36Sopenharmony_ci writel(val, pll->base + ETH_PLL_CTL0); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Clear the reset to let PLL lock */ 9262306a36Sopenharmony_ci val &= ~PLL_CTL0_RST; 9362306a36Sopenharmony_ci writel(val, pll->base + ETH_PLL_CTL0); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Poll on the digital lock instead of the usual analog lock 9662306a36Sopenharmony_ci * This is done because bit 31 is unreliable on some SoC. Bit 9762306a36Sopenharmony_ci * 31 may indicate that the PLL is not lock even though the clock 9862306a36Sopenharmony_ci * is actually running 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci return readl_poll_timeout(pll->base + ETH_PLL_CTL0, val, 10162306a36Sopenharmony_ci val & PLL_CTL0_LOCK_DIG, 0, PLL_LOCK_TIMEOUT); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void g12a_ephy_pll_disable(struct clk_hw *hw) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); 10762306a36Sopenharmony_ci u32 val; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci val = readl(pll->base + ETH_PLL_CTL0); 11062306a36Sopenharmony_ci val &= ~PLL_CTL0_EN; 11162306a36Sopenharmony_ci val |= PLL_CTL0_RST; 11262306a36Sopenharmony_ci writel(val, pll->base + ETH_PLL_CTL0); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int g12a_ephy_pll_is_enabled(struct clk_hw *hw) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); 11862306a36Sopenharmony_ci unsigned int val; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci val = readl(pll->base + ETH_PLL_CTL0); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return (val & PLL_CTL0_LOCK_DIG) ? 1 : 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int g12a_ephy_pll_init(struct clk_hw *hw) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Apply PLL HW settings */ 13062306a36Sopenharmony_ci writel(0x29c0040a, pll->base + ETH_PLL_CTL0); 13162306a36Sopenharmony_ci writel(0x927e0000, pll->base + ETH_PLL_CTL1); 13262306a36Sopenharmony_ci writel(0xac5f49e5, pll->base + ETH_PLL_CTL2); 13362306a36Sopenharmony_ci writel(0x00000000, pll->base + ETH_PLL_CTL3); 13462306a36Sopenharmony_ci writel(0x00000000, pll->base + ETH_PLL_CTL4); 13562306a36Sopenharmony_ci writel(0x20200000, pll->base + ETH_PLL_CTL5); 13662306a36Sopenharmony_ci writel(0x0000c002, pll->base + ETH_PLL_CTL6); 13762306a36Sopenharmony_ci writel(0x00000023, pll->base + ETH_PLL_CTL7); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic const struct clk_ops g12a_ephy_pll_ops = { 14362306a36Sopenharmony_ci .recalc_rate = g12a_ephy_pll_recalc_rate, 14462306a36Sopenharmony_ci .is_enabled = g12a_ephy_pll_is_enabled, 14562306a36Sopenharmony_ci .enable = g12a_ephy_pll_enable, 14662306a36Sopenharmony_ci .disable = g12a_ephy_pll_disable, 14762306a36Sopenharmony_ci .init = g12a_ephy_pll_init, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int g12a_enable_internal_mdio(struct g12a_mdio_mux *priv) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u32 value; 15362306a36Sopenharmony_ci int ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Enable the phy clock */ 15662306a36Sopenharmony_ci if (!__clk_is_enabled(priv->pll)) { 15762306a36Sopenharmony_ci ret = clk_prepare_enable(priv->pll); 15862306a36Sopenharmony_ci if (ret) 15962306a36Sopenharmony_ci return ret; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Initialize ephy control */ 16362306a36Sopenharmony_ci writel(EPHY_G12A_ID, priv->regs + ETH_PHY_CNTL0); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Make sure we get a 0 -> 1 transition on the enable bit */ 16662306a36Sopenharmony_ci value = FIELD_PREP(PHY_CNTL1_ST_MODE, 3) | 16762306a36Sopenharmony_ci FIELD_PREP(PHY_CNTL1_ST_PHYADD, EPHY_DFLT_ADD) | 16862306a36Sopenharmony_ci FIELD_PREP(PHY_CNTL1_MII_MODE, EPHY_MODE_RMII) | 16962306a36Sopenharmony_ci PHY_CNTL1_CLK_EN | 17062306a36Sopenharmony_ci PHY_CNTL1_CLKFREQ; 17162306a36Sopenharmony_ci writel(value, priv->regs + ETH_PHY_CNTL1); 17262306a36Sopenharmony_ci writel(PHY_CNTL2_USE_INTERNAL | 17362306a36Sopenharmony_ci PHY_CNTL2_SMI_SRC_MAC | 17462306a36Sopenharmony_ci PHY_CNTL2_RX_CLK_EPHY, 17562306a36Sopenharmony_ci priv->regs + ETH_PHY_CNTL2); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci value |= PHY_CNTL1_PHY_ENB; 17862306a36Sopenharmony_ci writel(value, priv->regs + ETH_PHY_CNTL1); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* The phy needs a bit of time to power up */ 18162306a36Sopenharmony_ci mdelay(10); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int g12a_enable_external_mdio(struct g12a_mdio_mux *priv) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci /* Reset the mdio bus mux */ 18962306a36Sopenharmony_ci writel_relaxed(0x0, priv->regs + ETH_PHY_CNTL2); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Disable the phy clock if enabled */ 19262306a36Sopenharmony_ci if (__clk_is_enabled(priv->pll)) 19362306a36Sopenharmony_ci clk_disable_unprepare(priv->pll); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int g12a_mdio_switch_fn(int current_child, int desired_child, 19962306a36Sopenharmony_ci void *data) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct g12a_mdio_mux *priv = dev_get_drvdata(data); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (current_child == desired_child) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci switch (desired_child) { 20762306a36Sopenharmony_ci case MESON_G12A_MDIO_EXTERNAL_ID: 20862306a36Sopenharmony_ci return g12a_enable_external_mdio(priv); 20962306a36Sopenharmony_ci case MESON_G12A_MDIO_INTERNAL_ID: 21062306a36Sopenharmony_ci return g12a_enable_internal_mdio(priv); 21162306a36Sopenharmony_ci default: 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct of_device_id g12a_mdio_mux_match[] = { 21762306a36Sopenharmony_ci { .compatible = "amlogic,g12a-mdio-mux", }, 21862306a36Sopenharmony_ci {}, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, g12a_mdio_mux_match); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int g12a_ephy_glue_clk_register(struct device *dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct g12a_mdio_mux *priv = dev_get_drvdata(dev); 22562306a36Sopenharmony_ci const char *parent_names[PLL_MUX_NUM_PARENT]; 22662306a36Sopenharmony_ci struct clk_init_data init; 22762306a36Sopenharmony_ci struct g12a_ephy_pll *pll; 22862306a36Sopenharmony_ci struct clk_mux *mux; 22962306a36Sopenharmony_ci struct clk *clk; 23062306a36Sopenharmony_ci char *name; 23162306a36Sopenharmony_ci int i; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* get the mux parents */ 23462306a36Sopenharmony_ci for (i = 0; i < PLL_MUX_NUM_PARENT; i++) { 23562306a36Sopenharmony_ci char in_name[8]; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci snprintf(in_name, sizeof(in_name), "clkin%d", i); 23862306a36Sopenharmony_ci clk = devm_clk_get(dev, in_name); 23962306a36Sopenharmony_ci if (IS_ERR(clk)) 24062306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(clk), 24162306a36Sopenharmony_ci "Missing clock %s\n", in_name); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci parent_names[i] = __clk_get_name(clk); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* create the input mux */ 24762306a36Sopenharmony_ci mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 24862306a36Sopenharmony_ci if (!mux) 24962306a36Sopenharmony_ci return -ENOMEM; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s#mux", dev_name(dev)); 25262306a36Sopenharmony_ci if (!name) 25362306a36Sopenharmony_ci return -ENOMEM; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci init.name = name; 25662306a36Sopenharmony_ci init.ops = &clk_mux_ro_ops; 25762306a36Sopenharmony_ci init.flags = 0; 25862306a36Sopenharmony_ci init.parent_names = parent_names; 25962306a36Sopenharmony_ci init.num_parents = PLL_MUX_NUM_PARENT; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci mux->reg = priv->regs + ETH_PLL_CTL0; 26262306a36Sopenharmony_ci mux->shift = __ffs(PLL_CTL0_SEL); 26362306a36Sopenharmony_ci mux->mask = PLL_CTL0_SEL >> mux->shift; 26462306a36Sopenharmony_ci mux->hw.init = &init; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci clk = devm_clk_register(dev, &mux->hw); 26762306a36Sopenharmony_ci kfree(name); 26862306a36Sopenharmony_ci if (IS_ERR(clk)) { 26962306a36Sopenharmony_ci dev_err(dev, "failed to register input mux\n"); 27062306a36Sopenharmony_ci return PTR_ERR(clk); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* create the pll */ 27462306a36Sopenharmony_ci pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); 27562306a36Sopenharmony_ci if (!pll) 27662306a36Sopenharmony_ci return -ENOMEM; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s#pll", dev_name(dev)); 27962306a36Sopenharmony_ci if (!name) 28062306a36Sopenharmony_ci return -ENOMEM; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci init.name = name; 28362306a36Sopenharmony_ci init.ops = &g12a_ephy_pll_ops; 28462306a36Sopenharmony_ci init.flags = 0; 28562306a36Sopenharmony_ci parent_names[0] = __clk_get_name(clk); 28662306a36Sopenharmony_ci init.parent_names = parent_names; 28762306a36Sopenharmony_ci init.num_parents = 1; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci pll->base = priv->regs; 29062306a36Sopenharmony_ci pll->hw.init = &init; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci clk = devm_clk_register(dev, &pll->hw); 29362306a36Sopenharmony_ci kfree(name); 29462306a36Sopenharmony_ci if (IS_ERR(clk)) { 29562306a36Sopenharmony_ci dev_err(dev, "failed to register input mux\n"); 29662306a36Sopenharmony_ci return PTR_ERR(clk); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci priv->pll = clk; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int g12a_mdio_mux_probe(struct platform_device *pdev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 30762306a36Sopenharmony_ci struct g12a_mdio_mux *priv; 30862306a36Sopenharmony_ci struct clk *pclk; 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 31262306a36Sopenharmony_ci if (!priv) 31362306a36Sopenharmony_ci return -ENOMEM; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci priv->regs = devm_platform_ioremap_resource(pdev, 0); 31862306a36Sopenharmony_ci if (IS_ERR(priv->regs)) 31962306a36Sopenharmony_ci return PTR_ERR(priv->regs); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci pclk = devm_clk_get_enabled(dev, "pclk"); 32262306a36Sopenharmony_ci if (IS_ERR(pclk)) 32362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(pclk), 32462306a36Sopenharmony_ci "failed to get peripheral clock\n"); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* Register PLL in CCF */ 32762306a36Sopenharmony_ci ret = g12a_ephy_glue_clk_register(dev); 32862306a36Sopenharmony_ci if (ret) 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ret = mdio_mux_init(dev, dev->of_node, g12a_mdio_switch_fn, 33262306a36Sopenharmony_ci &priv->mux_handle, dev, NULL); 33362306a36Sopenharmony_ci if (ret) 33462306a36Sopenharmony_ci dev_err_probe(dev, ret, "mdio multiplexer init failed\n"); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int g12a_mdio_mux_remove(struct platform_device *pdev) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct g12a_mdio_mux *priv = platform_get_drvdata(pdev); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci mdio_mux_uninit(priv->mux_handle); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (__clk_is_enabled(priv->pll)) 34662306a36Sopenharmony_ci clk_disable_unprepare(priv->pll); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic struct platform_driver g12a_mdio_mux_driver = { 35262306a36Sopenharmony_ci .probe = g12a_mdio_mux_probe, 35362306a36Sopenharmony_ci .remove = g12a_mdio_mux_remove, 35462306a36Sopenharmony_ci .driver = { 35562306a36Sopenharmony_ci .name = "g12a-mdio_mux", 35662306a36Sopenharmony_ci .of_match_table = g12a_mdio_mux_match, 35762306a36Sopenharmony_ci }, 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_cimodule_platform_driver(g12a_mdio_mux_driver); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic G12a MDIO multiplexer driver"); 36262306a36Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 36362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 364