Skip to content

Commit

Permalink
net: phy: mdio-bcm-unimac: Allow configuring MDIO clock divider
Browse files Browse the repository at this point in the history
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: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Florian Fainelli authored and David S. Miller committed Sep 22, 2018
1 parent 94e7c84 commit b78ac6e
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 2 deletions.
3 changes: 3 additions & 0 deletions Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 81 additions & 2 deletions drivers/net/phy/mdio-bcm-unimac.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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", },
Expand All @@ -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,
Expand Down

0 comments on commit b78ac6e

Please sign in to comment.