]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/sis900.c
Merge branch 'work-fixes'
[linux-2.6-omap-h63xx.git] / drivers / net / sis900.c
index 23b713c700b3beb2e3db8aad2a1321254afae46e..3d95fa20cd883e969cb3b138f8edb0a07a06c815 100644 (file)
@@ -1,6 +1,6 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
    Copyright 1999 Silicon Integrated System Corporation 
-   Revision:   1.08.08 Jan. 22 2005
+   Revision:   1.08.09 Sep. 19 2005
    
    Modified from the driver which is originally written by Donald Becker.
    
@@ -17,6 +17,7 @@
    SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
    preliminary Rev. 1.0 Jan. 18, 1998
 
+   Rev 1.08.09 Sep. 19 2005 Daniele Venzano add Wake on LAN support
    Rev 1.08.08 Jan. 22 2005 Daniele Venzano use netif_msg for debugging messages
    Rev 1.08.07 Nov.  2 2003 Daniele Venzano <webvenza@libero.it> add suspend/resume support
    Rev 1.08.06 Sep. 24 2002 Mufasa Yang bug fix for Tx timeout & add SiS963 support
@@ -76,7 +77,7 @@
 #include "sis900.h"
 
 #define SIS900_MODULE_NAME "sis900"
-#define SIS900_DRV_VERSION "v1.08.08 Jan. 22 2005"
+#define SIS900_DRV_VERSION "v1.08.09 Sep. 19 2005"
 
 static char version[] __devinitdata =
 KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n";
@@ -538,6 +539,11 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
                printk("%2.2x:", (u8)net_dev->dev_addr[i]);
        printk("%2.2x.\n", net_dev->dev_addr[i]);
 
+       /* Detect Wake on Lan support */
+       ret = inl(CFGPMC & PMESP);
+       if (netif_msg_probe(sis_priv) && (ret & PME_D3C) == 0)
+               printk(KERN_INFO "%s: Wake on LAN only available from suspend to RAM.", net_dev->name);
+
        return 0;
 
  err_unmap_rx:
@@ -1696,15 +1702,20 @@ static int sis900_rx(struct net_device *net_dev)
        long ioaddr = net_dev->base_addr;
        unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
        u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
+       int rx_work_limit;
 
        if (netif_msg_rx_status(sis_priv))
                printk(KERN_DEBUG "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
                       "status:0x%8.8x\n",
                       sis_priv->cur_rx, sis_priv->dirty_rx, rx_status);
+       rx_work_limit = sis_priv->dirty_rx + NUM_RX_DESC - sis_priv->cur_rx;
 
        while (rx_status & OWN) {
                unsigned int rx_size;
 
+               if (--rx_work_limit < 0)
+                       break;
+
                rx_size = (rx_status & DSIZE) - CRC_SIZE;
 
                if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
@@ -1732,9 +1743,11 @@ static int sis900_rx(struct net_device *net_dev)
                           we are working on NULL sk_buff :-( */
                        if (sis_priv->rx_skbuff[entry] == NULL) {
                                if (netif_msg_rx_err(sis_priv))
-                                       printk(KERN_INFO "%s: NULL pointer " 
-                                               "encountered in Rx ring, skipping\n",
-                                               net_dev->name);
+                                       printk(KERN_WARNING "%s: NULL pointer " 
+                                             "encountered in Rx ring\n"
+                                             "cur_rx:%4.4d, dirty_rx:%4.4d\n",
+                                             net_dev->name, sis_priv->cur_rx,
+                                             sis_priv->dirty_rx);
                                break;
                        }
 
@@ -1770,6 +1783,7 @@ static int sis900_rx(struct net_device *net_dev)
                                sis_priv->rx_ring[entry].cmdsts = 0;
                                sis_priv->rx_ring[entry].bufptr = 0;
                                sis_priv->stats.rx_dropped++;
+                               sis_priv->cur_rx++;
                                break;
                        }
                        skb->dev = net_dev;
@@ -1787,7 +1801,7 @@ static int sis900_rx(struct net_device *net_dev)
 
        /* refill the Rx buffer, what if the rate of refilling is slower
         * than consuming ?? */
-       for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) {
+       for (; sis_priv->cur_rx != sis_priv->dirty_rx; sis_priv->dirty_rx++) {
                struct sk_buff *skb;
 
                entry = sis_priv->dirty_rx % NUM_RX_DESC;
@@ -2007,6 +2021,67 @@ static int sis900_nway_reset(struct net_device *net_dev)
        return mii_nway_restart(&sis_priv->mii_info);
 }
 
+/**
+ *     sis900_set_wol - Set up Wake on Lan registers
+ *     @net_dev: the net device to probe
+ *     @wol: container for info passed to the driver
+ *
+ *     Process ethtool command "wol" to setup wake on lan features.
+ *     SiS900 supports sending WoL events if a correct packet is received,
+ *     but there is no simple way to filter them to only a subset (broadcast,
+ *     multicast, unicast or arp).
+ */
+static int sis900_set_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol)
+{
+       struct sis900_private *sis_priv = net_dev->priv;
+       long pmctrl_addr = net_dev->base_addr + pmctrl;
+       u32 cfgpmcsr = 0, pmctrl_bits = 0;
+
+       if (wol->wolopts == 0) {
+               pci_read_config_dword(sis_priv->pci_dev, CFGPMCSR, &cfgpmcsr);
+               cfgpmcsr |= ~PME_EN;
+               pci_write_config_dword(sis_priv->pci_dev, CFGPMCSR, cfgpmcsr);
+               outl(pmctrl_bits, pmctrl_addr);
+               if (netif_msg_wol(sis_priv))
+                       printk(KERN_DEBUG "%s: Wake on LAN disabled\n", net_dev->name);
+               return 0;
+       }
+
+       if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_UCAST | WAKE_MCAST
+                               | WAKE_BCAST | WAKE_ARP))
+               return -EINVAL;
+
+       if (wol->wolopts & WAKE_MAGIC)
+               pmctrl_bits |= MAGICPKT;
+       if (wol->wolopts & WAKE_PHY)
+               pmctrl_bits |= LINKON;
+       
+       outl(pmctrl_bits, pmctrl_addr);
+
+       pci_read_config_dword(sis_priv->pci_dev, CFGPMCSR, &cfgpmcsr);
+       cfgpmcsr |= PME_EN;
+       pci_write_config_dword(sis_priv->pci_dev, CFGPMCSR, cfgpmcsr);
+       if (netif_msg_wol(sis_priv))
+               printk(KERN_DEBUG "%s: Wake on LAN enabled\n", net_dev->name);
+
+       return 0;
+}
+
+static void sis900_get_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol)
+{
+       long pmctrl_addr = net_dev->base_addr + pmctrl;
+       u32 pmctrl_bits;
+
+       pmctrl_bits = inl(pmctrl_addr);
+       if (pmctrl_bits & MAGICPKT)
+               wol->wolopts |= WAKE_MAGIC;
+       if (pmctrl_bits & LINKON)
+               wol->wolopts |= WAKE_PHY;
+
+       wol->supported = (WAKE_PHY | WAKE_MAGIC);
+}
+
 static struct ethtool_ops sis900_ethtool_ops = {
        .get_drvinfo    = sis900_get_drvinfo,
        .get_msglevel   = sis900_get_msglevel,
@@ -2015,6 +2090,8 @@ static struct ethtool_ops sis900_ethtool_ops = {
        .get_settings   = sis900_get_settings,
        .set_settings   = sis900_set_settings,
        .nway_reset     = sis900_nway_reset,
+       .get_wol        = sis900_get_wol,
+       .set_wol        = sis900_set_wol
 };
 
 /**