summaryrefslogtreecommitdiffstats
path: root/drivers/net/tulip/dmfe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tulip/dmfe.c')
-rw-r--r--drivers/net/tulip/dmfe.c130
1 files changed, 119 insertions, 11 deletions
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c
index 9aeac76184f3..4ed67ff0e81e 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/tulip/dmfe.c
@@ -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.
@@ -125,6 +122,11 @@
#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");