Commit ee975351 authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

net: mdio: mdio-bcm-unimac: Manage clock around I/O accesses

Up until now we have managed not to have the mdio-bcm-unimac manage its
clock except during probe and suspend/resume. This works most of the
time, except where it does not.

With a fully modular build, we can get into a situation whereby the
GENET driver is fully registered, and so is the mdio-bcm-unimac driver,
however the Ethernet PHY driver is not yet, because it depends on a
resource that is not yet available (e.g.: GPIO provider). In that state,
the network device is not usable yet, and so to conserve power, the
GENET driver will have turned off its "main" clock which feeds its MDIO
controller.

When the PHY driver finally probes however, we make an access to the PHY
registers to e.g.: disable interrupts, and this causes a bus error
within the MDIO controller space because the MDIO controller clock(s)
are turned off.

To remedy that, we manage the clock around all of the I/O accesses to
the hardware which are done exclusively during read, write and clock
divider configuration.

This ensures that the register space is accessible, and this also
ensures that there are not unnecessarily elevated reference counts
keeping the clocks active when the network device is administratively
turned off. It would be the case with the previous way of managing the
clock.
Reviewed-by: default avatarJacob Keller <jacob.e.keller@intel.com>
Signed-off-by: default avatarFlorian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 78b88ef3
...@@ -94,6 +94,10 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) ...@@ -94,6 +94,10 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
int ret; int ret;
u32 cmd; u32 cmd;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
/* Prepare the read operation */ /* Prepare the read operation */
cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT); cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
unimac_mdio_writel(priv, cmd, MDIO_CMD); unimac_mdio_writel(priv, cmd, MDIO_CMD);
...@@ -103,7 +107,7 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) ...@@ -103,7 +107,7 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
ret = priv->wait_func(priv->wait_func_data); ret = priv->wait_func(priv->wait_func_data);
if (ret) if (ret)
return ret; goto out;
cmd = unimac_mdio_readl(priv, MDIO_CMD); cmd = unimac_mdio_readl(priv, MDIO_CMD);
...@@ -112,10 +116,15 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) ...@@ -112,10 +116,15 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
* that condition here and ignore the MDIO controller read failure * that condition here and ignore the MDIO controller read failure
* indication. * indication.
*/ */
if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) {
return -EIO; ret = -EIO;
goto out;
}
return cmd & 0xffff; ret = cmd & 0xffff;
out:
clk_disable_unprepare(priv->clk);
return ret;
} }
static int unimac_mdio_write(struct mii_bus *bus, int phy_id, static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
...@@ -123,6 +132,11 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, ...@@ -123,6 +132,11 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
{ {
struct unimac_mdio_priv *priv = bus->priv; struct unimac_mdio_priv *priv = bus->priv;
u32 cmd; u32 cmd;
int ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
/* Prepare the write operation */ /* Prepare the write operation */
cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
...@@ -131,7 +145,10 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, ...@@ -131,7 +145,10 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
unimac_mdio_start(priv); unimac_mdio_start(priv);
return priv->wait_func(priv->wait_func_data); ret = priv->wait_func(priv->wait_func_data);
clk_disable_unprepare(priv->clk);
return ret;
} }
/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with
...@@ -178,14 +195,19 @@ static int unimac_mdio_reset(struct mii_bus *bus) ...@@ -178,14 +195,19 @@ static int unimac_mdio_reset(struct mii_bus *bus)
return 0; return 0;
} }
static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv) static int unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
{ {
unsigned long rate; unsigned long rate;
u32 reg, div; u32 reg, div;
int ret;
/* Keep the hardware default values */ /* Keep the hardware default values */
if (!priv->clk_freq) if (!priv->clk_freq)
return; return 0;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
if (!priv->clk) if (!priv->clk)
rate = 250000000; rate = 250000000;
...@@ -195,7 +217,8 @@ static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv) ...@@ -195,7 +217,8 @@ static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
div = (rate / (2 * priv->clk_freq)) - 1; div = (rate / (2 * priv->clk_freq)) - 1;
if (div & ~MDIO_CLK_DIV_MASK) { if (div & ~MDIO_CLK_DIV_MASK) {
pr_warn("Incorrect MDIO clock frequency, ignoring\n"); pr_warn("Incorrect MDIO clock frequency, ignoring\n");
return; ret = 0;
goto out;
} }
/* The MDIO clock is the reference clock (typically 250Mhz) divided by /* The MDIO clock is the reference clock (typically 250Mhz) divided by
...@@ -205,6 +228,9 @@ static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv) ...@@ -205,6 +228,9 @@ static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT); reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT);
reg |= div << MDIO_CLK_DIV_SHIFT; reg |= div << MDIO_CLK_DIV_SHIFT;
unimac_mdio_writel(priv, reg, MDIO_CFG); unimac_mdio_writel(priv, reg, MDIO_CFG);
out:
clk_disable_unprepare(priv->clk);
return ret;
} }
static int unimac_mdio_probe(struct platform_device *pdev) static int unimac_mdio_probe(struct platform_device *pdev)
...@@ -235,24 +261,12 @@ static int unimac_mdio_probe(struct platform_device *pdev) ...@@ -235,24 +261,12 @@ static int unimac_mdio_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
} }
priv->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq)) if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
priv->clk_freq = 0; priv->clk_freq = 0;
unimac_mdio_clk_set(priv);
priv->mii_bus = mdiobus_alloc(); priv->mii_bus = mdiobus_alloc();
if (!priv->mii_bus) { if (!priv->mii_bus)
ret = -ENOMEM; return -ENOMEM;
goto out_clk_disable;
}
bus = priv->mii_bus; bus = priv->mii_bus;
bus->priv = priv; bus->priv = priv;
...@@ -261,17 +275,29 @@ static int unimac_mdio_probe(struct platform_device *pdev) ...@@ -261,17 +275,29 @@ static int unimac_mdio_probe(struct platform_device *pdev)
priv->wait_func = pdata->wait_func; priv->wait_func = pdata->wait_func;
priv->wait_func_data = pdata->wait_func_data; priv->wait_func_data = pdata->wait_func_data;
bus->phy_mask = ~pdata->phy_mask; bus->phy_mask = ~pdata->phy_mask;
priv->clk = pdata->clk;
} else { } else {
bus->name = "unimac MII bus"; bus->name = "unimac MII bus";
priv->wait_func_data = priv; priv->wait_func_data = priv;
priv->wait_func = unimac_mdio_poll; priv->wait_func = unimac_mdio_poll;
priv->clk = devm_clk_get_optional(&pdev->dev, NULL);
}
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto out_mdio_free;
} }
bus->parent = &pdev->dev; bus->parent = &pdev->dev;
bus->read = unimac_mdio_read; bus->read = unimac_mdio_read;
bus->write = unimac_mdio_write; bus->write = unimac_mdio_write;
bus->reset = unimac_mdio_reset; bus->reset = unimac_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
ret = unimac_mdio_clk_set(priv);
if (ret)
goto out_mdio_free;
ret = of_mdiobus_register(bus, np); ret = of_mdiobus_register(bus, np);
if (ret) { if (ret) {
dev_err(&pdev->dev, "MDIO bus registration failed\n"); dev_err(&pdev->dev, "MDIO bus registration failed\n");
...@@ -286,8 +312,6 @@ static int unimac_mdio_probe(struct platform_device *pdev) ...@@ -286,8 +312,6 @@ static int unimac_mdio_probe(struct platform_device *pdev)
out_mdio_free: out_mdio_free:
mdiobus_free(bus); mdiobus_free(bus);
out_clk_disable:
clk_disable_unprepare(priv->clk);
return ret; return ret;
} }
...@@ -297,34 +321,17 @@ static void unimac_mdio_remove(struct platform_device *pdev) ...@@ -297,34 +321,17 @@ static void unimac_mdio_remove(struct platform_device *pdev)
mdiobus_unregister(priv->mii_bus); mdiobus_unregister(priv->mii_bus);
mdiobus_free(priv->mii_bus); mdiobus_free(priv->mii_bus);
clk_disable_unprepare(priv->clk);
}
static int __maybe_unused unimac_mdio_suspend(struct device *d)
{
struct unimac_mdio_priv *priv = dev_get_drvdata(d);
clk_disable_unprepare(priv->clk);
return 0;
} }
static int __maybe_unused unimac_mdio_resume(struct device *d) static int __maybe_unused unimac_mdio_resume(struct device *d)
{ {
struct unimac_mdio_priv *priv = dev_get_drvdata(d); struct unimac_mdio_priv *priv = dev_get_drvdata(d);
int ret;
ret = clk_prepare_enable(priv->clk); return unimac_mdio_clk_set(priv);
if (ret)
return ret;
unimac_mdio_clk_set(priv);
return 0;
} }
static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops, static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops,
unimac_mdio_suspend, unimac_mdio_resume); NULL, unimac_mdio_resume);
static const struct of_device_id unimac_mdio_ids[] = { static const struct of_device_id unimac_mdio_ids[] = {
{ .compatible = "brcm,asp-v2.1-mdio", }, { .compatible = "brcm,asp-v2.1-mdio", },
......
#ifndef __MDIO_BCM_UNIMAC_PDATA_H #ifndef __MDIO_BCM_UNIMAC_PDATA_H
#define __MDIO_BCM_UNIMAC_PDATA_H #define __MDIO_BCM_UNIMAC_PDATA_H
struct clk;
struct unimac_mdio_pdata { struct unimac_mdio_pdata {
u32 phy_mask; u32 phy_mask;
int (*wait_func)(void *data); int (*wait_func)(void *data);
void *wait_func_data; void *wait_func_data;
const char *bus_name; const char *bus_name;
struct clk *clk;
}; };
#define UNIMAC_MDIO_DRV_NAME "unimac-mdio" #define UNIMAC_MDIO_DRV_NAME "unimac-mdio"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment