Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 590ee2e0 authored by Florian Fainelli's avatar Florian Fainelli Committed by Greg Kroah-Hartman
Browse files

net: phy: mdio-bcm-unimac: Allow configuring MDIO clock divider



[ Upstream commit b78ac6ecd1b6b46f8767cbafa95a7b0b51b87ad8 ]

Allow the configuration of the MDIO clock divider when the Device Tree
contains 'clock-frequency' property (similar to I2C and SPI buses).
Because the hardware may have lost its state during suspend/resume,
re-apply the MDIO clock divider upon resumption.

Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 73cb5485
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ Optional properties:
- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
  to this hardware block, or must be "mdio_done" for the first interrupt and
  "mdio_error" for the second when there are separate interrupts
- clocks: A reference to the clock supplying the MDIO bus controller
- clock-frequency: the MDIO bus clock that must be output by the MDIO bus
  hardware, if absent, the default hardware values are used

Child nodes of this MDIO bus controller node are standard Ethernet PHY device
nodes as described in Documentation/devicetree/bindings/net/phy.txt
+81 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/clk.h>

#include <linux/of.h>
#include <linux/of_platform.h>
@@ -45,6 +46,8 @@ struct unimac_mdio_priv {
	void __iomem		*base;
	int (*wait_func)	(void *wait_func_data);
	void			*wait_func_data;
	struct clk		*clk;
	u32			clk_freq;
};

static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset)
@@ -189,6 +192,35 @@ static int unimac_mdio_reset(struct mii_bus *bus)
	return 0;
}

static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
{
	unsigned long rate;
	u32 reg, div;

	/* Keep the hardware default values */
	if (!priv->clk_freq)
		return;

	if (!priv->clk)
		rate = 250000000;
	else
		rate = clk_get_rate(priv->clk);

	div = (rate / (2 * priv->clk_freq)) - 1;
	if (div & ~MDIO_CLK_DIV_MASK) {
		pr_warn("Incorrect MDIO clock frequency, ignoring\n");
		return;
	}

	/* The MDIO clock is the reference clock (typicaly 250Mhz) divided by
	 * 2 x (MDIO_CLK_DIV + 1)
	 */
	reg = unimac_mdio_readl(priv, MDIO_CFG);
	reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT);
	reg |= div << MDIO_CLK_DIV_SHIFT;
	unimac_mdio_writel(priv, reg, MDIO_CFG);
}

static int unimac_mdio_probe(struct platform_device *pdev)
{
	struct unimac_mdio_pdata *pdata = pdev->dev.platform_data;
@@ -217,9 +249,26 @@ static int unimac_mdio_probe(struct platform_device *pdev)
		return -ENOMEM;
	}

	priv->clk = devm_clk_get(&pdev->dev, NULL);
	if (PTR_ERR(priv->clk) == -EPROBE_DEFER)
		return PTR_ERR(priv->clk);
	else
		priv->clk = NULL;

	ret = clk_prepare_enable(priv->clk);
	if (ret)
		return ret;

	if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
		priv->clk_freq = 0;

	unimac_mdio_clk_set(priv);

	priv->mii_bus = mdiobus_alloc();
	if (!priv->mii_bus)
		return -ENOMEM;
	if (!priv->mii_bus) {
		ret = -ENOMEM;
		goto out_clk_disable;
	}

	bus = priv->mii_bus;
	bus->priv = priv;
@@ -253,6 +302,8 @@ static int unimac_mdio_probe(struct platform_device *pdev)

out_mdio_free:
	mdiobus_free(bus);
out_clk_disable:
	clk_disable_unprepare(priv->clk);
	return ret;
}

@@ -262,10 +313,37 @@ static int unimac_mdio_remove(struct platform_device *pdev)

	mdiobus_unregister(priv->mii_bus);
	mdiobus_free(priv->mii_bus);
	clk_disable_unprepare(priv->clk);

	return 0;
}

static int unimac_mdio_suspend(struct device *d)
{
	struct unimac_mdio_priv *priv = dev_get_drvdata(d);

	clk_disable_unprepare(priv->clk);

	return 0;
}

static int unimac_mdio_resume(struct device *d)
{
	struct unimac_mdio_priv *priv = dev_get_drvdata(d);
	int ret;

	ret = clk_prepare_enable(priv->clk);
	if (ret)
		return ret;

	unimac_mdio_clk_set(priv);

	return 0;
}

static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops,
			 unimac_mdio_suspend, unimac_mdio_resume);

static const struct of_device_id unimac_mdio_ids[] = {
	{ .compatible = "brcm,genet-mdio-v5", },
	{ .compatible = "brcm,genet-mdio-v4", },
@@ -281,6 +359,7 @@ static struct platform_driver unimac_mdio_driver = {
	.driver = {
		.name = UNIMAC_MDIO_DRV_NAME,
		.of_match_table = unimac_mdio_ids,
		.pm = &unimac_mdio_pm_ops,
	},
	.probe	= unimac_mdio_probe,
	.remove	= unimac_mdio_remove,