netdev: bfin_mac: invalid data cache only once for each new rx skb buffer
[safe/jmp/linux-2.6] / drivers / net / dm9000.c
index 31b8bef..254b6f7 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <linux/slab.h>
 
 #include <asm/delay.h>
 #include <asm/irq.h>
@@ -100,6 +101,7 @@ typedef struct board_info {
 
        unsigned int    flags;
        unsigned int    in_suspend :1;
+       unsigned int    wake_supported :1;
        int             debug_level;
 
        enum dm9000_type type;
@@ -116,6 +118,8 @@ typedef struct board_info {
        struct resource *data_req;
        struct resource *irq_res;
 
+       int              irq_wake;
+
        struct mutex     addr_lock;     /* phy and eeprom access lock */
 
        struct delayed_work phy_poll;
@@ -125,6 +129,7 @@ typedef struct board_info {
 
        struct mii_if_info mii;
        u32             msg_enable;
+       u32             wake_state;
 
        int             rx_csum;
        int             can_csum;
@@ -568,6 +573,54 @@ static int dm9000_set_eeprom(struct net_device *dev,
        return 0;
 }
 
+static void dm9000_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+
+       memset(w, 0, sizeof(struct ethtool_wolinfo));
+
+       /* note, we could probably support wake-phy too */
+       w->supported = dm->wake_supported ? WAKE_MAGIC : 0;
+       w->wolopts = dm->wake_state;
+}
+
+static int dm9000_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       unsigned long flags;
+       u32 opts = w->wolopts;
+       u32 wcr = 0;
+
+       if (!dm->wake_supported)
+               return -EOPNOTSUPP;
+
+       if (opts & ~WAKE_MAGIC)
+               return -EINVAL;
+
+       if (opts & WAKE_MAGIC)
+               wcr |= WCR_MAGICEN;
+
+       mutex_lock(&dm->addr_lock);
+
+       spin_lock_irqsave(&dm->lock, flags);
+       iow(dm, DM9000_WCR, wcr);
+       spin_unlock_irqrestore(&dm->lock, flags);
+
+       mutex_unlock(&dm->addr_lock);
+
+       if (dm->wake_state != opts) {
+               /* change in wol state, update IRQ state */
+
+               if (!dm->wake_state)
+                       set_irq_wake(dm->irq_wake, 1);
+               else if (dm->wake_state & !opts)
+                       set_irq_wake(dm->irq_wake, 0);
+       }
+
+       dm->wake_state = opts;
+       return 0;
+}
+
 static const struct ethtool_ops dm9000_ethtool_ops = {
        .get_drvinfo            = dm9000_get_drvinfo,
        .get_settings           = dm9000_get_settings,
@@ -576,6 +629,8 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
        .set_msglevel           = dm9000_set_msglevel,
        .nway_reset             = dm9000_nway_reset,
        .get_link               = dm9000_get_link,
+       .get_wol                = dm9000_get_wol,
+       .set_wol                = dm9000_set_wol,
        .get_eeprom_len         = dm9000_get_eeprom_len,
        .get_eeprom             = dm9000_get_eeprom,
        .set_eeprom             = dm9000_set_eeprom,
@@ -670,8 +725,7 @@ static void
 dm9000_hash_table(struct net_device *dev)
 {
        board_info_t *db = netdev_priv(dev);
-       struct dev_mc_list *mcptr = dev->mc_list;
-       int mc_cnt = dev->mc_count;
+       struct netdev_hw_addr *ha;
        int i, oft;
        u32 hash_val;
        u16 hash_table[4];
@@ -699,8 +753,8 @@ dm9000_hash_table(struct net_device *dev)
                rcr |= RCR_ALL;
 
        /* the multicast address in Hash Table : 64 bits */
-       for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
-               hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;
+       netdev_for_each_mc_addr(ha, dev) {
+               hash_val = ether_crc_le(6, ha->addr) & 0x3f;
                hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
        }
 
@@ -715,13 +769,14 @@ dm9000_hash_table(struct net_device *dev)
 }
 
 /*
- * Initilize dm9000 board
+ * Initialize dm9000 board
  */
 static void
 dm9000_init_dm9000(struct net_device *dev)
 {
        board_info_t *db = netdev_priv(dev);
        unsigned int imr;
+       unsigned int ncr;
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
 
@@ -736,8 +791,15 @@ dm9000_init_dm9000(struct net_device *dev)
        iow(db, DM9000_GPCR, GPCR_GEP_CNTL);    /* Let GPIO0 output */
        iow(db, DM9000_GPR, 0); /* Enable PHY */
 
-       if (db->flags & DM9000_PLATF_EXT_PHY)
-               iow(db, DM9000_NCR, NCR_EXT_PHY);
+       ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
+
+       /* if wol is needed, then always set NCR_WAKEEN otherwise we end
+        * up dumping the wake events if we disable this. There is already
+        * a wake-mask in DM9000_WCR */
+       if (db->wake_supported)
+               ncr |= NCR_WAKEEN;
+
+       iow(db, DM9000_NCR, ncr);
 
        /* Program operating register */
        iow(db, DM9000_TCR, 0);         /* TX Polling clear */
@@ -763,7 +825,7 @@ dm9000_init_dm9000(struct net_device *dev)
        /* Init Driver variable */
        db->tx_pkt_cnt = 0;
        db->queue_pkt_len = 0;
-       dev->trans_start = 0;
+       dev->trans_start = jiffies;
 }
 
 /* Our watchdog timed out. Called by the networking layer */
@@ -781,7 +843,7 @@ static void dm9000_timeout(struct net_device *dev)
        dm9000_reset(db);
        dm9000_init_dm9000(dev);
        /* We can accept TX packets again */
-       dev->trans_start = jiffies;
+       dev->trans_start = jiffies; /* prevent tx timeout */
        netif_wake_queue(dev);
 
        /* Restore previous register address */
@@ -962,8 +1024,8 @@ dm9000_rx(struct net_device *dev)
                }
 
                /* Move data from DM9000 */
-               if (GoodPacket
-                   && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
+               if (GoodPacket &&
+                   ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
                        skb_reserve(skb, 2);
                        rdptr = (u8 *) skb_put(skb, RxLen - 4);
 
@@ -1045,6 +1107,41 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       board_info_t *db = netdev_priv(dev);
+       unsigned long flags;
+       unsigned nsr, wcr;
+
+       spin_lock_irqsave(&db->lock, flags);
+
+       nsr = ior(db, DM9000_NSR);
+       wcr = ior(db, DM9000_WCR);
+
+       dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr);
+
+       if (nsr & NSR_WAKEST) {
+               /* clear, so we can avoid */
+               iow(db, DM9000_NSR, NSR_WAKEST);
+
+               if (wcr & WCR_LINKST)
+                       dev_info(db->dev, "wake by link status change\n");
+               if (wcr & WCR_SAMPLEST)
+                       dev_info(db->dev, "wake by sample packet\n");
+               if (wcr & WCR_MAGICST )
+                       dev_info(db->dev, "wake by magic packet\n");
+               if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST)))
+                       dev_err(db->dev, "wake signalled with no reason? "
+                               "NSR=0x%02x, WSR=0x%02x\n", nsr, wcr);
+
+       }
+
+       spin_unlock_irqrestore(&db->lock, flags);
+
+       return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE;
+}
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /*
  *Used by netconsole
@@ -1078,7 +1175,7 @@ dm9000_open(struct net_device *dev)
 
        irqflags |= IRQF_SHARED;
 
-       if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))
+       if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
                return -EAGAIN;
 
        /* Initialize DM9000 board */
