Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

DM9000: Wake on LAN support

Add support for Wake on LAN (WOL) reception and waking the device up from
this signal via the ethtool interface. Currently we are only supporting
the magic-packet variant of wakeup.

WOL is enabled by specifying a second interrupt resource to the driver
which indicates where the interrupt for the WOL is being signalled. This
then enables the necessary ethtool calls to leave the device in a state
to receive WOL frames when going into suspend.

Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Simtec Linux Team <linux@simtec.co.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ben Dooks and committed by
David S. Miller
c029f444 f9254eda

+142 -8
+135 -8
drivers/net/dm9000.c
··· 100 100 101 101 unsigned int flags; 102 102 unsigned int in_suspend :1; 103 + unsigned int wake_supported :1; 103 104 int debug_level; 104 105 105 106 enum dm9000_type type; ··· 117 116 struct resource *data_req; 118 117 struct resource *irq_res; 119 118 119 + int irq_wake; 120 + 120 121 struct mutex addr_lock; /* phy and eeprom access lock */ 121 122 122 123 struct delayed_work phy_poll; ··· 128 125 129 126 struct mii_if_info mii; 130 127 u32 msg_enable; 128 + u32 wake_state; 131 129 132 130 int rx_csum; 133 131 int can_csum; ··· 572 568 return 0; 573 569 } 574 570 571 + static void dm9000_get_wol(struct net_device *dev, struct ethtool_wolinfo *w) 572 + { 573 + board_info_t *dm = to_dm9000_board(dev); 574 + 575 + memset(w, 0, sizeof(struct ethtool_wolinfo)); 576 + 577 + /* note, we could probably support wake-phy too */ 578 + w->supported = dm->wake_supported ? WAKE_MAGIC : 0; 579 + w->wolopts = dm->wake_state; 580 + } 581 + 582 + static int dm9000_set_wol(struct net_device *dev, struct ethtool_wolinfo *w) 583 + { 584 + board_info_t *dm = to_dm9000_board(dev); 585 + unsigned long flags; 586 + u32 opts = w->wolopts; 587 + u32 wcr = 0; 588 + 589 + if (!dm->wake_supported) 590 + return -EOPNOTSUPP; 591 + 592 + if (opts & ~WAKE_MAGIC) 593 + return -EINVAL; 594 + 595 + if (opts & WAKE_MAGIC) 596 + wcr |= WCR_MAGICEN; 597 + 598 + mutex_lock(&dm->addr_lock); 599 + 600 + spin_lock_irqsave(&dm->lock, flags); 601 + iow(dm, DM9000_WCR, wcr); 602 + spin_unlock_irqrestore(&dm->lock, flags); 603 + 604 + mutex_unlock(&dm->addr_lock); 605 + 606 + if (dm->wake_state != opts) { 607 + /* change in wol state, update IRQ state */ 608 + 609 + if (!dm->wake_state) 610 + set_irq_wake(dm->irq_wake, 1); 611 + else if (dm->wake_state & !opts) 612 + set_irq_wake(dm->irq_wake, 0); 613 + } 614 + 615 + dm->wake_state = opts; 616 + return 0; 617 + } 618 + 575 619 static const struct ethtool_ops dm9000_ethtool_ops = { 576 620 .get_drvinfo = dm9000_get_drvinfo, 577 621 .get_settings = dm9000_get_settings, ··· 628 576 .set_msglevel = dm9000_set_msglevel, 629 577 .nway_reset = dm9000_nway_reset, 630 578 .get_link = dm9000_get_link, 579 + .get_wol = dm9000_get_wol, 580 + .set_wol = dm9000_set_wol, 631 581 .get_eeprom_len = dm9000_get_eeprom_len, 632 582 .get_eeprom = dm9000_get_eeprom, 633 583 .set_eeprom = dm9000_set_eeprom, ··· 776 722 { 777 723 board_info_t *db = netdev_priv(dev); 778 724 unsigned int imr; 725 + unsigned int ncr; 779 726 780 727 dm9000_dbg(db, 1, "entering %s\n", __func__); 781 728 ··· 791 736 iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */ 792 737 iow(db, DM9000_GPR, 0); /* Enable PHY */ 793 738 794 - if (db->flags & DM9000_PLATF_EXT_PHY) 795 - iow(db, DM9000_NCR, NCR_EXT_PHY); 739 + ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0; 740 + 741 + /* if wol is needed, then always set NCR_WAKEEN otherwise we end 742 + * up dumping the wake events if we disable this. There is already 743 + * a wake-mask in DM9000_WCR */ 744 + if (db->wake_supported) 745 + ncr |= NCR_WAKEEN; 746 + 747 + iow(db, DM9000_NCR, ncr); 796 748 797 749 /* Program operating register */ 798 750 iow(db, DM9000_TCR, 0); /* TX Polling clear */ ··· 1107 1045 return IRQ_HANDLED; 1108 1046 } 1109 1047 1048 + static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id) 1049 + { 1050 + struct net_device *dev = dev_id; 1051 + board_info_t *db = netdev_priv(dev); 1052 + unsigned long flags; 1053 + unsigned nsr, wcr; 1054 + 1055 + spin_lock_irqsave(&db->lock, flags); 1056 + 1057 + nsr = ior(db, DM9000_NSR); 1058 + wcr = ior(db, DM9000_WCR); 1059 + 1060 + dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr); 1061 + 1062 + if (nsr & NSR_WAKEST) { 1063 + /* clear, so we can avoid */ 1064 + iow(db, DM9000_NSR, NSR_WAKEST); 1065 + 1066 + if (wcr & WCR_LINKST) 1067 + dev_info(db->dev, "wake by link status change\n"); 1068 + if (wcr & WCR_SAMPLEST) 1069 + dev_info(db->dev, "wake by sample packet\n"); 1070 + if (wcr & WCR_MAGICST ) 1071 + dev_info(db->dev, "wake by magic packet\n"); 1072 + if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST))) 1073 + dev_err(db->dev, "wake signalled with no reason? " 1074 + "NSR=0x%02x, WSR=0x%02x\n", nsr, wcr); 1075 + 1076 + } 1077 + 1078 + spin_unlock_irqrestore(&db->lock, flags); 1079 + 1080 + return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE; 1081 + } 1082 + 1110 1083 #ifdef CONFIG_NET_POLL_CONTROLLER 1111 1084 /* 1112 1085 *Used by netconsole ··· 1396 1299 goto out; 1397 1300 } 1398 1301 1302 + db->irq_wake = platform_get_irq(pdev, 1); 1303 + if (db->irq_wake >= 0) { 1304 + dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake); 1305 + 1306 + ret = request_irq(db->irq_wake, dm9000_wol_interrupt, 1307 + IRQF_SHARED, dev_name(db->dev), ndev); 1308 + if (ret) { 1309 + dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret); 1310 + } else { 1311 + 1312 + /* test to see if irq is really wakeup capable */ 1313 + ret = set_irq_wake(db->irq_wake, 1); 1314 + if (ret) { 1315 + dev_err(db->dev, "irq %d cannot set wakeup (%d)\n", 1316 + db->irq_wake, ret); 1317 + ret = 0; 1318 + } else { 1319 + set_irq_wake(db->irq_wake, 0); 1320 + db->wake_supported = 1; 1321 + } 1322 + } 1323 + } 1324 + 1399 1325 iosize = resource_size(db->addr_res); 1400 1326 db->addr_req = request_mem_region(db->addr_res->start, iosize, 1401 1327 pdev->name); ··· 1610 1490 db = netdev_priv(ndev); 1611 1491 db->in_suspend = 1; 1612 1492 1613 - if (netif_running(ndev)) { 1614 - netif_device_detach(ndev); 1493 + if (!netif_running(ndev)) 1494 + return 0; 1495 + 1496 + netif_device_detach(ndev); 1497 + 1498 + /* only shutdown if not using WoL */ 1499 + if (!db->wake_state) 1615 1500 dm9000_shutdown(ndev); 1616 - } 1617 1501 } 1618 1502 return 0; 1619 1503 } ··· 1630 1506 board_info_t *db = netdev_priv(ndev); 1631 1507 1632 1508 if (ndev) { 1633 - 1634 1509 if (netif_running(ndev)) { 1635 - dm9000_reset(db); 1636 - dm9000_init_dm9000(ndev); 1510 + /* reset if we were not in wake mode to ensure if 1511 + * the device was powered off it is in a known state */ 1512 + if (!db->wake_state) { 1513 + dm9000_reset(db); 1514 + dm9000_init_dm9000(ndev); 1515 + } 1637 1516 1638 1517 netif_device_attach(ndev); 1639 1518 }
+7
drivers/net/dm9000.h
··· 111 111 #define RSR_CE (1<<1) 112 112 #define RSR_FOE (1<<0) 113 113 114 + #define WCR_LINKEN (1 << 5) 115 + #define WCR_SAMPLEEN (1 << 4) 116 + #define WCR_MAGICEN (1 << 3) 117 + #define WCR_LINKST (1 << 2) 118 + #define WCR_SAMPLEST (1 << 1) 119 + #define WCR_MAGICST (1 << 0) 120 + 114 121 #define FCTR_HWOT(ot) (( ot & 0xf ) << 4 ) 115 122 #define FCTR_LWOT(ot) ( ot & 0xf ) 116 123