Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Florian Fainelli and committed by
David S. Miller
ee975351 78b88ef3

+53 -43
+50 -43
drivers/net/mdio/mdio-bcm-unimac.c
··· 94 94 int ret; 95 95 u32 cmd; 96 96 97 + ret = clk_prepare_enable(priv->clk); 98 + if (ret) 99 + return ret; 100 + 97 101 /* Prepare the read operation */ 98 102 cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT); 99 103 unimac_mdio_writel(priv, cmd, MDIO_CMD); ··· 107 103 108 104 ret = priv->wait_func(priv->wait_func_data); 109 105 if (ret) 110 - return ret; 106 + goto out; 111 107 112 108 cmd = unimac_mdio_readl(priv, MDIO_CMD); 113 109 ··· 116 112 * that condition here and ignore the MDIO controller read failure 117 113 * indication. 118 114 */ 119 - if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) 120 - return -EIO; 115 + if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) { 116 + ret = -EIO; 117 + goto out; 118 + } 121 119 122 - return cmd & 0xffff; 120 + ret = cmd & 0xffff; 121 + out: 122 + clk_disable_unprepare(priv->clk); 123 + return ret; 123 124 } 124 125 125 126 static int unimac_mdio_write(struct mii_bus *bus, int phy_id, ··· 132 123 { 133 124 struct unimac_mdio_priv *priv = bus->priv; 134 125 u32 cmd; 126 + int ret; 127 + 128 + ret = clk_prepare_enable(priv->clk); 129 + if (ret) 130 + return ret; 135 131 136 132 /* Prepare the write operation */ 137 133 cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | ··· 145 131 146 132 unimac_mdio_start(priv); 147 133 148 - return priv->wait_func(priv->wait_func_data); 134 + ret = priv->wait_func(priv->wait_func_data); 135 + clk_disable_unprepare(priv->clk); 136 + 137 + return ret; 149 138 } 150 139 151 140 /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with ··· 195 178 return 0; 196 179 } 197 180 198 - static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv) 181 + static int unimac_mdio_clk_set(struct unimac_mdio_priv *priv) 199 182 { 200 183 unsigned long rate; 201 184 u32 reg, div; 185 + int ret; 202 186 203 187 /* Keep the hardware default values */ 204 188 if (!priv->clk_freq) 205 - return; 189 + return 0; 190 + 191 + ret = clk_prepare_enable(priv->clk); 192 + if (ret) 193 + return ret; 206 194 207 195 if (!priv->clk) 208 196 rate = 250000000; ··· 217 195 div = (rate / (2 * priv->clk_freq)) - 1; 218 196 if (div & ~MDIO_CLK_DIV_MASK) { 219 197 pr_warn("Incorrect MDIO clock frequency, ignoring\n"); 220 - return; 198 + ret = 0; 199 + goto out; 221 200 } 222 201 223 202 /* The MDIO clock is the reference clock (typically 250Mhz) divided by ··· 228 205 reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT); 229 206 reg |= div << MDIO_CLK_DIV_SHIFT; 230 207 unimac_mdio_writel(priv, reg, MDIO_CFG); 208 + out: 209 + clk_disable_unprepare(priv->clk); 210 + return ret; 231 211 } 232 212 233 213 static int unimac_mdio_probe(struct platform_device *pdev) ··· 261 235 return -ENOMEM; 262 236 } 263 237 264 - priv->clk = devm_clk_get_optional(&pdev->dev, NULL); 265 - if (IS_ERR(priv->clk)) 266 - return PTR_ERR(priv->clk); 267 - 268 - ret = clk_prepare_enable(priv->clk); 269 - if (ret) 270 - return ret; 271 - 272 238 if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq)) 273 239 priv->clk_freq = 0; 274 240 275 - unimac_mdio_clk_set(priv); 276 - 277 241 priv->mii_bus = mdiobus_alloc(); 278 - if (!priv->mii_bus) { 279 - ret = -ENOMEM; 280 - goto out_clk_disable; 281 - } 242 + if (!priv->mii_bus) 243 + return -ENOMEM; 282 244 283 245 bus = priv->mii_bus; 284 246 bus->priv = priv; ··· 275 261 priv->wait_func = pdata->wait_func; 276 262 priv->wait_func_data = pdata->wait_func_data; 277 263 bus->phy_mask = ~pdata->phy_mask; 264 + priv->clk = pdata->clk; 278 265 } else { 279 266 bus->name = "unimac MII bus"; 280 267 priv->wait_func_data = priv; 281 268 priv->wait_func = unimac_mdio_poll; 269 + priv->clk = devm_clk_get_optional(&pdev->dev, NULL); 282 270 } 271 + 272 + if (IS_ERR(priv->clk)) { 273 + ret = PTR_ERR(priv->clk); 274 + goto out_mdio_free; 275 + } 276 + 283 277 bus->parent = &pdev->dev; 284 278 bus->read = unimac_mdio_read; 285 279 bus->write = unimac_mdio_write; 286 280 bus->reset = unimac_mdio_reset; 287 281 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); 282 + 283 + ret = unimac_mdio_clk_set(priv); 284 + if (ret) 285 + goto out_mdio_free; 288 286 289 287 ret = of_mdiobus_register(bus, np); 290 288 if (ret) { ··· 312 286 313 287 out_mdio_free: 314 288 mdiobus_free(bus); 315 - out_clk_disable: 316 - clk_disable_unprepare(priv->clk); 317 289 return ret; 318 290 } 319 291 ··· 321 297 322 298 mdiobus_unregister(priv->mii_bus); 323 299 mdiobus_free(priv->mii_bus); 324 - clk_disable_unprepare(priv->clk); 325 - } 326 - 327 - static int __maybe_unused unimac_mdio_suspend(struct device *d) 328 - { 329 - struct unimac_mdio_priv *priv = dev_get_drvdata(d); 330 - 331 - clk_disable_unprepare(priv->clk); 332 - 333 - return 0; 334 300 } 335 301 336 302 static int __maybe_unused unimac_mdio_resume(struct device *d) 337 303 { 338 304 struct unimac_mdio_priv *priv = dev_get_drvdata(d); 339 - int ret; 340 305 341 - ret = clk_prepare_enable(priv->clk); 342 - if (ret) 343 - return ret; 344 - 345 - unimac_mdio_clk_set(priv); 346 - 347 - return 0; 306 + return unimac_mdio_clk_set(priv); 348 307 } 349 308 350 309 static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops, 351 - unimac_mdio_suspend, unimac_mdio_resume); 310 + NULL, unimac_mdio_resume); 352 311 353 312 static const struct of_device_id unimac_mdio_ids[] = { 354 313 { .compatible = "brcm,asp-v2.1-mdio", },
+3
include/linux/platform_data/mdio-bcm-unimac.h
··· 1 1 #ifndef __MDIO_BCM_UNIMAC_PDATA_H 2 2 #define __MDIO_BCM_UNIMAC_PDATA_H 3 3 4 + struct clk; 5 + 4 6 struct unimac_mdio_pdata { 5 7 u32 phy_mask; 6 8 int (*wait_func)(void *data); 7 9 void *wait_func_data; 8 10 const char *bus_name; 11 + struct clk *clk; 9 12 }; 10 13 11 14 #define UNIMAC_MDIO_DRV_NAME "unimac-mdio"