@@ -1299,6 +1396,29 @@ dm9000_probe(struct platform_device *pdev)
                goto out;
        }
 
+       db->irq_wake = platform_get_irq(pdev, 1);
+       if (db->irq_wake >= 0) {
+               dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);
+
+               ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
+                                 IRQF_SHARED, dev_name(db->dev), ndev);
+               if (ret) {
+                       dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
+               } else {
+
+                       /* test to see if irq is really wakeup capable */
+                       ret = set_irq_wake(db->irq_wake, 1);
+                       if (ret) {
+                               dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
+                                       db->irq_wake, ret);
+                               ret = 0;
+                       } else {
+                               set_irq_wake(db->irq_wake, 0);
+                               db->wake_supported = 1;
+                       }
+               }
+       }
+
        iosize = resource_size(db->addr_res);
        db->addr_req = request_mem_region(db->addr_res->start, iosize,
                                          pdev->name);
@@ -1490,10 +1610,14 @@ dm9000_drv_suspend(struct device *dev)
                db = netdev_priv(ndev);
                db->in_suspend = 1;
 
-               if (netif_running(ndev)) {
-                       netif_device_detach(ndev);
+               if (!netif_running(ndev))
+                       return 0;
+
+               netif_device_detach(ndev);
+
+               /* only shutdown if not using WoL */
+               if (!db->wake_state)
                        dm9000_shutdown(ndev);
-               }
        }
        return 0;
 }
@@ -1506,10 +1630,13 @@ dm9000_drv_resume(struct device *dev)
        board_info_t *db = netdev_priv(ndev);
 
        if (ndev) {
-
                if (netif_running(ndev)) {
-                       dm9000_reset(db);
-                       dm9000_init_dm9000(ndev);
+                       /* reset if we were not in wake mode to ensure if
+                        * the device was powered off it is in a known state */
+                       if (!db->wake_state) {
+                               dm9000_reset(db);
+                               dm9000_init_dm9000(ndev);
+                       }
 
                        netif_device_attach(ndev);
                }
@@ -1519,7 +1646,7 @@ dm9000_drv_resume(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops dm9000_drv_pm_ops = {
+static const struct dev_pm_ops dm9000_drv_pm_ops = {
        .suspend        = dm9000_drv_suspend,
        .resume         = dm9000_drv_resume,
 };