diff options
author | Steven Walter <stevenrwalter@gmail.com> | 2010-05-31 08:34:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-01 03:15:51 -0400 |
commit | db6f30078dcb0117336f20275e4828c86132e46e (patch) | |
tree | 2fb0213d26c06ceb98b0b8b4ad47afde8ef9a71e /drivers | |
parent | 7a1d7f01b5e90f85d0b4ec4bfd5a5da769d2bb1d (diff) |
tulip: implement wake-on-lan support
Based on a patch from http://simon.baatz.info/wol-support-for-an983b/
Tested to resume from suspend by magic packet.
Signed-off-by: Steven Walter <stevenrwalter@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/tulip/tulip.h | 64 | ||||
-rw-r--r-- | drivers/net/tulip/tulip_core.c | 115 |
2 files changed, 156 insertions, 23 deletions
diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 0afa2d4f9472..e525875ed67d 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/types.h> | 20 | #include <linux/types.h> |
21 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
22 | #include <linux/netdevice.h> | 22 | #include <linux/netdevice.h> |
23 | #include <linux/ethtool.h> | ||
23 | #include <linux/timer.h> | 24 | #include <linux/timer.h> |
24 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
25 | #include <linux/pci.h> | 26 | #include <linux/pci.h> |
@@ -51,22 +52,23 @@ struct tulip_chip_table { | |||
51 | 52 | ||
52 | 53 | ||
53 | enum tbl_flag { | 54 | enum tbl_flag { |
54 | HAS_MII = 0x0001, | 55 | HAS_MII = 0x00001, |
55 | HAS_MEDIA_TABLE = 0x0002, | 56 | HAS_MEDIA_TABLE = 0x00002, |
56 | CSR12_IN_SROM = 0x0004, | 57 | CSR12_IN_SROM = 0x00004, |
57 | ALWAYS_CHECK_MII = 0x0008, | 58 | ALWAYS_CHECK_MII = 0x00008, |
58 | HAS_ACPI = 0x0010, | 59 | HAS_ACPI = 0x00010, |
59 | MC_HASH_ONLY = 0x0020, /* Hash-only multicast filter. */ | 60 | MC_HASH_ONLY = 0x00020, /* Hash-only multicast filter. */ |
60 | HAS_PNICNWAY = 0x0080, | 61 | HAS_PNICNWAY = 0x00080, |
61 | HAS_NWAY = 0x0040, /* Uses internal NWay xcvr. */ | 62 | HAS_NWAY = 0x00040, /* Uses internal NWay xcvr. */ |
62 | HAS_INTR_MITIGATION = 0x0100, | 63 | HAS_INTR_MITIGATION = 0x00100, |
63 | IS_ASIX = 0x0200, | 64 | IS_ASIX = 0x00200, |
64 | HAS_8023X = 0x0400, | 65 | HAS_8023X = 0x00400, |
65 | COMET_MAC_ADDR = 0x0800, | 66 | COMET_MAC_ADDR = 0x00800, |
66 | HAS_PCI_MWI = 0x1000, | 67 | HAS_PCI_MWI = 0x01000, |
67 | HAS_PHY_IRQ = 0x2000, | 68 | HAS_PHY_IRQ = 0x02000, |
68 | HAS_SWAPPED_SEEPROM = 0x4000, | 69 | HAS_SWAPPED_SEEPROM = 0x04000, |
69 | NEEDS_FAKE_MEDIA_TABLE = 0x8000, | 70 | NEEDS_FAKE_MEDIA_TABLE = 0x08000, |
71 | COMET_PM = 0x10000, | ||
70 | }; | 72 | }; |
71 | 73 | ||
72 | 74 | ||
@@ -120,6 +122,11 @@ enum tulip_offsets { | |||
120 | CSR13 = 0x68, | 122 | CSR13 = 0x68, |
121 | CSR14 = 0x70, | 123 | CSR14 = 0x70, |
122 | CSR15 = 0x78, | 124 | CSR15 = 0x78, |
125 | CSR18 = 0x88, | ||
126 | CSR19 = 0x8c, | ||
127 | CSR20 = 0x90, | ||
128 | CSR27 = 0xAC, | ||
129 | CSR28 = 0xB0, | ||
123 | }; | 130 | }; |
124 | 131 | ||
125 | /* register offset and bits for CFDD PCI config reg */ | 132 | /* register offset and bits for CFDD PCI config reg */ |
@@ -289,6 +296,30 @@ enum t21143_csr6_bits { | |||
289 | csr6_mask_100bt = (csr6_scr | csr6_pcs | csr6_hbd), | 296 | csr6_mask_100bt = (csr6_scr | csr6_pcs | csr6_hbd), |
290 | }; | 297 | }; |
291 | 298 | ||
299 | enum tulip_comet_csr13_bits { | ||
300 | /* The LINKOFFE and LINKONE work in conjunction with LSCE, i.e. they | ||
301 | * determine which link status transition wakes up if LSCE is | ||
302 | * enabled */ | ||
303 | comet_csr13_linkoffe = (1 << 17), | ||
304 | comet_csr13_linkone = (1 << 16), | ||
305 | comet_csr13_wfre = (1 << 10), | ||
306 | comet_csr13_mpre = (1 << 9), | ||
307 | comet_csr13_lsce = (1 << 8), | ||
308 | comet_csr13_wfr = (1 << 2), | ||
309 | comet_csr13_mpr = (1 << 1), | ||
310 | comet_csr13_lsc = (1 << 0), | ||
311 | }; | ||
312 | |||
313 | enum tulip_comet_csr18_bits { | ||
314 | comet_csr18_pmes_sticky = (1 << 24), | ||
315 | comet_csr18_pm_mode = (1 << 19), | ||
316 | comet_csr18_apm_mode = (1 << 18), | ||
317 | comet_csr18_d3a = (1 << 7) | ||
318 | }; | ||
319 | |||
320 | enum tulip_comet_csr20_bits { | ||
321 | comet_csr20_pmes = (1 << 15), | ||
322 | }; | ||
292 | 323 | ||
293 | /* Keep the ring sizes a power of two for efficiency. | 324 | /* Keep the ring sizes a power of two for efficiency. |
294 | Making the Tx ring too large decreases the effectiveness of channel | 325 | Making the Tx ring too large decreases the effectiveness of channel |
@@ -411,6 +442,7 @@ struct tulip_private { | |||
411 | unsigned int csr6; /* Current CSR6 control settings. */ | 442 | unsigned int csr6; /* Current CSR6 control settings. */ |
412 | unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ | 443 | unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ |
413 | void (*link_change) (struct net_device * dev, int csr5); | 444 | void (*link_change) (struct net_device * dev, int csr5); |
445 | struct ethtool_wolinfo wolinfo; /* WOL settings */ | ||
414 | u16 sym_advertise, mii_advertise; /* NWay capabilities advertised. */ | 446 | u16 sym_advertise, mii_advertise; /* NWay capabilities advertised. */ |
415 | u16 lpar; /* 21143 Link partner ability. */ | 447 | u16 lpar; /* 21143 Link partner ability. */ |
416 | u16 advertising[4]; | 448 | u16 advertising[4]; |
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index bd3b41daa892..03e96b928c04 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <linux/etherdevice.h> | 30 | #include <linux/etherdevice.h> |
31 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
32 | #include <linux/mii.h> | 32 | #include <linux/mii.h> |
33 | #include <linux/ethtool.h> | ||
34 | #include <linux/crc32.h> | 33 | #include <linux/crc32.h> |
35 | #include <asm/unaligned.h> | 34 | #include <asm/unaligned.h> |
36 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
@@ -272,6 +271,7 @@ static void tulip_down(struct net_device *dev); | |||
272 | static struct net_device_stats *tulip_get_stats(struct net_device *dev); | 271 | static struct net_device_stats *tulip_get_stats(struct net_device *dev); |
273 | static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); | 272 | static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); |
274 | static void set_rx_mode(struct net_device *dev); | 273 | static void set_rx_mode(struct net_device *dev); |
274 | static void tulip_set_wolopts(struct pci_dev *pdev, u32 wolopts); | ||
275 | #ifdef CONFIG_NET_POLL_CONTROLLER | 275 | #ifdef CONFIG_NET_POLL_CONTROLLER |
276 | static void poll_tulip(struct net_device *dev); | 276 | static void poll_tulip(struct net_device *dev); |
277 | #endif | 277 | #endif |
@@ -309,6 +309,11 @@ static void tulip_up(struct net_device *dev) | |||
309 | /* Wake the chip from sleep/snooze mode. */ | 309 | /* Wake the chip from sleep/snooze mode. */ |
310 | tulip_set_power_state (tp, 0, 0); | 310 | tulip_set_power_state (tp, 0, 0); |
311 | 311 | ||
312 | /* Disable all WOL events */ | ||
313 | pci_enable_wake(tp->pdev, PCI_D3hot, 0); | ||
314 | pci_enable_wake(tp->pdev, PCI_D3cold, 0); | ||
315 | tulip_set_wolopts(tp->pdev, 0); | ||
316 | |||
312 | /* On some chip revs we must set the MII/SYM port before the reset!? */ | 317 | /* On some chip revs we must set the MII/SYM port before the reset!? */ |
313 | if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) | 318 | if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) |
314 | iowrite32(0x00040000, ioaddr + CSR6); | 319 | iowrite32(0x00040000, ioaddr + CSR6); |
@@ -345,8 +350,8 @@ static void tulip_up(struct net_device *dev) | |||
345 | } else if (tp->flags & COMET_MAC_ADDR) { | 350 | } else if (tp->flags & COMET_MAC_ADDR) { |
346 | iowrite32(addr_low, ioaddr + 0xA4); | 351 | iowrite32(addr_low, ioaddr + 0xA4); |
347 | iowrite32(addr_high, ioaddr + 0xA8); | 352 | iowrite32(addr_high, ioaddr + 0xA8); |
348 | iowrite32(0, ioaddr + 0xAC); | 353 | iowrite32(0, ioaddr + CSR27); |
349 | iowrite32(0, ioaddr + 0xB0); | 354 | iowrite32(0, ioaddr + CSR28); |
350 | } | 355 | } |
351 | } else { | 356 | } else { |
352 | /* This is set_rx_mode(), but without starting the transmitter. */ | 357 | /* This is set_rx_mode(), but without starting the transmitter. */ |
@@ -876,8 +881,35 @@ static void tulip_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in | |||
876 | strcpy(info->bus_info, pci_name(np->pdev)); | 881 | strcpy(info->bus_info, pci_name(np->pdev)); |
877 | } | 882 | } |
878 | 883 | ||
884 | |||
885 | static int tulip_ethtool_set_wol(struct net_device *dev, | ||
886 | struct ethtool_wolinfo *wolinfo) | ||
887 | { | ||
888 | struct tulip_private *tp = netdev_priv(dev); | ||
889 | |||
890 | if (wolinfo->wolopts & (~tp->wolinfo.supported)) | ||
891 | return -EOPNOTSUPP; | ||
892 | |||
893 | tp->wolinfo.wolopts = wolinfo->wolopts; | ||
894 | device_set_wakeup_enable(&tp->pdev->dev, tp->wolinfo.wolopts); | ||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | static void tulip_ethtool_get_wol(struct net_device *dev, | ||
899 | struct ethtool_wolinfo *wolinfo) | ||
900 | { | ||
901 | struct tulip_private *tp = netdev_priv(dev); | ||
902 | |||
903 | wolinfo->supported = tp->wolinfo.supported; | ||
904 | wolinfo->wolopts = tp->wolinfo.wolopts; | ||
905 | return; | ||
906 | } | ||
907 | |||
908 | |||
879 | static const struct ethtool_ops ops = { | 909 | static const struct ethtool_ops ops = { |
880 | .get_drvinfo = tulip_get_drvinfo | 910 | .get_drvinfo = tulip_get_drvinfo, |
911 | .set_wol = tulip_ethtool_set_wol, | ||
912 | .get_wol = tulip_ethtool_get_wol, | ||
881 | }; | 913 | }; |
882 | 914 | ||
883 | /* Provide ioctl() calls to examine the MII xcvr state. */ | 915 | /* Provide ioctl() calls to examine the MII xcvr state. */ |
@@ -1093,8 +1125,8 @@ static void set_rx_mode(struct net_device *dev) | |||
1093 | iowrite32(3, ioaddr + CSR13); | 1125 | iowrite32(3, ioaddr + CSR13); |
1094 | iowrite32(mc_filter[1], ioaddr + CSR14); | 1126 | iowrite32(mc_filter[1], ioaddr + CSR14); |
1095 | } else if (tp->flags & COMET_MAC_ADDR) { | 1127 | } else if (tp->flags & COMET_MAC_ADDR) { |
1096 | iowrite32(mc_filter[0], ioaddr + 0xAC); | 1128 | iowrite32(mc_filter[0], ioaddr + CSR27); |
1097 | iowrite32(mc_filter[1], ioaddr + 0xB0); | 1129 | iowrite32(mc_filter[1], ioaddr + CSR28); |
1098 | } | 1130 | } |
1099 | tp->mc_filter[0] = mc_filter[0]; | 1131 | tp->mc_filter[0] = mc_filter[0]; |
1100 | tp->mc_filter[1] = mc_filter[1]; | 1132 | tp->mc_filter[1] = mc_filter[1]; |
@@ -1434,6 +1466,19 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, | |||
1434 | 1466 | ||
1435 | tp->chip_id = chip_idx; | 1467 | tp->chip_id = chip_idx; |
1436 | tp->flags = tulip_tbl[chip_idx].flags; | 1468 | tp->flags = tulip_tbl[chip_idx].flags; |
1469 | |||
1470 | tp->wolinfo.supported = 0; | ||
1471 | tp->wolinfo.wolopts = 0; | ||
1472 | /* COMET: Enable power management only for AN983B */ | ||
1473 | if (chip_idx == COMET ) { | ||
1474 | u32 sig; | ||
1475 | pci_read_config_dword (pdev, 0x80, &sig); | ||
1476 | if (sig == 0x09811317) { | ||
1477 | tp->flags |= COMET_PM; | ||
1478 | tp->wolinfo.supported = WAKE_PHY | WAKE_MAGIC; | ||
1479 | printk(KERN_INFO "tulip_init_one: Enabled WOL support for AN983B\n"); | ||
1480 | } | ||
1481 | } | ||
1437 | tp->pdev = pdev; | 1482 | tp->pdev = pdev; |
1438 | tp->base_addr = ioaddr; | 1483 | tp->base_addr = ioaddr; |
1439 | tp->revision = pdev->revision; | 1484 | tp->revision = pdev->revision; |
@@ -1766,11 +1811,43 @@ err_out_free_netdev: | |||
1766 | } | 1811 | } |
1767 | 1812 | ||
1768 | 1813 | ||
1814 | /* set the registers according to the given wolopts */ | ||
1815 | static void tulip_set_wolopts (struct pci_dev *pdev, u32 wolopts) | ||
1816 | { | ||
1817 | struct net_device *dev = pci_get_drvdata(pdev); | ||
1818 | struct tulip_private *tp = netdev_priv(dev); | ||
1819 | void __iomem *ioaddr = tp->base_addr; | ||
1820 | |||
1821 | if (tp->flags & COMET_PM) { | ||
1822 | |||
1823 | unsigned int tmp; | ||
1824 | |||
1825 | tmp = ioread32(ioaddr + CSR18); | ||
1826 | tmp &= ~(comet_csr18_pmes_sticky | comet_csr18_apm_mode | comet_csr18_d3a); | ||
1827 | tmp |= comet_csr18_pm_mode; | ||
1828 | iowrite32(tmp, ioaddr + CSR18); | ||
1829 | |||
1830 | /* Set the Wake-up Control/Status Register to the given WOL options*/ | ||
1831 | tmp = ioread32(ioaddr + CSR13); | ||
1832 | tmp &= ~(comet_csr13_linkoffe | comet_csr13_linkone | comet_csr13_wfre | comet_csr13_lsce | comet_csr13_mpre); | ||
1833 | if (wolopts & WAKE_MAGIC) | ||
1834 | tmp |= comet_csr13_mpre; | ||
1835 | if (wolopts & WAKE_PHY) | ||
1836 | tmp |= comet_csr13_linkoffe | comet_csr13_linkone | comet_csr13_lsce; | ||
1837 | /* Clear the event flags */ | ||
1838 | tmp |= comet_csr13_wfr | comet_csr13_mpr | comet_csr13_lsc; | ||
1839 | iowrite32(tmp, ioaddr + CSR13); | ||
1840 | } | ||
1841 | } | ||
1842 | |||
1769 | #ifdef CONFIG_PM | 1843 | #ifdef CONFIG_PM |
1770 | 1844 | ||
1845 | |||
1771 | static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) | 1846 | static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) |
1772 | { | 1847 | { |
1848 | pci_power_t pstate; | ||
1773 | struct net_device *dev = pci_get_drvdata(pdev); | 1849 | struct net_device *dev = pci_get_drvdata(pdev); |
1850 | struct tulip_private *tp = netdev_priv(dev); | ||
1774 | 1851 | ||
1775 | if (!dev) | 1852 | if (!dev) |
1776 | return -EINVAL; | 1853 | return -EINVAL; |
@@ -1786,7 +1863,16 @@ static int tulip_suspend (struct pci_dev *pdev, pm_message_t state) | |||
1786 | save_state: | 1863 | save_state: |
1787 | pci_save_state(pdev); | 1864 | pci_save_state(pdev); |
1788 | pci_disable_device(pdev); | 1865 | pci_disable_device(pdev); |
1789 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | 1866 | pstate = pci_choose_state(pdev, state); |
1867 | if (state.event == PM_EVENT_SUSPEND && pstate != PCI_D0) { | ||
1868 | int rc; | ||
1869 | |||
1870 | tulip_set_wolopts(pdev, tp->wolinfo.wolopts); | ||
1871 | rc = pci_enable_wake(pdev, pstate, tp->wolinfo.wolopts); | ||
1872 | if (rc) | ||
1873 | printk("tulip: pci_enable_wake failed (%d)\n", rc); | ||
1874 | } | ||
1875 | pci_set_power_state(pdev, pstate); | ||
1790 | 1876 | ||
1791 | return 0; | 1877 | return 0; |
1792 | } | 1878 | } |
@@ -1795,7 +1881,10 @@ save_state: | |||
1795 | static int tulip_resume(struct pci_dev *pdev) | 1881 | static int tulip_resume(struct pci_dev *pdev) |
1796 | { | 1882 | { |
1797 | struct net_device *dev = pci_get_drvdata(pdev); | 1883 | struct net_device *dev = pci_get_drvdata(pdev); |
1884 | struct tulip_private *tp = netdev_priv(dev); | ||
1885 | void __iomem *ioaddr = tp->base_addr; | ||
1798 | int retval; | 1886 | int retval; |
1887 | unsigned int tmp; | ||
1799 | 1888 | ||
1800 | if (!dev) | 1889 | if (!dev) |
1801 | return -EINVAL; | 1890 | return -EINVAL; |
@@ -1816,6 +1905,18 @@ static int tulip_resume(struct pci_dev *pdev) | |||
1816 | return retval; | 1905 | return retval; |
1817 | } | 1906 | } |
1818 | 1907 | ||
1908 | if (tp->flags & COMET_PM) { | ||
1909 | pci_enable_wake(pdev, PCI_D3hot, 0); | ||
1910 | pci_enable_wake(pdev, PCI_D3cold, 0); | ||
1911 | |||
1912 | /* Clear the PMES flag */ | ||
1913 | tmp = ioread32(ioaddr + CSR20); | ||
1914 | tmp |= comet_csr20_pmes; | ||
1915 | iowrite32(tmp, ioaddr + CSR20); | ||
1916 | |||
1917 | /* Disable all wake-up events */ | ||
1918 | tulip_set_wolopts(pdev, 0); | ||
1919 | } | ||
1819 | netif_device_attach(dev); | 1920 | netif_device_attach(dev); |
1820 | 1921 | ||
1821 | if (netif_running(dev)) | 1922 | if (netif_running(dev)) |