diff options
author | Fugang Duan <b38611@freescale.com> | 2014-05-13 02:22:14 -0400 |
---|---|---|
committer | Fugang Duan <b38611@freescale.com> | 2014-05-15 03:55:06 -0400 |
commit | 349166536e7aa124f8d559044147f2550a5cb87a (patch) | |
tree | d9ec2fdc00ea3f10ce32b84ab04511fd83af58f7 | |
parent | 4bcf608a8a5b74f595faab5b6cb4f4e84e711471 (diff) |
ENGR00313508-02 net: fec: add sleep mode support
When imx6sx is in stop mode, enet sleep mode is entered, the magic
pattern can wake up imx6sx system.
Use ethtool to enable/disable wol magic pattern wake up.
Signed-off-by: Fugang Duan <B38611@freescale.com>
-rw-r--r-- | drivers/net/ethernet/freescale/fec.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/fec_main.c | 109 |
2 files changed, 102 insertions, 9 deletions
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 7190f171d02e..878d64efc178 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h | |||
@@ -307,6 +307,7 @@ struct bufdesc_ex { | |||
307 | #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ | 307 | #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ |
308 | #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ | 308 | #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ |
309 | #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ | 309 | #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ |
310 | #define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */ | ||
310 | #define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) | 311 | #define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) |
311 | #define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) | 312 | #define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) |
312 | #define FEC_ENET_TS_AVAIL ((uint)0x00010000) | 313 | #define FEC_ENET_TS_AVAIL ((uint)0x00010000) |
@@ -591,6 +592,7 @@ struct fec_enet_private { | |||
591 | u64 prtc; | 592 | u64 prtc; |
592 | int bufdesc_ex; | 593 | int bufdesc_ex; |
593 | int pause_flag; | 594 | int pause_flag; |
595 | int wol_flag; | ||
594 | 596 | ||
595 | struct napi_struct napi; | 597 | struct napi_struct napi; |
596 | int csum_flags; | 598 | int csum_flags; |
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 3447f7c2248d..165310209fe6 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c | |||
@@ -196,6 +196,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); | |||
196 | #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) | 196 | #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) |
197 | #define FEC_MMFR_TA (2 << 16) | 197 | #define FEC_MMFR_TA (2 << 16) |
198 | #define FEC_MMFR_DATA(v) (v & 0xffff) | 198 | #define FEC_MMFR_DATA(v) (v & 0xffff) |
199 | /* FEC ECR bits definition */ | ||
200 | #define FEC_ECR_MAGICEN (1 << 2) | ||
201 | #define FEC_ECR_SLEEP (1 << 3) | ||
199 | 202 | ||
200 | #define FEC_MII_TIMEOUT 30000 /* us */ | 203 | #define FEC_MII_TIMEOUT 30000 /* us */ |
201 | 204 | ||
@@ -204,6 +207,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); | |||
204 | 207 | ||
205 | #define FEC_PAUSE_FLAG_AUTONEG 0x1 | 208 | #define FEC_PAUSE_FLAG_AUTONEG 0x1 |
206 | #define FEC_PAUSE_FLAG_ENABLE 0x2 | 209 | #define FEC_PAUSE_FLAG_ENABLE 0x2 |
210 | #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) | ||
211 | #define FEC_WOL_FLAG_ENABLE (0x1 << 1) | ||
212 | #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) | ||
207 | 213 | ||
208 | static int mii_cnt; | 214 | static int mii_cnt; |
209 | 215 | ||
@@ -765,9 +771,11 @@ static void | |||
765 | fec_stop(struct net_device *ndev) | 771 | fec_stop(struct net_device *ndev) |
766 | { | 772 | { |
767 | struct fec_enet_private *fep = netdev_priv(ndev); | 773 | struct fec_enet_private *fep = netdev_priv(ndev); |
774 | struct fec_platform_data *pdata = fep->pdev->dev.platform_data; | ||
768 | const struct platform_device_id *id_entry = | 775 | const struct platform_device_id *id_entry = |
769 | platform_get_device_id(fep->pdev); | 776 | platform_get_device_id(fep->pdev); |
770 | u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); | 777 | u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); |
778 | u32 val; | ||
771 | 779 | ||
772 | /* We cannot expect a graceful transmit stop without link !!! */ | 780 | /* We cannot expect a graceful transmit stop without link !!! */ |
773 | if (fep->link) { | 781 | if (fep->link) { |
@@ -782,14 +790,27 @@ fec_stop(struct net_device *ndev) | |||
782 | * For i.MX6SX SOC, enet use AXI bus, we use disable MAC | 790 | * For i.MX6SX SOC, enet use AXI bus, we use disable MAC |
783 | * instead of reset MAC itself. | 791 | * instead of reset MAC itself. |
784 | */ | 792 | */ |
785 | if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) | 793 | if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { |
786 | writel(0, fep->hwp + FEC_ECNTRL); | 794 | if (id_entry && id_entry->driver_data & FEC_QUIRK_HAS_AVB) |
787 | else { | 795 | writel(0, fep->hwp + FEC_ECNTRL); |
788 | writel(1, fep->hwp + FEC_ECNTRL); | 796 | else { |
789 | udelay(10); | 797 | writel(1, fep->hwp + FEC_ECNTRL); |
798 | udelay(10); | ||
799 | } | ||
800 | |||
801 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); | ||
802 | } else { | ||
803 | writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, | ||
804 | fep->hwp + FEC_IMASK); | ||
805 | val = readl(fep->hwp + FEC_ECNTRL); | ||
806 | val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); | ||
807 | writel(val, fep->hwp + FEC_ECNTRL); | ||
808 | |||
809 | if (pdata && pdata->sleep_mode_enable) | ||
810 | pdata->sleep_mode_enable(true); | ||
790 | } | 811 | } |
812 | |||
791 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | 813 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
792 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); | ||
793 | 814 | ||
794 | if (fep->bufdesc_ex && (fep->hwts_tx_en_ioctl || | 815 | if (fep->bufdesc_ex && (fep->hwts_tx_en_ioctl || |
795 | fep->hwts_rx_en_ioctl || fep->hwts_tx_en || | 816 | fep->hwts_rx_en_ioctl || fep->hwts_tx_en || |
@@ -797,7 +818,8 @@ fec_stop(struct net_device *ndev) | |||
797 | fec_ptp_stop(ndev); | 818 | fec_ptp_stop(ndev); |
798 | 819 | ||
799 | /* We have to keep ENET enabled to have MII interrupt stay working */ | 820 | /* We have to keep ENET enabled to have MII interrupt stay working */ |
800 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | 821 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC && |
822 | !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { | ||
801 | writel(2, fep->hwp + FEC_ECNTRL); | 823 | writel(2, fep->hwp + FEC_ECNTRL); |
802 | writel(rmii_mode, fep->hwp + FEC_R_CNTRL); | 824 | writel(rmii_mode, fep->hwp + FEC_R_CNTRL); |
803 | } | 825 | } |
@@ -1974,6 +1996,47 @@ static void fec_enet_itr_coal_init(struct net_device *ndev) | |||
1974 | fec_enet_set_coalesce(ndev, &ec); | 1996 | fec_enet_set_coalesce(ndev, &ec); |
1975 | } | 1997 | } |
1976 | 1998 | ||
1999 | #ifdef CONFIG_PM | ||
2000 | static void | ||
2001 | fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) | ||
2002 | { | ||
2003 | struct fec_enet_private *fep = netdev_priv(ndev); | ||
2004 | |||
2005 | if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { | ||
2006 | wol->supported = WAKE_MAGIC; | ||
2007 | wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; | ||
2008 | } else { | ||
2009 | wol->supported = wol->wolopts = 0; | ||
2010 | } | ||
2011 | } | ||
2012 | |||
2013 | static int | ||
2014 | fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) | ||
2015 | { | ||
2016 | struct fec_enet_private *fep = netdev_priv(ndev); | ||
2017 | |||
2018 | if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) && | ||
2019 | wol->wolopts != 0) | ||
2020 | return -EINVAL; | ||
2021 | |||
2022 | if (wol->wolopts & ~WAKE_MAGIC) | ||
2023 | return -EINVAL; | ||
2024 | |||
2025 | device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); | ||
2026 | if (device_may_wakeup(&ndev->dev)) { | ||
2027 | fep->wol_flag |= FEC_WOL_FLAG_ENABLE; | ||
2028 | if (fep->irq[0] > 0) | ||
2029 | enable_irq_wake(fep->irq[0]); | ||
2030 | } else { | ||
2031 | fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); | ||
2032 | if (fep->irq[0] > 0) | ||
2033 | disable_irq_wake(fep->irq[0]); | ||
2034 | } | ||
2035 | |||
2036 | return 0; | ||
2037 | } | ||
2038 | #endif | ||
2039 | |||
1977 | static const struct ethtool_ops fec_enet_ethtool_ops = { | 2040 | static const struct ethtool_ops fec_enet_ethtool_ops = { |
1978 | #if !defined(CONFIG_M5272) | 2041 | #if !defined(CONFIG_M5272) |
1979 | .get_pauseparam = fec_enet_get_pauseparam, | 2042 | .get_pauseparam = fec_enet_get_pauseparam, |
@@ -1992,6 +2055,10 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { | |||
1992 | .get_strings = fec_enet_get_strings, | 2055 | .get_strings = fec_enet_get_strings, |
1993 | .get_sset_count = fec_enet_get_sset_count, | 2056 | .get_sset_count = fec_enet_get_sset_count, |
1994 | #endif | 2057 | #endif |
2058 | #ifdef CONFIG_PM | ||
2059 | .get_wol = fec_enet_get_wol, | ||
2060 | .set_wol = fec_enet_set_wol, | ||
2061 | #endif | ||
1995 | }; | 2062 | }; |
1996 | 2063 | ||
1997 | static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) | 2064 | static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) |
@@ -2171,6 +2238,9 @@ fec_enet_open(struct net_device *ndev) | |||
2171 | PM_QOS_CPU_DMA_LATENCY, | 2238 | PM_QOS_CPU_DMA_LATENCY, |
2172 | PM_QOS_DEFAULT_VALUE); | 2239 | PM_QOS_DEFAULT_VALUE); |
2173 | 2240 | ||
2241 | device_set_wakeup_enable(&ndev->dev, | ||
2242 | fep->wol_flag & FEC_WOL_FLAG_ENABLE); | ||
2243 | |||
2174 | return 0; | 2244 | return 0; |
2175 | } | 2245 | } |
2176 | 2246 | ||
@@ -2539,6 +2609,9 @@ static void fec_of_init(struct platform_device *pdev) | |||
2539 | if (!np) | 2609 | if (!np) |
2540 | return; | 2610 | return; |
2541 | 2611 | ||
2612 | if (of_get_property(np, "fsl,magic-packet", NULL)) | ||
2613 | fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; | ||
2614 | |||
2542 | of_property_read_u32(np, "phy-reset-duration", | 2615 | of_property_read_u32(np, "phy-reset-duration", |
2543 | &fep->reset_duration); | 2616 | &fep->reset_duration); |
2544 | /* A sane reset duration should not be longer than 1s */ | 2617 | /* A sane reset duration should not be longer than 1s */ |
@@ -2751,6 +2824,7 @@ fec_probe(struct platform_device *pdev) | |||
2751 | } | 2824 | } |
2752 | goto failed_irq; | 2825 | goto failed_irq; |
2753 | } | 2826 | } |
2827 | fep->irq[i] = irq; | ||
2754 | } | 2828 | } |
2755 | 2829 | ||
2756 | init_completion(&fep->mdio_done); | 2830 | init_completion(&fep->mdio_done); |
@@ -2760,6 +2834,9 @@ fec_probe(struct platform_device *pdev) | |||
2760 | 2834 | ||
2761 | /* Carrier starts down, phylib will bring it up */ | 2835 | /* Carrier starts down, phylib will bring it up */ |
2762 | netif_carrier_off(ndev); | 2836 | netif_carrier_off(ndev); |
2837 | device_init_wakeup(&ndev->dev, | ||
2838 | fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET); | ||
2839 | |||
2763 | fec_enet_clk_enable(ndev, false); | 2840 | fec_enet_clk_enable(ndev, false); |
2764 | 2841 | ||
2765 | /* Select sleep pin state */ | 2842 | /* Select sleep pin state */ |
@@ -2835,9 +2912,12 @@ fec_suspend(struct device *dev) | |||
2835 | struct fec_enet_private *fep = netdev_priv(ndev); | 2912 | struct fec_enet_private *fep = netdev_priv(ndev); |
2836 | 2913 | ||
2837 | if (netif_running(ndev)) { | 2914 | if (netif_running(ndev)) { |
2915 | if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) | ||
2916 | fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; | ||
2838 | fec_stop(ndev); | 2917 | fec_stop(ndev); |
2839 | netif_device_detach(ndev); | 2918 | netif_device_detach(ndev); |
2840 | fec_enet_clk_enable(ndev, false); | 2919 | if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) |
2920 | fec_enet_clk_enable(ndev, false); | ||
2841 | pinctrl_pm_select_sleep_state(&fep->pdev->dev); | 2921 | pinctrl_pm_select_sleep_state(&fep->pdev->dev); |
2842 | } | 2922 | } |
2843 | 2923 | ||
@@ -2859,7 +2939,9 @@ fec_resume(struct device *dev) | |||
2859 | { | 2939 | { |
2860 | struct net_device *ndev = dev_get_drvdata(dev); | 2940 | struct net_device *ndev = dev_get_drvdata(dev); |
2861 | struct fec_enet_private *fep = netdev_priv(ndev); | 2941 | struct fec_enet_private *fep = netdev_priv(ndev); |
2942 | struct fec_platform_data *pdata = fep->pdev->dev.platform_data; | ||
2862 | int ret; | 2943 | int ret; |
2944 | int val; | ||
2863 | 2945 | ||
2864 | if (fep->reg_phy) { | 2946 | if (fep->reg_phy) { |
2865 | ret = regulator_enable(fep->reg_phy); | 2947 | ret = regulator_enable(fep->reg_phy); |
@@ -2869,7 +2951,16 @@ fec_resume(struct device *dev) | |||
2869 | 2951 | ||
2870 | if (netif_running(ndev)) { | 2952 | if (netif_running(ndev)) { |
2871 | pinctrl_pm_select_default_state(&fep->pdev->dev); | 2953 | pinctrl_pm_select_default_state(&fep->pdev->dev); |
2872 | fec_enet_clk_enable(ndev, true); | 2954 | if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { |
2955 | if (pdata && pdata->sleep_mode_enable) | ||
2956 | pdata->sleep_mode_enable(false); | ||
2957 | val = readl(fep->hwp + FEC_ECNTRL); | ||
2958 | val &= (~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP)); | ||
2959 | writel(val, fep->hwp + FEC_ECNTRL); | ||
2960 | fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; | ||
2961 | } else { | ||
2962 | fec_enet_clk_enable(ndev, true); | ||
2963 | } | ||
2873 | if (!fep->clk_enet_out && !fep->reg_phy) | 2964 | if (!fep->clk_enet_out && !fep->reg_phy) |
2874 | fec_restart(ndev, fep->full_duplex); | 2965 | fec_restart(ndev, fep->full_duplex); |
2875 | netif_device_attach(ndev); | 2966 | netif_device_attach(ndev); |