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

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>
This commit is contained in:
Florian Fainelli 2018-09-20 17:05:40 -07:00 committed by David S. Miller
parent 94e7c84499
commit b78ac6ecd1
2 changed files with 84 additions and 2 deletions

View File

@ -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

View File

@ -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,