- patches.apparmor/remove_suid_new_case_in_2.6.22.diff: Merge fix.
[linux-flexiantxendom0-3.2.10.git] / drivers / net / tulip / dmfe.c
index 9aeac76..4ed67ff 100644 (file)
@@ -55,9 +55,6 @@
 
     TODO
 
-    Implement pci_driver::suspend() and pci_driver::resume()
-    power management methods.
-
     Check on 64 bit boxes.
     Check and fix on big endian boxes.
 
 #define DM9801_NOISE_FLOOR 8
 #define DM9802_NOISE_FLOOR 5
 
+#define DMFE_WOL_LINKCHANGE    0x20000000
+#define DMFE_WOL_SAMPLEPACKET  0x10000000
+#define DMFE_WOL_MAGICPACKET   0x08000000
+
+
 #define DMFE_10MHF      0
 #define DMFE_100MHF     1
 #define DMFE_10MFD      4
@@ -251,6 +253,7 @@ struct dmfe_board_info {
        u8 wait_reset;                  /* Hardware failed, need to reset */
        u8 dm910x_chk_mode;             /* Operating mode check */
        u8 first_in_callback;           /* Flag to record state */
+       u8 wol_mode;                    /* user WOL settings */
        struct timer_list timer;
 
        /* System defined statistic counter */
@@ -431,6 +434,7 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev,
        db->chip_id = ent->driver_data;
        db->ioaddr = pci_resource_start(pdev, 0);
        db->chip_revision = dev_rev;
+       db->wol_mode = 0;
 
        db->pdev = pdev;
 
@@ -682,7 +686,7 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev)
 
        /* transmit this packet */
        txptr = db->tx_insert_ptr;
-       memcpy(txptr->tx_buf_ptr, skb->data, skb->len);
+       skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len);
        txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
 
        /* Point to next transmit free descriptor */
@@ -988,14 +992,14 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db)
 
                                                skb = newskb;
                                                /* size less than COPY_SIZE, allocate a rxlen SKB */
-                                               skb->dev = dev;
                                                skb_reserve(skb, 2); /* 16byte align */
-                                               memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->data, rxlen);
+                                               skb_copy_from_linear_data(rxptr->rx_skb_ptr,
+                                                         skb_put(skb, rxlen),
+                                                                         rxlen);
                                                dmfe_reuse_skb(db, rxptr->rx_skb_ptr);
-                                       } else {
-                                               skb->dev = dev;
+                                       } else
                                                skb_put(skb, rxlen);
-                                       }
+
                                        skb->protocol = eth_type_trans(skb, dev);
                                        netif_rx(skb);
                                        dev->last_rx = jiffies;
@@ -1065,7 +1069,11 @@ static void dmfe_set_filter_mode(struct DEVICE * dev)
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
-static void netdev_get_drvinfo(struct net_device *dev,
+/*
+ *     Ethtool interace
+ */
+
+static void dmfe_ethtool_get_drvinfo(struct net_device *dev,
                               struct ethtool_drvinfo *info)
 {
        struct dmfe_board_info *np = netdev_priv(dev);
@@ -1079,9 +1087,35 @@ static void netdev_get_drvinfo(struct net_device *dev,
                        dev->base_addr, dev->irq);
 }
 
+static int dmfe_ethtool_set_wol(struct net_device *dev,
+                               struct ethtool_wolinfo *wolinfo)
+{
+       struct dmfe_board_info *db = netdev_priv(dev);
+
+       if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST |
+                               WAKE_ARP | WAKE_MAGICSECURE))
+                  return -EOPNOTSUPP;
+
+       db->wol_mode = wolinfo->wolopts;
+       return 0;
+}
+
+static void dmfe_ethtool_get_wol(struct net_device *dev,
+                                struct ethtool_wolinfo *wolinfo)
+{
+       struct dmfe_board_info *db = netdev_priv(dev);
+
+       wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+       wolinfo->wolopts = db->wol_mode;
+       return;
+}
+
+
 static const struct ethtool_ops netdev_ethtool_ops = {
-       .get_drvinfo            = netdev_get_drvinfo,
+       .get_drvinfo            = dmfe_ethtool_get_drvinfo,
        .get_link               = ethtool_op_get_link,
+       .set_wol                = dmfe_ethtool_set_wol,
+       .get_wol                = dmfe_ethtool_get_wol,
 };
 
 /*
@@ -2050,11 +2084,85 @@ static struct pci_device_id dmfe_pci_tbl[] = {
 MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl);
 
 
+#ifdef CONFIG_PM
+static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct dmfe_board_info *db = netdev_priv(dev);
+       u32 tmp;
+
+       /* Disable upper layer interface */
+       netif_device_detach(dev);
+
+       /* Disable Tx/Rx */
+       db->cr6_data &= ~(CR6_RXSC | CR6_TXSC);
+       update_cr6(db->cr6_data, dev->base_addr);
+
+       /* Disable Interrupt */
+       outl(0, dev->base_addr + DCR7);
+       outl(inl (dev->base_addr + DCR5), dev->base_addr + DCR5);
+
+       /* Fre RX buffers */
+       dmfe_free_rxbuffer(db);
+
+       /* Enable WOL */
+       pci_read_config_dword(pci_dev, 0x40, &tmp);
+       tmp &= ~(DMFE_WOL_LINKCHANGE|DMFE_WOL_MAGICPACKET);
+
+       if (db->wol_mode & WAKE_PHY)
+               tmp |= DMFE_WOL_LINKCHANGE;
+       if (db->wol_mode & WAKE_MAGIC)
+               tmp |= DMFE_WOL_MAGICPACKET;
+
+       pci_write_config_dword(pci_dev, 0x40, tmp);
+
+       pci_enable_wake(pci_dev, PCI_D3hot, 1);
+       pci_enable_wake(pci_dev, PCI_D3cold, 1);
+
+       /* Power down device*/
+       pci_set_power_state(pci_dev, pci_choose_state (pci_dev,state));
+       pci_save_state(pci_dev);
+
+       return 0;
+}
+
+static int dmfe_resume(struct pci_dev *pci_dev)
+{
+       struct net_device *dev = pci_get_drvdata(pci_dev);
+       u32 tmp;
+
+       pci_restore_state(pci_dev);
+       pci_set_power_state(pci_dev, PCI_D0);
+
+       /* Re-initilize DM910X board */
+       dmfe_init_dm910x(dev);
+
+       /* Disable WOL */
+       pci_read_config_dword(pci_dev, 0x40, &tmp);
+
+       tmp &= ~(DMFE_WOL_LINKCHANGE | DMFE_WOL_MAGICPACKET);
+       pci_write_config_dword(pci_dev, 0x40, tmp);
+
+       pci_enable_wake(pci_dev, PCI_D3hot, 0);
+       pci_enable_wake(pci_dev, PCI_D3cold, 0);
+
+       /* Restart upper layer interface */
+       netif_device_attach(dev);
+
+       return 0;
+}
+#else
+#define dmfe_suspend NULL
+#define dmfe_resume NULL
+#endif
+
 static struct pci_driver dmfe_driver = {
        .name           = "dmfe",
        .id_table       = dmfe_pci_tbl,
        .probe          = dmfe_init_one,
        .remove         = __devexit_p(dmfe_remove_one),
+       .suspend        = dmfe_suspend,
+       .resume         = dmfe_resume
 };
 
 MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw");