18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2019 Baylibre, SAS.
38c2ecf20Sopenharmony_ci * Author: Jerome Brunet <jbrunet@baylibre.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
138c2ecf20Sopenharmony_ci#include <linux/mdio-mux.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/phy.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define ETH_PLL_STS		0x40
198c2ecf20Sopenharmony_ci#define ETH_PLL_CTL0		0x44
208c2ecf20Sopenharmony_ci#define  PLL_CTL0_LOCK_DIG	BIT(30)
218c2ecf20Sopenharmony_ci#define  PLL_CTL0_RST		BIT(29)
228c2ecf20Sopenharmony_ci#define  PLL_CTL0_EN		BIT(28)
238c2ecf20Sopenharmony_ci#define  PLL_CTL0_SEL		BIT(23)
248c2ecf20Sopenharmony_ci#define  PLL_CTL0_N		GENMASK(14, 10)
258c2ecf20Sopenharmony_ci#define  PLL_CTL0_M		GENMASK(8, 0)
268c2ecf20Sopenharmony_ci#define  PLL_LOCK_TIMEOUT	1000000
278c2ecf20Sopenharmony_ci#define  PLL_MUX_NUM_PARENT	2
288c2ecf20Sopenharmony_ci#define ETH_PLL_CTL1		0x48
298c2ecf20Sopenharmony_ci#define ETH_PLL_CTL2		0x4c
308c2ecf20Sopenharmony_ci#define ETH_PLL_CTL3		0x50
318c2ecf20Sopenharmony_ci#define ETH_PLL_CTL4		0x54
328c2ecf20Sopenharmony_ci#define ETH_PLL_CTL5		0x58
338c2ecf20Sopenharmony_ci#define ETH_PLL_CTL6		0x5c
348c2ecf20Sopenharmony_ci#define ETH_PLL_CTL7		0x60
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define ETH_PHY_CNTL0		0x80
378c2ecf20Sopenharmony_ci#define   EPHY_G12A_ID		0x33010180
388c2ecf20Sopenharmony_ci#define ETH_PHY_CNTL1		0x84
398c2ecf20Sopenharmony_ci#define  PHY_CNTL1_ST_MODE	GENMASK(2, 0)
408c2ecf20Sopenharmony_ci#define  PHY_CNTL1_ST_PHYADD	GENMASK(7, 3)
418c2ecf20Sopenharmony_ci#define   EPHY_DFLT_ADD		8
428c2ecf20Sopenharmony_ci#define  PHY_CNTL1_MII_MODE	GENMASK(15, 14)
438c2ecf20Sopenharmony_ci#define   EPHY_MODE_RMII	0x1
448c2ecf20Sopenharmony_ci#define  PHY_CNTL1_CLK_EN	BIT(16)
458c2ecf20Sopenharmony_ci#define  PHY_CNTL1_CLKFREQ	BIT(17)
468c2ecf20Sopenharmony_ci#define  PHY_CNTL1_PHY_ENB	BIT(18)
478c2ecf20Sopenharmony_ci#define ETH_PHY_CNTL2		0x88
488c2ecf20Sopenharmony_ci#define  PHY_CNTL2_USE_INTERNAL	BIT(5)
498c2ecf20Sopenharmony_ci#define  PHY_CNTL2_SMI_SRC_MAC	BIT(6)
508c2ecf20Sopenharmony_ci#define  PHY_CNTL2_RX_CLK_EPHY	BIT(9)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define MESON_G12A_MDIO_EXTERNAL_ID 0
538c2ecf20Sopenharmony_ci#define MESON_G12A_MDIO_INTERNAL_ID 1
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct g12a_mdio_mux {
568c2ecf20Sopenharmony_ci	bool pll_is_enabled;
578c2ecf20Sopenharmony_ci	void __iomem *regs;
588c2ecf20Sopenharmony_ci	void *mux_handle;
598c2ecf20Sopenharmony_ci	struct clk *pclk;
608c2ecf20Sopenharmony_ci	struct clk *pll;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct g12a_ephy_pll {
648c2ecf20Sopenharmony_ci	void __iomem *base;
658c2ecf20Sopenharmony_ci	struct clk_hw hw;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define g12a_ephy_pll_to_dev(_hw)			\
698c2ecf20Sopenharmony_ci	container_of(_hw, struct g12a_ephy_pll, hw)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic unsigned long g12a_ephy_pll_recalc_rate(struct clk_hw *hw,
728c2ecf20Sopenharmony_ci					       unsigned long parent_rate)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw);
758c2ecf20Sopenharmony_ci	u32 val, m, n;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	val = readl(pll->base + ETH_PLL_CTL0);
788c2ecf20Sopenharmony_ci	m = FIELD_GET(PLL_CTL0_M, val);
798c2ecf20Sopenharmony_ci	n = FIELD_GET(PLL_CTL0_N, val);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return parent_rate * m / n;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int g12a_ephy_pll_enable(struct clk_hw *hw)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw);
878c2ecf20Sopenharmony_ci	u32 val = readl(pll->base + ETH_PLL_CTL0);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Apply both enable an reset */
908c2ecf20Sopenharmony_ci	val |= PLL_CTL0_RST | PLL_CTL0_EN;
918c2ecf20Sopenharmony_ci	writel(val, pll->base + ETH_PLL_CTL0);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* Clear the reset to let PLL lock */
948c2ecf20Sopenharmony_ci	val &= ~PLL_CTL0_RST;
958c2ecf20Sopenharmony_ci	writel(val, pll->base + ETH_PLL_CTL0);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* Poll on the digital lock instead of the usual analog lock
988c2ecf20Sopenharmony_ci	 * This is done because bit 31 is unreliable on some SoC. Bit
998c2ecf20Sopenharmony_ci	 * 31 may indicate that the PLL is not lock eventhough the clock
1008c2ecf20Sopenharmony_ci	 * is actually running
1018c2ecf20Sopenharmony_ci	 */
1028c2ecf20Sopenharmony_ci	return readl_poll_timeout(pll->base + ETH_PLL_CTL0, val,
1038c2ecf20Sopenharmony_ci				  val & PLL_CTL0_LOCK_DIG, 0, PLL_LOCK_TIMEOUT);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void g12a_ephy_pll_disable(struct clk_hw *hw)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw);
1098c2ecf20Sopenharmony_ci	u32 val;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	val = readl(pll->base + ETH_PLL_CTL0);
1128c2ecf20Sopenharmony_ci	val &= ~PLL_CTL0_EN;
1138c2ecf20Sopenharmony_ci	val |= PLL_CTL0_RST;
1148c2ecf20Sopenharmony_ci	writel(val, pll->base + ETH_PLL_CTL0);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int g12a_ephy_pll_is_enabled(struct clk_hw *hw)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw);
1208c2ecf20Sopenharmony_ci	unsigned int val;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	val = readl(pll->base + ETH_PLL_CTL0);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return (val & PLL_CTL0_LOCK_DIG) ? 1 : 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int g12a_ephy_pll_init(struct clk_hw *hw)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct g12a_ephy_pll *pll = g12a_ephy_pll_to_dev(hw);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Apply PLL HW settings */
1328c2ecf20Sopenharmony_ci	writel(0x29c0040a, pll->base + ETH_PLL_CTL0);
1338c2ecf20Sopenharmony_ci	writel(0x927e0000, pll->base + ETH_PLL_CTL1);
1348c2ecf20Sopenharmony_ci	writel(0xac5f49e5, pll->base + ETH_PLL_CTL2);
1358c2ecf20Sopenharmony_ci	writel(0x00000000, pll->base + ETH_PLL_CTL3);
1368c2ecf20Sopenharmony_ci	writel(0x00000000, pll->base + ETH_PLL_CTL4);
1378c2ecf20Sopenharmony_ci	writel(0x20200000, pll->base + ETH_PLL_CTL5);
1388c2ecf20Sopenharmony_ci	writel(0x0000c002, pll->base + ETH_PLL_CTL6);
1398c2ecf20Sopenharmony_ci	writel(0x00000023, pll->base + ETH_PLL_CTL7);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic const struct clk_ops g12a_ephy_pll_ops = {
1458c2ecf20Sopenharmony_ci	.recalc_rate	= g12a_ephy_pll_recalc_rate,
1468c2ecf20Sopenharmony_ci	.is_enabled	= g12a_ephy_pll_is_enabled,
1478c2ecf20Sopenharmony_ci	.enable		= g12a_ephy_pll_enable,
1488c2ecf20Sopenharmony_ci	.disable	= g12a_ephy_pll_disable,
1498c2ecf20Sopenharmony_ci	.init		= g12a_ephy_pll_init,
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int g12a_enable_internal_mdio(struct g12a_mdio_mux *priv)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	u32 value;
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* Enable the phy clock */
1588c2ecf20Sopenharmony_ci	if (!priv->pll_is_enabled) {
1598c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(priv->pll);
1608c2ecf20Sopenharmony_ci		if (ret)
1618c2ecf20Sopenharmony_ci			return ret;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	priv->pll_is_enabled = true;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Initialize ephy control */
1678c2ecf20Sopenharmony_ci	writel(EPHY_G12A_ID, priv->regs + ETH_PHY_CNTL0);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Make sure we get a 0 -> 1 transition on the enable bit */
1708c2ecf20Sopenharmony_ci	value = FIELD_PREP(PHY_CNTL1_ST_MODE, 3) |
1718c2ecf20Sopenharmony_ci		FIELD_PREP(PHY_CNTL1_ST_PHYADD, EPHY_DFLT_ADD) |
1728c2ecf20Sopenharmony_ci		FIELD_PREP(PHY_CNTL1_MII_MODE, EPHY_MODE_RMII) |
1738c2ecf20Sopenharmony_ci		PHY_CNTL1_CLK_EN |
1748c2ecf20Sopenharmony_ci		PHY_CNTL1_CLKFREQ;
1758c2ecf20Sopenharmony_ci	writel(value, priv->regs + ETH_PHY_CNTL1);
1768c2ecf20Sopenharmony_ci	writel(PHY_CNTL2_USE_INTERNAL |
1778c2ecf20Sopenharmony_ci	       PHY_CNTL2_SMI_SRC_MAC |
1788c2ecf20Sopenharmony_ci	       PHY_CNTL2_RX_CLK_EPHY,
1798c2ecf20Sopenharmony_ci	       priv->regs + ETH_PHY_CNTL2);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	value |= PHY_CNTL1_PHY_ENB;
1828c2ecf20Sopenharmony_ci	writel(value, priv->regs + ETH_PHY_CNTL1);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* The phy needs a bit of time to power up */
1858c2ecf20Sopenharmony_ci	mdelay(10);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int g12a_enable_external_mdio(struct g12a_mdio_mux *priv)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	/* Reset the mdio bus mux */
1938c2ecf20Sopenharmony_ci	writel_relaxed(0x0, priv->regs + ETH_PHY_CNTL2);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* Disable the phy clock if enabled */
1968c2ecf20Sopenharmony_ci	if (priv->pll_is_enabled) {
1978c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->pll);
1988c2ecf20Sopenharmony_ci		priv->pll_is_enabled = false;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int g12a_mdio_switch_fn(int current_child, int desired_child,
2058c2ecf20Sopenharmony_ci			       void *data)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct g12a_mdio_mux *priv = dev_get_drvdata(data);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (current_child == desired_child)
2108c2ecf20Sopenharmony_ci		return 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	switch (desired_child) {
2138c2ecf20Sopenharmony_ci	case MESON_G12A_MDIO_EXTERNAL_ID:
2148c2ecf20Sopenharmony_ci		return g12a_enable_external_mdio(priv);
2158c2ecf20Sopenharmony_ci	case MESON_G12A_MDIO_INTERNAL_ID:
2168c2ecf20Sopenharmony_ci		return g12a_enable_internal_mdio(priv);
2178c2ecf20Sopenharmony_ci	default:
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic const struct of_device_id g12a_mdio_mux_match[] = {
2238c2ecf20Sopenharmony_ci	{ .compatible = "amlogic,g12a-mdio-mux", },
2248c2ecf20Sopenharmony_ci	{},
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, g12a_mdio_mux_match);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int g12a_ephy_glue_clk_register(struct device *dev)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct g12a_mdio_mux *priv = dev_get_drvdata(dev);
2318c2ecf20Sopenharmony_ci	const char *parent_names[PLL_MUX_NUM_PARENT];
2328c2ecf20Sopenharmony_ci	struct clk_init_data init;
2338c2ecf20Sopenharmony_ci	struct g12a_ephy_pll *pll;
2348c2ecf20Sopenharmony_ci	struct clk_mux *mux;
2358c2ecf20Sopenharmony_ci	struct clk *clk;
2368c2ecf20Sopenharmony_ci	char *name;
2378c2ecf20Sopenharmony_ci	int i;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* get the mux parents */
2408c2ecf20Sopenharmony_ci	for (i = 0; i < PLL_MUX_NUM_PARENT; i++) {
2418c2ecf20Sopenharmony_ci		char in_name[8];
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		snprintf(in_name, sizeof(in_name), "clkin%d", i);
2448c2ecf20Sopenharmony_ci		clk = devm_clk_get(dev, in_name);
2458c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
2468c2ecf20Sopenharmony_ci			if (PTR_ERR(clk) != -EPROBE_DEFER)
2478c2ecf20Sopenharmony_ci				dev_err(dev, "Missing clock %s\n", in_name);
2488c2ecf20Sopenharmony_ci			return PTR_ERR(clk);
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		parent_names[i] = __clk_get_name(clk);
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* create the input mux */
2558c2ecf20Sopenharmony_ci	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
2568c2ecf20Sopenharmony_ci	if (!mux)
2578c2ecf20Sopenharmony_ci		return -ENOMEM;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	name = kasprintf(GFP_KERNEL, "%s#mux", dev_name(dev));
2608c2ecf20Sopenharmony_ci	if (!name)
2618c2ecf20Sopenharmony_ci		return -ENOMEM;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	init.name = name;
2648c2ecf20Sopenharmony_ci	init.ops = &clk_mux_ro_ops;
2658c2ecf20Sopenharmony_ci	init.flags = 0;
2668c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
2678c2ecf20Sopenharmony_ci	init.num_parents = PLL_MUX_NUM_PARENT;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	mux->reg = priv->regs + ETH_PLL_CTL0;
2708c2ecf20Sopenharmony_ci	mux->shift = __ffs(PLL_CTL0_SEL);
2718c2ecf20Sopenharmony_ci	mux->mask = PLL_CTL0_SEL >> mux->shift;
2728c2ecf20Sopenharmony_ci	mux->hw.init = &init;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	clk = devm_clk_register(dev, &mux->hw);
2758c2ecf20Sopenharmony_ci	kfree(name);
2768c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
2778c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register input mux\n");
2788c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* create the pll */
2828c2ecf20Sopenharmony_ci	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
2838c2ecf20Sopenharmony_ci	if (!pll)
2848c2ecf20Sopenharmony_ci		return -ENOMEM;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	name = kasprintf(GFP_KERNEL, "%s#pll", dev_name(dev));
2878c2ecf20Sopenharmony_ci	if (!name)
2888c2ecf20Sopenharmony_ci		return -ENOMEM;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	init.name = name;
2918c2ecf20Sopenharmony_ci	init.ops = &g12a_ephy_pll_ops;
2928c2ecf20Sopenharmony_ci	init.flags = 0;
2938c2ecf20Sopenharmony_ci	parent_names[0] = __clk_get_name(clk);
2948c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
2958c2ecf20Sopenharmony_ci	init.num_parents = 1;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	pll->base = priv->regs;
2988c2ecf20Sopenharmony_ci	pll->hw.init = &init;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	clk = devm_clk_register(dev, &pll->hw);
3018c2ecf20Sopenharmony_ci	kfree(name);
3028c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
3038c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register input mux\n");
3048c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	priv->pll = clk;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int g12a_mdio_mux_probe(struct platform_device *pdev)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3158c2ecf20Sopenharmony_ci	struct g12a_mdio_mux *priv;
3168c2ecf20Sopenharmony_ci	int ret;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
3198c2ecf20Sopenharmony_ci	if (!priv)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	priv->regs = devm_platform_ioremap_resource(pdev, 0);
3258c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regs))
3268c2ecf20Sopenharmony_ci		return PTR_ERR(priv->regs);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	priv->pclk = devm_clk_get(dev, "pclk");
3298c2ecf20Sopenharmony_ci	if (IS_ERR(priv->pclk)) {
3308c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->pclk);
3318c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3328c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get peripheral clock\n");
3338c2ecf20Sopenharmony_ci		return ret;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Make sure the device registers are clocked */
3378c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->pclk);
3388c2ecf20Sopenharmony_ci	if (ret) {
3398c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable peripheral clock");
3408c2ecf20Sopenharmony_ci		return ret;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Register PLL in CCF */
3448c2ecf20Sopenharmony_ci	ret = g12a_ephy_glue_clk_register(dev);
3458c2ecf20Sopenharmony_ci	if (ret)
3468c2ecf20Sopenharmony_ci		goto err;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret = mdio_mux_init(dev, dev->of_node, g12a_mdio_switch_fn,
3498c2ecf20Sopenharmony_ci			    &priv->mux_handle, dev, NULL);
3508c2ecf20Sopenharmony_ci	if (ret) {
3518c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3528c2ecf20Sopenharmony_ci			dev_err(dev, "mdio multiplexer init failed: %d", ret);
3538c2ecf20Sopenharmony_ci		goto err;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cierr:
3598c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->pclk);
3608c2ecf20Sopenharmony_ci	return ret;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int g12a_mdio_mux_remove(struct platform_device *pdev)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct g12a_mdio_mux *priv = platform_get_drvdata(pdev);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	mdio_mux_uninit(priv->mux_handle);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (priv->pll_is_enabled)
3708c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->pll);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->pclk);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic struct platform_driver g12a_mdio_mux_driver = {
3788c2ecf20Sopenharmony_ci	.probe		= g12a_mdio_mux_probe,
3798c2ecf20Sopenharmony_ci	.remove		= g12a_mdio_mux_remove,
3808c2ecf20Sopenharmony_ci	.driver		= {
3818c2ecf20Sopenharmony_ci		.name	= "g12a-mdio_mux",
3828c2ecf20Sopenharmony_ci		.of_match_table = g12a_mdio_mux_match,
3838c2ecf20Sopenharmony_ci	},
3848c2ecf20Sopenharmony_ci};
3858c2ecf20Sopenharmony_cimodule_platform_driver(g12a_mdio_mux_driver);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic G12a MDIO multiplexer driver");
3888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
3898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
390