diff options
Diffstat (limited to 'drivers/net/phy/mdio-mux-meson-g12a.c')
-rw-r--r-- | drivers/net/phy/mdio-mux-meson-g12a.c | 380 |
1 files changed, 0 insertions, 380 deletions
diff --git a/drivers/net/phy/mdio-mux-meson-g12a.c b/drivers/net/phy/mdio-mux-meson-g12a.c deleted file mode 100644 index bf86c9c7a288..000000000000 --- a/drivers/net/phy/mdio-mux-meson-g12a.c +++ /dev/null @@ -1,380 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2019 Baylibre, SAS. - * Author: Jerome Brunet <jbrunet@baylibre.com> - */ - -#include <linux/bitfield.h> -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/mdio-mux.h> -#include <linux/module.h> -#include <linux/phy.h> -#include <linux/platform_device.h> - -#define ETH_PLL_STS 0x40 -#define ETH_PLL_CTL0 0x44 -#define PLL_CTL0_LOCK_DIG BIT(30) -#define PLL_CTL0_RST BIT(29) -#define PLL_CTL0_EN BIT(28) -#define PLL_CTL0_SEL BIT(23) -#define PLL_CTL0_N GENMASK(14, 10) -#define PLL_CTL0_M GENMASK(8, 0) -#define PLL_LOCK_TIMEOUT 1000000 -#define PLL_MUX_NUM_PARENT 2 -#define ETH_PLL_CTL1 0x48 -#define ETH_PLL_CTL2 0x4c -#define ETH_PLL_CTL3 0x50 -#define ETH_PLL_CTL4 0x54 -#define ETH_PLL_CTL5 0x58 -#define ETH_PLL_CTL6 0x5c -#define ETH_PLL_CTL7 0x60 - -#define ETH_PHY_CNTL0 0x80 -#define EPHY_G12A_ID 0x33010180 -#define ETH_PHY_CNTL1 0x84 -#define PHY_CNTL1_ST_MODE GENMASK(2, 0) -#define PHY_CNTL1_ST_PHYADD GENMASK(7, 3) -#define EPHY_DFLT_ADD 8 -#define PHY_CNTL1_MII_MODE GENMASK(15, 14) -#define EPHY_MODE_RMII 0x1 -#define PHY_CNTL1_CLK_EN BIT(16) -#define PHY_CNTL1_CLKFREQ BIT(17) -#define PHY_CNTL1_PHY_ENB BIT(18) -#define ETH_PHY_CNTL2 0x88 -#define PHY_CNTL2_USE_INTERNAL BIT(5) -#define PHY_CNTL2_SMI_SRC_MAC BIT(6) -#define PHY_CNTL2_RX_CLK_EPHY BIT(9) - -#define MESON_G12A_MDIO_EXTERNAL_ID 0 -#define MESON_G12A_MDIO_INTERNAL_ID 1 - -struct g12a_mdio_mux { - bool pll_is_enabled; - void __iomem *regs; - void *mux_handle; - struct clk *pclk; - struct clk *pll; -}; - -struct g12a_ephy_pll { - void __iomem *base; - struct clk_hw hw; -}; - -#define g12a_ephy_pll_to_dev(_hw) \ - container_of(_hw, struct g12a_ephy_pll, hw) - -static unsigned long g12a_ephy_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); - u32 val, m, n; - - val = readl(pll->base + ETH_PLL_CTL0); - m = FIELD_GET(PLL_CTL0_M, val); - n = FIELD_GET(PLL_CTL0_N, val); - - return parent_rate * m / n; -} - -static int g12a_ephy_pll_enable(struct clk_hw *hw) -{ - struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); - u32 val = readl(pll->base + ETH_PLL_CTL0); - - /* Apply both enable an reset */ - val |= PLL_CTL0_RST | PLL_CTL0_EN; - writel(val, pll->base + ETH_PLL_CTL0); - - /* Clear the reset to let PLL lock */ - val &= ~PLL_CTL0_RST; - writel(val, pll->base + ETH_PLL_CTL0); - - /* Poll on the digital lock instead of the usual analog lock - * This is done because bit 31 is unreliable on some SoC. Bit - * 31 may indicate that the PLL is not lock eventhough the clock - * is actually running - */ - return readl_poll_timeout(pll->base + ETH_PLL_CTL0, val, - val & PLL_CTL0_LOCK_DIG, 0, PLL_LOCK_TIMEOUT); -} - -static void g12a_ephy_pll_disable(struct clk_hw *hw) -{ - struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); - u32 val; - - val = readl(pll->base + ETH_PLL_CTL0); - val &= ~PLL_CTL0_EN; - val |= PLL_CTL0_RST; - writel(val, pll->base + ETH_PLL_CTL0); -} - -static int g12a_ephy_pll_is_enabled(struct clk_hw *hw) -{ - struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); - unsigned int val; - - val = readl(pll->base + ETH_PLL_CTL0); - - return (val & PLL_CTL0_LOCK_DIG) ? 1 : 0; -} - -static int g12a_ephy_pll_init(struct clk_hw *hw) -{ - struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw); - - /* Apply PLL HW settings */ - writel(0x29c0040a, pll->base + ETH_PLL_CTL0); - writel(0x927e0000, pll->base + ETH_PLL_CTL1); - writel(0xac5f49e5, pll->base + ETH_PLL_CTL2); - writel(0x00000000, pll->base + ETH_PLL_CTL3); - writel(0x00000000, pll->base + ETH_PLL_CTL4); - writel(0x20200000, pll->base + ETH_PLL_CTL5); - writel(0x0000c002, pll->base + ETH_PLL_CTL6); - writel(0x00000023, pll->base + ETH_PLL_CTL7); - - return 0; -} - -static const struct clk_ops g12a_ephy_pll_ops = { - .recalc_rate = g12a_ephy_pll_recalc_rate, - .is_enabled = g12a_ephy_pll_is_enabled, - .enable = g12a_ephy_pll_enable, - .disable = g12a_ephy_pll_disable, - .init = g12a_ephy_pll_init, -}; - -static int g12a_enable_internal_mdio(struct g12a_mdio_mux *priv) -{ - int ret; - - /* Enable the phy clock */ - if (!priv->pll_is_enabled) { - ret = clk_prepare_enable(priv->pll); - if (ret) - return ret; - } - - priv->pll_is_enabled = true; - - /* Initialize ephy control */ - writel(EPHY_G12A_ID, priv->regs + ETH_PHY_CNTL0); - writel(FIELD_PREP(PHY_CNTL1_ST_MODE, 3) | - FIELD_PREP(PHY_CNTL1_ST_PHYADD, EPHY_DFLT_ADD) | - FIELD_PREP(PHY_CNTL1_MII_MODE, EPHY_MODE_RMII) | - PHY_CNTL1_CLK_EN | - PHY_CNTL1_CLKFREQ | - PHY_CNTL1_PHY_ENB, - priv->regs + ETH_PHY_CNTL1); - writel(PHY_CNTL2_USE_INTERNAL | - PHY_CNTL2_SMI_SRC_MAC | - PHY_CNTL2_RX_CLK_EPHY, - priv->regs + ETH_PHY_CNTL2); - - return 0; -} - -static int g12a_enable_external_mdio(struct g12a_mdio_mux *priv) -{ - /* Reset the mdio bus mux */ - writel_relaxed(0x0, priv->regs + ETH_PHY_CNTL2); - - /* Disable the phy clock if enabled */ - if (priv->pll_is_enabled) { - clk_disable_unprepare(priv->pll); - priv->pll_is_enabled = false; - } - - return 0; -} - -static int g12a_mdio_switch_fn(int current_child, int desired_child, - void *data) -{ - struct g12a_mdio_mux *priv = dev_get_drvdata(data); - - if (current_child == desired_child) - return 0; - - switch (desired_child) { - case MESON_G12A_MDIO_EXTERNAL_ID: - return g12a_enable_external_mdio(priv); - case MESON_G12A_MDIO_INTERNAL_ID: - return g12a_enable_internal_mdio(priv); - default: - return -EINVAL; - } -} - -static const struct of_device_id g12a_mdio_mux_match[] = { - { .compatible = "amlogic,g12a-mdio-mux", }, - {}, -}; -MODULE_DEVICE_TABLE(of, g12a_mdio_mux_match); - -static int g12a_ephy_glue_clk_register(struct device *dev) -{ - struct g12a_mdio_mux *priv = dev_get_drvdata(dev); - const char *parent_names[PLL_MUX_NUM_PARENT]; - struct clk_init_data init; - struct g12a_ephy_pll *pll; - struct clk_mux *mux; - struct clk *clk; - char *name; - int i; - - /* get the mux parents */ - for (i = 0; i < PLL_MUX_NUM_PARENT; i++) { - char in_name[8]; - - snprintf(in_name, sizeof(in_name), "clkin%d", i); - clk = devm_clk_get(dev, in_name); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - dev_err(dev, "Missing clock %s\n", in_name); - return PTR_ERR(clk); - } - - parent_names[i] = __clk_get_name(clk); - } - - /* create the input mux */ - mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); - if (!mux) - return -ENOMEM; - - name = kasprintf(GFP_KERNEL, "%s#mux", dev_name(dev)); - if (!name) - return -ENOMEM; - - init.name = name; - init.ops = &clk_mux_ro_ops; - init.flags = 0; - init.parent_names = parent_names; - init.num_parents = PLL_MUX_NUM_PARENT; - - mux->reg = priv->regs + ETH_PLL_CTL0; - mux->shift = __ffs(PLL_CTL0_SEL); - mux->mask = PLL_CTL0_SEL >> mux->shift; - mux->hw.init = &init; - - clk = devm_clk_register(dev, &mux->hw); - kfree(name); - if (IS_ERR(clk)) { - dev_err(dev, "failed to register input mux\n"); - return PTR_ERR(clk); - } - - /* create the pll */ - pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); - if (!pll) - return -ENOMEM; - - name = kasprintf(GFP_KERNEL, "%s#pll", dev_name(dev)); - if (!name) - return -ENOMEM; - - init.name = name; - init.ops = &g12a_ephy_pll_ops; - init.flags = 0; - parent_names[0] = __clk_get_name(clk); - init.parent_names = parent_names; - init.num_parents = 1; - - pll->base = priv->regs; - pll->hw.init = &init; - - clk = devm_clk_register(dev, &pll->hw); - kfree(name); - if (IS_ERR(clk)) { - dev_err(dev, "failed to register input mux\n"); - return PTR_ERR(clk); - } - - priv->pll = clk; - - return 0; -} - -static int g12a_mdio_mux_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct g12a_mdio_mux *priv; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_set_drvdata(pdev, priv); - - priv->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->regs)) - return PTR_ERR(priv->regs); - - priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get peripheral clock\n"); - return ret; - } - - /* Make sure the device registers are clocked */ - ret = clk_prepare_enable(priv->pclk); - if (ret) { - dev_err(dev, "failed to enable peripheral clock"); - return ret; - } - - /* Register PLL in CCF */ - ret = g12a_ephy_glue_clk_register(dev); - if (ret) - goto err; - - ret = mdio_mux_init(dev, dev->of_node, g12a_mdio_switch_fn, - &priv->mux_handle, dev, NULL); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "mdio multiplexer init failed: %d", ret); - goto err; - } - - return 0; - -err: - clk_disable_unprepare(priv->pclk); - return ret; -} - -static int g12a_mdio_mux_remove(struct platform_device *pdev) -{ - struct g12a_mdio_mux *priv = platform_get_drvdata(pdev); - - mdio_mux_uninit(priv->mux_handle); - - if (priv->pll_is_enabled) - clk_disable_unprepare(priv->pll); - - clk_disable_unprepare(priv->pclk); - - return 0; -} - -static struct platform_driver g12a_mdio_mux_driver = { - .probe = g12a_mdio_mux_probe, - .remove = g12a_mdio_mux_remove, - .driver = { - .name = "g12a-mdio_mux", - .of_match_table = g12a_mdio_mux_match, - }, -}; -module_platform_driver(g12a_mdio_mux_driver); - -MODULE_DESCRIPTION("Amlogic G12a MDIO multiplexer driver"); -MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); -MODULE_LICENSE("GPL v2"); |