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:
		
							parent
							
								
									94e7c84499
								
							
						
					
					
						commit
						b78ac6ecd1
					
				| @ -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 | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user