@@ -384,6 +384,64 @@ static void bcm_sysport_get_stats(struct net_device *dev,
384384 }
385385}
386386
387+ static void bcm_sysport_get_wol (struct net_device * dev ,
388+ struct ethtool_wolinfo * wol )
389+ {
390+ struct bcm_sysport_priv * priv = netdev_priv (dev );
391+ u32 reg ;
392+
393+ wol -> supported = WAKE_MAGIC | WAKE_MAGICSECURE ;
394+ wol -> wolopts = priv -> wolopts ;
395+
396+ if (!(priv -> wolopts & WAKE_MAGICSECURE ))
397+ return ;
398+
399+ /* Return the programmed SecureOn password */
400+ reg = umac_readl (priv , UMAC_PSW_MS );
401+ put_unaligned_be16 (reg , & wol -> sopass [0 ]);
402+ reg = umac_readl (priv , UMAC_PSW_LS );
403+ put_unaligned_be32 (reg , & wol -> sopass [2 ]);
404+ }
405+
406+ static int bcm_sysport_set_wol (struct net_device * dev ,
407+ struct ethtool_wolinfo * wol )
408+ {
409+ struct bcm_sysport_priv * priv = netdev_priv (dev );
410+ struct device * kdev = & priv -> pdev -> dev ;
411+ u32 supported = WAKE_MAGIC | WAKE_MAGICSECURE ;
412+
413+ if (!device_can_wakeup (kdev ))
414+ return - ENOTSUPP ;
415+
416+ if (wol -> wolopts & ~supported )
417+ return - EINVAL ;
418+
419+ /* Program the SecureOn password */
420+ if (wol -> wolopts & WAKE_MAGICSECURE ) {
421+ umac_writel (priv , get_unaligned_be16 (& wol -> sopass [0 ]),
422+ UMAC_PSW_MS );
423+ umac_writel (priv , get_unaligned_be32 (& wol -> sopass [2 ]),
424+ UMAC_PSW_LS );
425+ }
426+
427+ /* Flag the device and relevant IRQ as wakeup capable */
428+ if (wol -> wolopts ) {
429+ device_set_wakeup_enable (kdev , 1 );
430+ enable_irq_wake (priv -> wol_irq );
431+ priv -> wol_irq_disabled = 0 ;
432+ } else {
433+ device_set_wakeup_enable (kdev , 0 );
434+ /* Avoid unbalanced disable_irq_wake calls */
435+ if (!priv -> wol_irq_disabled )
436+ disable_irq_wake (priv -> wol_irq );
437+ priv -> wol_irq_disabled = 1 ;
438+ }
439+
440+ priv -> wolopts = wol -> wolopts ;
441+
442+ return 0 ;
443+ }
444+
387445static void bcm_sysport_free_cb (struct bcm_sysport_cb * cb )
388446{
389447 dev_kfree_skb_any (cb -> skb );
@@ -692,6 +750,20 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
692750 return work_done ;
693751}
694752
753+ static void bcm_sysport_resume_from_wol (struct bcm_sysport_priv * priv )
754+ {
755+ u32 reg ;
756+
757+ /* Stop monitoring MPD interrupt */
758+ intrl2_0_mask_set (priv , INTRL2_0_MPD );
759+
760+ /* Clear the MagicPacket detection logic */
761+ reg = umac_readl (priv , UMAC_MPD_CTRL );
762+ reg &= ~MPD_EN ;
763+ umac_writel (priv , reg , UMAC_MPD_CTRL );
764+
765+ netif_dbg (priv , wol , priv -> netdev , "resumed from WOL\n" );
766+ }
695767
696768/* RX and misc interrupt routine */
697769static irqreturn_t bcm_sysport_rx_isr (int irq , void * dev_id )
@@ -722,6 +794,11 @@ static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
722794 if (priv -> irq0_stat & INTRL2_0_TX_RING_FULL )
723795 bcm_sysport_tx_reclaim_all (priv );
724796
797+ if (priv -> irq0_stat & INTRL2_0_MPD ) {
798+ netdev_info (priv -> netdev , "Wake-on-LAN interrupt!\n" );
799+ bcm_sysport_resume_from_wol (priv );
800+ }
801+
725802 return IRQ_HANDLED ;
726803}
727804
@@ -757,6 +834,15 @@ static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id)
757834 return IRQ_HANDLED ;
758835}
759836
837+ static irqreturn_t bcm_sysport_wol_isr (int irq , void * dev_id )
838+ {
839+ struct bcm_sysport_priv * priv = dev_id ;
840+
841+ pm_wakeup_event (& priv -> pdev -> dev , 0 );
842+
843+ return IRQ_HANDLED ;
844+ }
845+
760846static int bcm_sysport_insert_tsb (struct sk_buff * skb , struct net_device * dev )
761847{
762848 struct sk_buff * nskb ;
@@ -1507,6 +1593,8 @@ static struct ethtool_ops bcm_sysport_ethtool_ops = {
15071593 .get_strings = bcm_sysport_get_strings ,
15081594 .get_ethtool_stats = bcm_sysport_get_stats ,
15091595 .get_sset_count = bcm_sysport_get_sset_count ,
1596+ .get_wol = bcm_sysport_get_wol ,
1597+ .set_wol = bcm_sysport_set_wol ,
15101598};
15111599
15121600static const struct net_device_ops bcm_sysport_netdev_ops = {
@@ -1548,6 +1636,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
15481636
15491637 priv -> irq0 = platform_get_irq (pdev , 0 );
15501638 priv -> irq1 = platform_get_irq (pdev , 1 );
1639+ priv -> wol_irq = platform_get_irq (pdev , 2 );
15511640 if (priv -> irq0 <= 0 || priv -> irq1 <= 0 ) {
15521641 dev_err (& pdev -> dev , "invalid interrupts\n" );
15531642 ret = - EINVAL ;
@@ -1600,6 +1689,13 @@ static int bcm_sysport_probe(struct platform_device *pdev)
16001689 dev -> hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA |
16011690 NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ;
16021691
1692+ /* Request the WOL interrupt and advertise suspend if available */
1693+ priv -> wol_irq_disabled = 1 ;
1694+ ret = devm_request_irq (& pdev -> dev , priv -> wol_irq ,
1695+ bcm_sysport_wol_isr , 0 , dev -> name , priv );
1696+ if (!ret )
1697+ device_set_wakeup_capable (& pdev -> dev , 1 );
1698+
16031699 /* Set the needed headroom once and for all */
16041700 BUILD_BUG_ON (sizeof (struct bcm_tsb ) != 8 );
16051701 dev -> needed_headroom += sizeof (struct bcm_tsb );
@@ -1647,12 +1743,55 @@ static int bcm_sysport_remove(struct platform_device *pdev)
16471743}
16481744
16491745#ifdef CONFIG_PM_SLEEP
1746+ static int bcm_sysport_suspend_to_wol (struct bcm_sysport_priv * priv )
1747+ {
1748+ struct net_device * ndev = priv -> netdev ;
1749+ unsigned int timeout = 1000 ;
1750+ u32 reg ;
1751+
1752+ /* Password has already been programmed */
1753+ reg = umac_readl (priv , UMAC_MPD_CTRL );
1754+ reg |= MPD_EN ;
1755+ reg &= ~PSW_EN ;
1756+ if (priv -> wolopts & WAKE_MAGICSECURE )
1757+ reg |= PSW_EN ;
1758+ umac_writel (priv , reg , UMAC_MPD_CTRL );
1759+
1760+ /* Make sure RBUF entered WoL mode as result */
1761+ do {
1762+ reg = rbuf_readl (priv , RBUF_STATUS );
1763+ if (reg & RBUF_WOL_MODE )
1764+ break ;
1765+
1766+ udelay (10 );
1767+ } while (timeout -- > 0 );
1768+
1769+ /* Do not leave the UniMAC RBUF matching only MPD packets */
1770+ if (!timeout ) {
1771+ reg = umac_readl (priv , UMAC_MPD_CTRL );
1772+ reg &= ~MPD_EN ;
1773+ umac_writel (priv , reg , UMAC_MPD_CTRL );
1774+ netif_err (priv , wol , ndev , "failed to enter WOL mode\n" );
1775+ return - ETIMEDOUT ;
1776+ }
1777+
1778+ /* UniMAC receive needs to be turned on */
1779+ umac_enable_set (priv , CMD_RX_EN , 1 );
1780+
1781+ /* Enable the interrupt wake-up source */
1782+ intrl2_0_mask_clear (priv , INTRL2_0_MPD );
1783+
1784+ netif_dbg (priv , wol , ndev , "entered WOL mode\n" );
1785+
1786+ return 0 ;
1787+ }
1788+
16501789static int bcm_sysport_suspend (struct device * d )
16511790{
16521791 struct net_device * dev = dev_get_drvdata (d );
16531792 struct bcm_sysport_priv * priv = netdev_priv (dev );
16541793 unsigned int i ;
1655- int ret ;
1794+ int ret = 0 ;
16561795 u32 reg ;
16571796
16581797 if (!netif_running (dev ))
@@ -1681,7 +1820,8 @@ static int bcm_sysport_suspend(struct device *d)
16811820 }
16821821
16831822 /* Flush RX pipe */
1684- topctrl_writel (priv , RX_FLUSH , RX_FLUSH_CNTL );
1823+ if (!priv -> wolopts )
1824+ topctrl_writel (priv , RX_FLUSH , RX_FLUSH_CNTL );
16851825
16861826 ret = tdma_enable_set (priv , 0 );
16871827 if (ret ) {
@@ -1701,7 +1841,11 @@ static int bcm_sysport_suspend(struct device *d)
17011841 bcm_sysport_fini_tx_ring (priv , i );
17021842 bcm_sysport_fini_rx_ring (priv );
17031843
1704- return 0 ;
1844+ /* Get prepared for Wake-on-LAN */
1845+ if (device_may_wakeup (d ) && priv -> wolopts )
1846+ ret = bcm_sysport_suspend_to_wol (priv );
1847+
1848+ return ret ;
17051849}
17061850
17071851static int bcm_sysport_resume (struct device * d )
@@ -1715,6 +1859,11 @@ static int bcm_sysport_resume(struct device *d)
17151859 if (!netif_running (dev ))
17161860 return 0 ;
17171861
1862+ /* We may have been suspended and never received a WOL event that
1863+ * would turn off MPD detection, take care of that now
1864+ */
1865+ bcm_sysport_resume_from_wol (priv );
1866+
17181867 /* Initialize both hardware and software ring */
17191868 for (i = 0 ; i < dev -> num_tx_queues ; i ++ ) {
17201869 ret = bcm_sysport_init_tx_ring (priv , i );
0 commit comments