diff options
-rw-r--r-- | Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt | 3 | ||||
-rw-r--r-- | drivers/net/phy/mdio-bcm-unimac.c | 83 |
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 | ||
23 | Child nodes of this MDIO bus controller node are standard Ethernet PHY device | 26 | Child nodes of this MDIO bus controller node are standard Ethernet PHY device |
24 | nodes as described in Documentation/devicetree/bindings/net/phy.txt | 27 | nodes 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 | ||
50 | static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset) | 53 | 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) | |||
189 | return 0; | 192 | return 0; |
190 | } | 193 | } |
191 | 194 | ||
195 | static 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 | |||
192 | static int unimac_mdio_probe(struct platform_device *pdev) | 224 | static 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 | ||
254 | out_mdio_free: | 303 | out_mdio_free: |
255 | mdiobus_free(bus); | 304 | mdiobus_free(bus); |
305 | out_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 | |||
321 | static 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 | |||
330 | static 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 | ||
344 | static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops, | ||
345 | unimac_mdio_suspend, unimac_mdio_resume); | ||
346 | |||
269 | static const struct of_device_id unimac_mdio_ids[] = { | 347 | static 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, |