aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2018-09-20 20:05:40 -0400
committerDavid S. Miller <davem@davemloft.net>2018-09-21 22:08:24 -0400
commitb78ac6ecd1b6b46f8767cbafa95a7b0b51b87ad8 (patch)
tree619545c43c1803f67e2828e921716bbcfa553f5d
parent94e7c844990f0db92418586b107be135b4963b66 (diff)
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>
-rw-r--r--Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt3
-rw-r--r--drivers/net/phy/mdio-bcm-unimac.c83
2 files changed, 84 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt b/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
index 4648948f7c3b..e15589f47787 100644
--- a/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
+++ b/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
@@ -19,6 +19,9 @@ Optional properties:
19- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed 19- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
20 to this hardware block, or must be "mdio_done" for the first interrupt and 20 to this hardware block, or must be "mdio_done" for the first interrupt and
21 "mdio_error" for the second when there are separate interrupts 21 "mdio_error" for the second when there are separate interrupts
22- clocks: A reference to the clock supplying the MDIO bus controller
23- clock-frequency: the MDIO bus clock that must be output by the MDIO bus
24 hardware, if absent, the default hardware values are used
22 25
23Child nodes of this MDIO bus controller node are standard Ethernet PHY device 26Child nodes of this MDIO bus controller node are standard Ethernet PHY device
24nodes as described in Documentation/devicetree/bindings/net/phy.txt 27nodes as described in Documentation/devicetree/bindings/net/phy.txt
diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c
index 8d370667fa1b..80b9583eaa95 100644
--- a/drivers/net/phy/mdio-bcm-unimac.c
+++ b/drivers/net/phy/mdio-bcm-unimac.c
@@ -16,6 +16,7 @@
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/io.h> 17#include <linux/io.h>
18#include <linux/delay.h> 18#include <linux/delay.h>
19#include <linux/clk.h>
19 20
20#include <linux/of.h> 21#include <linux/of.h>
21#include <linux/of_platform.h> 22#include <linux/of_platform.h>
@@ -45,6 +46,8 @@ struct unimac_mdio_priv {
45 void __iomem *base; 46 void __iomem *base;
46 int (*wait_func) (void *wait_func_data); 47 int (*wait_func) (void *wait_func_data);
47 void *wait_func_data; 48 void *wait_func_data;
49 struct clk *clk;
50 u32 clk_freq;
48}; 51};
49 52
50static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset) 53static 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)
189 return 0; 192 return 0;
190} 193}
191 194
195static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
196{
197 unsigned long rate;
198 u32 reg, div;
199
200 /* Keep the hardware default values */
201 if (!priv->clk_freq)
202 return;
203
204 if (!priv->clk)
205 rate = 250000000;
206 else
207 rate = clk_get_rate(priv->clk);
208
209 div = (rate / (2 * priv->clk_freq)) - 1;
210 if (div & ~MDIO_CLK_DIV_MASK) {
211 pr_warn("Incorrect MDIO clock frequency, ignoring\n");
212 return;
213 }
214
215 /* The MDIO clock is the reference clock (typicaly 250Mhz) divided by
216 * 2 x (MDIO_CLK_DIV + 1)
217 */
218 reg = unimac_mdio_readl(priv, MDIO_CFG);
219 reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT);
220 reg |= div << MDIO_CLK_DIV_SHIFT;
221 unimac_mdio_writel(priv, reg, MDIO_CFG);
222}
223
192static int unimac_mdio_probe(struct platform_device *pdev) 224static int unimac_mdio_probe(struct platform_device *pdev)
193{ 225{
194 struct unimac_mdio_pdata *pdata = pdev->dev.platform_data; 226 struct unimac_mdio_pdata *pdata = pdev->dev.platform_data;
@@ -217,9 +249,26 @@ static int unimac_mdio_probe(struct platform_device *pdev)
217 return -ENOMEM; 249 return -ENOMEM;
218 } 250 }
219 251
252 priv->clk = devm_clk_get(&pdev->dev, NULL);
253 if (PTR_ERR(priv->clk) == -EPROBE_DEFER)
254 return PTR_ERR(priv->clk);
255 else
256 priv->clk = NULL;
257
258 ret = clk_prepare_enable(priv->clk);
259 if (ret)
260 return ret;
261
262 if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
263 priv->clk_freq = 0;
264
265 unimac_mdio_clk_set(priv);
266
220 priv->mii_bus = mdiobus_alloc(); 267 priv->mii_bus = mdiobus_alloc();
221 if (!priv->mii_bus) 268 if (!priv->mii_bus) {
222 return -ENOMEM; 269 ret = -ENOMEM;
270 goto out_clk_disable;
271 }
223 272
224 bus = priv->mii_bus; 273 bus = priv->mii_bus;
225 bus->priv = priv; 274 bus->priv = priv;
@@ -253,6 +302,8 @@ static int unimac_mdio_probe(struct platform_device *pdev)
253 302
254out_mdio_free: 303out_mdio_free:
255 mdiobus_free(bus); 304 mdiobus_free(bus);
305out_clk_disable:
306 clk_disable_unprepare(priv->clk);
256 return ret; 307 return ret;
257} 308}
258 309
@@ -262,10 +313,37 @@ static int unimac_mdio_remove(struct platform_device *pdev)
262 313
263 mdiobus_unregister(priv->mii_bus); 314 mdiobus_unregister(priv->mii_bus);
264 mdiobus_free(priv->mii_bus); 315 mdiobus_free(priv->mii_bus);
316 clk_disable_unprepare(priv->clk);
317
318 return 0;
319}
320
321static int unimac_mdio_suspend(struct device *d)
322{
323 struct unimac_mdio_priv *priv = dev_get_drvdata(d);
324
325 clk_disable_unprepare(priv->clk);
326
327 return 0;
328}
329
330static int unimac_mdio_resume(struct device *d)
331{
332 struct unimac_mdio_priv *priv = dev_get_drvdata(d);
333 int ret;
334
335 ret = clk_prepare_enable(priv->clk);
336 if (ret)
337 return ret;
338
339 unimac_mdio_clk_set(priv);
265 340
266 return 0; 341 return 0;
267} 342}
268 343
344static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops,
345 unimac_mdio_suspend, unimac_mdio_resume);
346
269static const struct of_device_id unimac_mdio_ids[] = { 347static const struct of_device_id unimac_mdio_ids[] = {
270 { .compatible = "brcm,genet-mdio-v5", }, 348 { .compatible = "brcm,genet-mdio-v5", },
271 { .compatible = "brcm,genet-mdio-v4", }, 349 { .compatible = "brcm,genet-mdio-v4", },
@@ -281,6 +359,7 @@ static struct platform_driver unimac_mdio_driver = {
281 .driver = { 359 .driver = {
282 .name = UNIMAC_MDIO_DRV_NAME, 360 .name = UNIMAC_MDIO_DRV_NAME,
283 .of_match_table = unimac_mdio_ids, 361 .of_match_table = unimac_mdio_ids,
362 .pm = &unimac_mdio_pm_ops,
284 }, 363 },
285 .probe = unimac_mdio_probe, 364 .probe = unimac_mdio_probe,
286 .remove = unimac_mdio_remove, 365 .remove = unimac_mdio_remove,