@@ -384,6 +384,64 @@ static void bcm_sysport_get_stats(struct net_device *dev,
384
384
}
385
385
}
386
386
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
+
387
445
static void bcm_sysport_free_cb (struct bcm_sysport_cb * cb )
388
446
{
389
447
dev_kfree_skb_any (cb -> skb );
@@ -692,6 +750,20 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget)
692
750
return work_done ;
693
751
}
694
752
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
+ }
695
767
696
768
/* RX and misc interrupt routine */
697
769
static 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)
722
794
if (priv -> irq0_stat & INTRL2_0_TX_RING_FULL )
723
795
bcm_sysport_tx_reclaim_all (priv );
724
796
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
+
725
802
return IRQ_HANDLED ;
726
803
}
727
804
@@ -757,6 +834,15 @@ static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id)
757
834
return IRQ_HANDLED ;
758
835
}
759
836
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
+
760
846
static int bcm_sysport_insert_tsb (struct sk_buff * skb , struct net_device * dev )
761
847
{
762
848
struct sk_buff * nskb ;
@@ -1507,6 +1593,8 @@ static struct ethtool_ops bcm_sysport_ethtool_ops = {
1507
1593
.get_strings = bcm_sysport_get_strings ,
1508
1594
.get_ethtool_stats = bcm_sysport_get_stats ,
1509
1595
.get_sset_count = bcm_sysport_get_sset_count ,
1596
+ .get_wol = bcm_sysport_get_wol ,
1597
+ .set_wol = bcm_sysport_set_wol ,
1510
1598
};
1511
1599
1512
1600
static const struct net_device_ops bcm_sysport_netdev_ops = {
@@ -1548,6 +1636,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
1548
1636
1549
1637
priv -> irq0 = platform_get_irq (pdev , 0 );
1550
1638
priv -> irq1 = platform_get_irq (pdev , 1 );
1639
+ priv -> wol_irq = platform_get_irq (pdev , 2 );
1551
1640
if (priv -> irq0 <= 0 || priv -> irq1 <= 0 ) {
1552
1641
dev_err (& pdev -> dev , "invalid interrupts\n" );
1553
1642
ret = - EINVAL ;
@@ -1600,6 +1689,13 @@ static int bcm_sysport_probe(struct platform_device *pdev)
1600
1689
dev -> hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA |
1601
1690
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ;
1602
1691
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
+
1603
1699
/* Set the needed headroom once and for all */
1604
1700
BUILD_BUG_ON (sizeof (struct bcm_tsb ) != 8 );
1605
1701
dev -> needed_headroom += sizeof (struct bcm_tsb );
@@ -1647,12 +1743,55 @@ static int bcm_sysport_remove(struct platform_device *pdev)
1647
1743
}
1648
1744
1649
1745
#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
+
1650
1789
static int bcm_sysport_suspend (struct device * d )
1651
1790
{
1652
1791
struct net_device * dev = dev_get_drvdata (d );
1653
1792
struct bcm_sysport_priv * priv = netdev_priv (dev );
1654
1793
unsigned int i ;
1655
- int ret ;
1794
+ int ret = 0 ;
1656
1795
u32 reg ;
1657
1796
1658
1797
if (!netif_running (dev ))
@@ -1681,7 +1820,8 @@ static int bcm_sysport_suspend(struct device *d)
1681
1820
}
1682
1821
1683
1822
/* 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 );
1685
1825
1686
1826
ret = tdma_enable_set (priv , 0 );
1687
1827
if (ret ) {
@@ -1701,7 +1841,11 @@ static int bcm_sysport_suspend(struct device *d)
1701
1841
bcm_sysport_fini_tx_ring (priv , i );
1702
1842
bcm_sysport_fini_rx_ring (priv );
1703
1843
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 ;
1705
1849
}
1706
1850
1707
1851
static int bcm_sysport_resume (struct device * d )
@@ -1715,6 +1859,11 @@ static int bcm_sysport_resume(struct device *d)
1715
1859
if (!netif_running (dev ))
1716
1860
return 0 ;
1717
1861
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
+
1718
1867
/* Initialize both hardware and software ring */
1719
1868
for (i = 0 ; i < dev -> num_tx_queues ; i ++ ) {
1720
1869
ret = bcm_sysport_init_tx_ring (priv , i );
0 commit comments