diff options
author | Andrew Lunn <andrew@lunn.ch> | 2015-07-25 16:38:02 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-07-27 04:21:47 -0400 |
commit | 8fff755e9f8d0f70a595e79f248695ce6aef5cc3 (patch) | |
tree | dbaf78056303cdc56757fe65f1714ff70c8a1b30 | |
parent | 7025e88a79d64aa4ba58fd03d630a78b12cce6ae (diff) |
net: fec: Ensure clocks are enabled while using mdio bus
When a switch is attached to the mdio bus, the mdio bus can be used
while the interface is not open. If the IPG clock is not enabled, MDIO
reads/writes will simply time out.
Add support for runtime PM to control this clock. Enable/disable this
clock using runtime PM, with open()/close() and mdio read()/write()
function triggering runtime PM operations. Since PM is optional, the
IPG clock is enabled at probe and is no longer modified by
fec_enet_clk_enable(), thus if PM is not enabled in the kernel, it is
guaranteed the clock is running when MDIO operations are performed.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Cc: tyler.baker@linaro.org
Cc: fabio.estevam@freescale.com
Cc: shawn.guo@linaro.org
Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
Tested-by: Tyler Baker <tyler.baker@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 89 |
1 files changed, 76 insertions, 13 deletions
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 5e8b8370b5a7..32e3807c650e 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/string.h> | 26 | #include <linux/string.h> |
27 | #include <linux/pm_runtime.h> | ||
27 | #include <linux/ptrace.h> | 28 | #include <linux/ptrace.h> |
28 | #include <linux/errno.h> | 29 | #include <linux/errno.h> |
29 | #include <linux/ioport.h> | 30 | #include <linux/ioport.h> |
@@ -77,6 +78,7 @@ static void fec_enet_itr_coal_init(struct net_device *ndev); | |||
77 | #define FEC_ENET_RAEM_V 0x8 | 78 | #define FEC_ENET_RAEM_V 0x8 |
78 | #define FEC_ENET_RAFL_V 0x8 | 79 | #define FEC_ENET_RAFL_V 0x8 |
79 | #define FEC_ENET_OPD_V 0xFFF0 | 80 | #define FEC_ENET_OPD_V 0xFFF0 |
81 | #define FEC_MDIO_PM_TIMEOUT 100 /* ms */ | ||
80 | 82 | ||
81 | static struct platform_device_id fec_devtype[] = { | 83 | static struct platform_device_id fec_devtype[] = { |
82 | { | 84 | { |
@@ -1767,7 +1769,13 @@ static void fec_enet_adjust_link(struct net_device *ndev) | |||
1767 | static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) | 1769 | static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) |
1768 | { | 1770 | { |
1769 | struct fec_enet_private *fep = bus->priv; | 1771 | struct fec_enet_private *fep = bus->priv; |
1772 | struct device *dev = &fep->pdev->dev; | ||
1770 | unsigned long time_left; | 1773 | unsigned long time_left; |
1774 | int ret = 0; | ||
1775 | |||
1776 | ret = pm_runtime_get_sync(dev); | ||
1777 | if (IS_ERR_VALUE(ret)) | ||
1778 | return ret; | ||
1771 | 1779 | ||
1772 | fep->mii_timeout = 0; | 1780 | fep->mii_timeout = 0; |
1773 | init_completion(&fep->mdio_done); | 1781 | init_completion(&fep->mdio_done); |
@@ -1783,18 +1791,30 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) | |||
1783 | if (time_left == 0) { | 1791 | if (time_left == 0) { |
1784 | fep->mii_timeout = 1; | 1792 | fep->mii_timeout = 1; |
1785 | netdev_err(fep->netdev, "MDIO read timeout\n"); | 1793 | netdev_err(fep->netdev, "MDIO read timeout\n"); |
1786 | return -ETIMEDOUT; | 1794 | ret = -ETIMEDOUT; |
1795 | goto out; | ||
1787 | } | 1796 | } |
1788 | 1797 | ||
1789 | /* return value */ | 1798 | ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); |
1790 | return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); | 1799 | |
1800 | out: | ||
1801 | pm_runtime_mark_last_busy(dev); | ||
1802 | pm_runtime_put_autosuspend(dev); | ||
1803 | |||
1804 | return ret; | ||
1791 | } | 1805 | } |
1792 | 1806 | ||
1793 | static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, | 1807 | static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, |
1794 | u16 value) | 1808 | u16 value) |
1795 | { | 1809 | { |
1796 | struct fec_enet_private *fep = bus->priv; | 1810 | struct fec_enet_private *fep = bus->priv; |
1811 | struct device *dev = &fep->pdev->dev; | ||
1797 | unsigned long time_left; | 1812 | unsigned long time_left; |
1813 | int ret = 0; | ||
1814 | |||
1815 | ret = pm_runtime_get_sync(dev); | ||
1816 | if (IS_ERR_VALUE(ret)) | ||
1817 | return ret; | ||
1798 | 1818 | ||
1799 | fep->mii_timeout = 0; | 1819 | fep->mii_timeout = 0; |
1800 | init_completion(&fep->mdio_done); | 1820 | init_completion(&fep->mdio_done); |
@@ -1811,10 +1831,13 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, | |||
1811 | if (time_left == 0) { | 1831 | if (time_left == 0) { |
1812 | fep->mii_timeout = 1; | 1832 | fep->mii_timeout = 1; |
1813 | netdev_err(fep->netdev, "MDIO write timeout\n"); | 1833 | netdev_err(fep->netdev, "MDIO write timeout\n"); |
1814 | return -ETIMEDOUT; | 1834 | ret = -ETIMEDOUT; |
1815 | } | 1835 | } |
1816 | 1836 | ||
1817 | return 0; | 1837 | pm_runtime_mark_last_busy(dev); |
1838 | pm_runtime_put_autosuspend(dev); | ||
1839 | |||
1840 | return ret; | ||
1818 | } | 1841 | } |
1819 | 1842 | ||
1820 | static int fec_enet_clk_enable(struct net_device *ndev, bool enable) | 1843 | static int fec_enet_clk_enable(struct net_device *ndev, bool enable) |
@@ -1826,9 +1849,6 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) | |||
1826 | ret = clk_prepare_enable(fep->clk_ahb); | 1849 | ret = clk_prepare_enable(fep->clk_ahb); |
1827 | if (ret) | 1850 | if (ret) |
1828 | return ret; | 1851 | return ret; |
1829 | ret = clk_prepare_enable(fep->clk_ipg); | ||
1830 | if (ret) | ||
1831 | goto failed_clk_ipg; | ||
1832 | if (fep->clk_enet_out) { | 1852 | if (fep->clk_enet_out) { |
1833 | ret = clk_prepare_enable(fep->clk_enet_out); | 1853 | ret = clk_prepare_enable(fep->clk_enet_out); |
1834 | if (ret) | 1854 | if (ret) |
@@ -1852,7 +1872,6 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable) | |||
1852 | } | 1872 | } |
1853 | } else { | 1873 | } else { |
1854 | clk_disable_unprepare(fep->clk_ahb); | 1874 | clk_disable_unprepare(fep->clk_ahb); |
1855 | clk_disable_unprepare(fep->clk_ipg); | ||
1856 | if (fep->clk_enet_out) | 1875 | if (fep->clk_enet_out) |
1857 | clk_disable_unprepare(fep->clk_enet_out); | 1876 | clk_disable_unprepare(fep->clk_enet_out); |
1858 | if (fep->clk_ptp) { | 1877 | if (fep->clk_ptp) { |
@@ -1874,8 +1893,6 @@ failed_clk_ptp: | |||
1874 | if (fep->clk_enet_out) | 1893 | if (fep->clk_enet_out) |
1875 | clk_disable_unprepare(fep->clk_enet_out); | 1894 | clk_disable_unprepare(fep->clk_enet_out); |
1876 | failed_clk_enet_out: | 1895 | failed_clk_enet_out: |
1877 | clk_disable_unprepare(fep->clk_ipg); | ||
1878 | failed_clk_ipg: | ||
1879 | clk_disable_unprepare(fep->clk_ahb); | 1896 | clk_disable_unprepare(fep->clk_ahb); |
1880 | 1897 | ||
1881 | return ret; | 1898 | return ret; |
@@ -2847,10 +2864,14 @@ fec_enet_open(struct net_device *ndev) | |||
2847 | struct fec_enet_private *fep = netdev_priv(ndev); | 2864 | struct fec_enet_private *fep = netdev_priv(ndev); |
2848 | int ret; | 2865 | int ret; |
2849 | 2866 | ||
2867 | ret = pm_runtime_get_sync(&fep->pdev->dev); | ||
2868 | if (IS_ERR_VALUE(ret)) | ||
2869 | return ret; | ||
2870 | |||
2850 | pinctrl_pm_select_default_state(&fep->pdev->dev); | 2871 | pinctrl_pm_select_default_state(&fep->pdev->dev); |
2851 | ret = fec_enet_clk_enable(ndev, true); | 2872 | ret = fec_enet_clk_enable(ndev, true); |
2852 | if (ret) | 2873 | if (ret) |
2853 | return ret; | 2874 | goto clk_enable; |
2854 | 2875 | ||
2855 | /* I should reset the ring buffers here, but I don't yet know | 2876 | /* I should reset the ring buffers here, but I don't yet know |
2856 | * a simple way to do that. | 2877 | * a simple way to do that. |
@@ -2881,6 +2902,9 @@ err_enet_mii_probe: | |||
2881 | fec_enet_free_buffers(ndev); | 2902 | fec_enet_free_buffers(ndev); |
2882 | err_enet_alloc: | 2903 | err_enet_alloc: |
2883 | fec_enet_clk_enable(ndev, false); | 2904 | fec_enet_clk_enable(ndev, false); |
2905 | clk_enable: | ||
2906 | pm_runtime_mark_last_busy(&fep->pdev->dev); | ||
2907 | pm_runtime_put_autosuspend(&fep->pdev->dev); | ||
2884 | pinctrl_pm_select_sleep_state(&fep->pdev->dev); | 2908 | pinctrl_pm_select_sleep_state(&fep->pdev->dev); |
2885 | return ret; | 2909 | return ret; |
2886 | } | 2910 | } |
@@ -2903,6 +2927,9 @@ fec_enet_close(struct net_device *ndev) | |||
2903 | 2927 | ||
2904 | fec_enet_clk_enable(ndev, false); | 2928 | fec_enet_clk_enable(ndev, false); |
2905 | pinctrl_pm_select_sleep_state(&fep->pdev->dev); | 2929 | pinctrl_pm_select_sleep_state(&fep->pdev->dev); |
2930 | pm_runtime_mark_last_busy(&fep->pdev->dev); | ||
2931 | pm_runtime_put_autosuspend(&fep->pdev->dev); | ||
2932 | |||
2906 | fec_enet_free_buffers(ndev); | 2933 | fec_enet_free_buffers(ndev); |
2907 | 2934 | ||
2908 | return 0; | 2935 | return 0; |
@@ -3388,6 +3415,10 @@ fec_probe(struct platform_device *pdev) | |||
3388 | if (ret) | 3415 | if (ret) |
3389 | goto failed_clk; | 3416 | goto failed_clk; |
3390 | 3417 | ||
3418 | ret = clk_prepare_enable(fep->clk_ipg); | ||
3419 | if (ret) | ||
3420 | goto failed_clk_ipg; | ||
3421 | |||
3391 | fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); | 3422 | fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); |
3392 | if (!IS_ERR(fep->reg_phy)) { | 3423 | if (!IS_ERR(fep->reg_phy)) { |
3393 | ret = regulator_enable(fep->reg_phy); | 3424 | ret = regulator_enable(fep->reg_phy); |
@@ -3400,6 +3431,11 @@ fec_probe(struct platform_device *pdev) | |||
3400 | fep->reg_phy = NULL; | 3431 | fep->reg_phy = NULL; |
3401 | } | 3432 | } |
3402 | 3433 | ||
3434 | pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); | ||
3435 | pm_runtime_use_autosuspend(&pdev->dev); | ||
3436 | pm_runtime_set_active(&pdev->dev); | ||
3437 | pm_runtime_enable(&pdev->dev); | ||
3438 | |||
3403 | fec_reset_phy(pdev); | 3439 | fec_reset_phy(pdev); |
3404 | 3440 | ||
3405 | if (fep->bufdesc_ex) | 3441 | if (fep->bufdesc_ex) |
@@ -3447,6 +3483,10 @@ fec_probe(struct platform_device *pdev) | |||
3447 | 3483 | ||
3448 | fep->rx_copybreak = COPYBREAK_DEFAULT; | 3484 | fep->rx_copybreak = COPYBREAK_DEFAULT; |
3449 | INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); | 3485 | INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); |
3486 | |||
3487 | pm_runtime_mark_last_busy(&pdev->dev); | ||
3488 | pm_runtime_put_autosuspend(&pdev->dev); | ||
3489 | |||
3450 | return 0; | 3490 | return 0; |
3451 | 3491 | ||
3452 | failed_register: | 3492 | failed_register: |
@@ -3458,6 +3498,8 @@ failed_init: | |||
3458 | if (fep->reg_phy) | 3498 | if (fep->reg_phy) |
3459 | regulator_disable(fep->reg_phy); | 3499 | regulator_disable(fep->reg_phy); |
3460 | failed_regulator: | 3500 | failed_regulator: |
3501 | clk_disable_unprepare(fep->clk_ipg); | ||
3502 | failed_clk_ipg: | ||
3461 | fec_enet_clk_enable(ndev, false); | 3503 | fec_enet_clk_enable(ndev, false); |
3462 | failed_clk: | 3504 | failed_clk: |
3463 | failed_phy: | 3505 | failed_phy: |
@@ -3567,7 +3609,28 @@ failed_clk: | |||
3567 | return ret; | 3609 | return ret; |
3568 | } | 3610 | } |
3569 | 3611 | ||
3570 | static SIMPLE_DEV_PM_OPS(fec_pm_ops, fec_suspend, fec_resume); | 3612 | static int __maybe_unused fec_runtime_suspend(struct device *dev) |
3613 | { | ||
3614 | struct net_device *ndev = dev_get_drvdata(dev); | ||
3615 | struct fec_enet_private *fep = netdev_priv(ndev); | ||
3616 | |||
3617 | clk_disable_unprepare(fep->clk_ipg); | ||
3618 | |||
3619 | return 0; | ||
3620 | } | ||
3621 | |||
3622 | static int __maybe_unused fec_runtime_resume(struct device *dev) | ||
3623 | { | ||
3624 | struct net_device *ndev = dev_get_drvdata(dev); | ||
3625 | struct fec_enet_private *fep = netdev_priv(ndev); | ||
3626 | |||
3627 | return clk_prepare_enable(fep->clk_ipg); | ||
3628 | } | ||
3629 | |||
3630 | static const struct dev_pm_ops fec_pm_ops = { | ||
3631 | SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) | ||
3632 | SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) | ||
3633 | }; | ||
3571 | 3634 | ||
3572 | static struct platform_driver fec_driver = { | 3635 | static struct platform_driver fec_driver = { |
3573 | .driver = { | 3636 | .driver = { |