Merge branch 'bkl/fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic...
[safe/jmp/linux-2.6] / drivers / net / dm9000.c
index 5ad2ec5..abcc838 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>
 #define CARDNAME       "dm9000"
 #define DRV_VERSION    "1.31"
 
-#ifdef CONFIG_BLACKFIN
-#define readsb insb
-#define readsw insw
-#define readsl insl
-#define writesb        outsb
-#define writesw        outsw
-#define writesl        outsl
-#define DEFAULT_TRIGGER IRQF_TRIGGER_HIGH
-#else
-#define DEFAULT_TRIGGER (0)
-#endif
-
 /*
  * Transmit timeout, default 5 seconds.
  */
@@ -104,6 +93,7 @@ typedef struct board_info {
        u16             tx_pkt_cnt;
        u16             queue_pkt_len;
        u16             queue_start_addr;
+       u16             queue_ip_summed;
        u16             dbug_cnt;
        u8              io_mode;                /* 0:word, 2:byte */
        u8              phy_addr;
@@ -111,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;
@@ -127,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;
@@ -136,6 +129,11 @@ typedef struct board_info {
 
        struct mii_if_info mii;
        u32             msg_enable;
+       u32             wake_state;
+
+       int             rx_csum;
+       int             can_csum;
+       int             ip_summed;
 } board_info_t;
 
 /* debug code */
@@ -149,7 +147,7 @@ typedef struct board_info {
 
 static inline board_info_t *to_dm9000_board(struct net_device *dev)
 {
-       return dev->priv;
+       return netdev_priv(dev);
 }
 
 /* DM9000 network board routine ---------------------------- */
@@ -343,6 +341,8 @@ static int dm9000_wait_eeprom(board_info_t *db)
                if ((status & EPCR_ERRE) == 0)
                        break;
 
+               msleep(1);
+
                if (timeout-- < 0) {
                        dev_dbg(db->dev, "timeout waiting EEPROM\n");
                        break;
@@ -470,6 +470,49 @@ static int dm9000_nway_reset(struct net_device *dev)
        return mii_nway_restart(&dm->mii);
 }
 
+static uint32_t dm9000_get_rx_csum(struct net_device *dev)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       return dm->rx_csum;
+}
+
+static int dm9000_set_rx_csum_unlocked(struct net_device *dev, uint32_t data)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+
+       if (dm->can_csum) {
+               dm->rx_csum = data;
+               iow(dm, DM9000_RCSR, dm->rx_csum ? RCSR_CSUM : 0);
+
+               return 0;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int dm9000_set_rx_csum(struct net_device *dev, uint32_t data)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&dm->lock, flags);
+       ret = dm9000_set_rx_csum_unlocked(dev, data);
+       spin_unlock_irqrestore(&dm->lock, flags);
+
+       return ret;
+}
+
+static int dm9000_set_tx_csum(struct net_device *dev, uint32_t data)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+       int ret = -EOPNOTSUPP;
+
+       if (dm->can_csum)
+               ret = ethtool_op_set_tx_csum(dev, data);
+       return ret;
+}
+
 static u32 dm9000_get_link(struct net_device *dev)
 {
        board_info_t *dm = to_dm9000_board(dev);
@@ -539,6 +582,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,
@@ -547,9 +638,15 @@ 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,
+       .get_rx_csum            = dm9000_get_rx_csum,
+       .set_rx_csum            = dm9000_set_rx_csum,
+       .get_tx_csum            = ethtool_op_get_tx_csum,
+       .set_tx_csum            = dm9000_set_tx_csum,
 };
 
 static void dm9000_show_carrier(board_info_t *db,
@@ -569,7 +666,7 @@ static void dm9000_show_carrier(board_info_t *db,
 static void
 dm9000_poll_work(struct work_struct *w)
 {
-       struct delayed_work *dw = container_of(w, struct delayed_work, work);
+       struct delayed_work *dw = to_delayed_work(w);
        board_info_t *db = container_of(dw, board_info_t, phy_poll);
        struct net_device *ndev = db->ndev;
 
@@ -634,21 +731,17 @@ static unsigned char dm9000_type_to_char(enum dm9000_type type)
  *  Set DM9000 multicast address
  */
 static void
-dm9000_hash_table(struct net_device *dev)
+dm9000_hash_table_unlocked(struct net_device *dev)
 {
-       board_info_t *db = (board_info_t *) dev->priv;
-       struct dev_mc_list *mcptr = dev->mc_list;
-       int mc_cnt = dev->mc_count;
+       board_info_t *db = netdev_priv(dev);
+       struct netdev_hw_addr *ha;
        int i, oft;
        u32 hash_val;
        u16 hash_table[4];
        u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
-       unsigned long flags;
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
 
-       spin_lock_irqsave(&db->lock, flags);
-
        for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
                iow(db, oft, dev->dev_addr[i]);
 
@@ -666,8 +759,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);
        }
 
@@ -678,30 +771,51 @@ dm9000_hash_table(struct net_device *dev)
        }
 
        iow(db, DM9000_RCR, rcr);
+}
+
+static void
+dm9000_hash_table(struct net_device *dev)
+{
+       board_info_t *db = netdev_priv(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&db->lock, flags);
+       dm9000_hash_table_unlocked(dev);
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
 /*
- * Initilize dm9000 board
+ * Initialize dm9000 board
  */
 static void
 dm9000_init_dm9000(struct net_device *dev)
 {
-       board_info_t *db = dev->priv;
+       board_info_t *db = netdev_priv(dev);
        unsigned int imr;
+       unsigned int ncr;
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
 
        /* I/O mode */
        db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
 
+       /* Checksum mode */
+       dm9000_set_rx_csum_unlocked(dev, db->rx_csum);
+
        /* GPIO0 on pre-activate PHY */
        iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */
        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 */
@@ -713,7 +827,7 @@ dm9000_init_dm9000(struct net_device *dev)
        iow(db, DM9000_ISR, ISR_CLR_STATUS); /* Clear interrupt status */
 
        /* Set address filter table */
-       dm9000_hash_table(dev);
+       dm9000_hash_table_unlocked(dev);
 
        imr = IMR_PAR | IMR_PTM | IMR_PRM;
        if (db->type != TYPE_DM9000E)
@@ -727,13 +841,13 @@ 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 */
 static void dm9000_timeout(struct net_device *dev)
 {
-       board_info_t *db = (board_info_t *) dev->priv;
+       board_info_t *db = netdev_priv(dev);
        u8 reg_save;
        unsigned long flags;
 
@@ -745,7 +859,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 */
@@ -753,6 +867,29 @@ static void dm9000_timeout(struct net_device *dev)
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
+static void dm9000_send_packet(struct net_device *dev,
+                              int ip_summed,
+                              u16 pkt_len)
+{
+       board_info_t *dm = to_dm9000_board(dev);
+
+       /* The DM9000 is not smart enough to leave fragmented packets alone. */
+       if (dm->ip_summed != ip_summed) {
+               if (ip_summed == CHECKSUM_NONE)
+                       iow(dm, DM9000_TCCR, 0);
+               else
+                       iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP);
+               dm->ip_summed = ip_summed;
+       }
+
+       /* Set TX length to DM9000 */
+       iow(dm, DM9000_TXPLL, pkt_len);
+       iow(dm, DM9000_TXPLH, pkt_len >> 8);
+
+       /* Issue TX polling command */
+       iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
+}
+
 /*
  *  Hardware start transmission.
  *  Send a packet to media from the upper layer.
@@ -761,12 +898,12 @@ static int
 dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned long flags;
-       board_info_t *db = dev->priv;
+       board_info_t *db = netdev_priv(dev);
 
        dm9000_dbg(db, 3, "%s:\n", __func__);
 
        if (db->tx_pkt_cnt > 1)
-               return 1;
+               return NETDEV_TX_BUSY;
 
        spin_lock_irqsave(&db->lock, flags);
 
@@ -779,17 +916,11 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
        db->tx_pkt_cnt++;
        /* TX control: First packet immediately send, second packet queue */
        if (db->tx_pkt_cnt == 1) {
-               /* Set TX length to DM9000 */
-               iow(db, DM9000_TXPLL, skb->len);
-               iow(db, DM9000_TXPLH, skb->len >> 8);
-
-               /* Issue TX polling command */
-               iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
-
-               dev->trans_start = jiffies;     /* save the time stamp */
+               dm9000_send_packet(dev, skb->ip_summed, skb->len);
        } else {
                /* Second packet */
                db->queue_pkt_len = skb->len;
+               db->queue_ip_summed = skb->ip_summed;
                netif_stop_queue(dev);
        }
 
@@ -798,7 +929,7 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* free this SKB */
        dev_kfree_skb(skb);
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 /*
@@ -819,12 +950,9 @@ static void dm9000_tx_done(struct net_device *dev, board_info_t *db)
                        dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
 
                /* Queue packet check & send */
-               if (db->tx_pkt_cnt > 0) {
-                       iow(db, DM9000_TXPLL, db->queue_pkt_len);
-                       iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8);
-                       iow(db, DM9000_TCR, TCR_TXREQ);
-                       dev->trans_start = jiffies;
-               }
+               if (db->tx_pkt_cnt > 0)
+                       dm9000_send_packet(dev, db->queue_ip_summed,
+                                          db->queue_pkt_len);
                netif_wake_queue(dev);
        }
 }
@@ -841,7 +969,7 @@ struct dm9000_rxhdr {
 static void
 dm9000_rx(struct net_device *dev)
 {
-       board_info_t *db = (board_info_t *) dev->priv;
+       board_info_t *db = netdev_priv(dev);
        struct dm9000_rxhdr rxhdr;
        struct sk_buff *skb;
        u8 rxbyte, *rdptr;
@@ -856,14 +984,14 @@ dm9000_rx(struct net_device *dev)
                rxbyte = readb(db->io_data);
 
                /* Status check: this byte must be 0 or 1 */
-               if (rxbyte > DM9000_PKT_RDY) {
+               if (rxbyte & DM9000_PKT_ERR) {
                        dev_warn(db->dev, "status check fail: %d\n", rxbyte);
                        iow(db, DM9000_RCR, 0x00);      /* Stop Device */
                        iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */
                        return;
                }
 
-               if (rxbyte != DM9000_PKT_RDY)
+               if (!(rxbyte & DM9000_PKT_RDY))
                        return;
 
                /* A packet ready now  & Get status/length */
@@ -889,19 +1017,22 @@ dm9000_rx(struct net_device *dev)
                        dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
                }
 
-               if (rxhdr.RxStatus & 0xbf) {
+               /* rxhdr.RxStatus is identical to RSR register. */
+               if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
+                                     RSR_PLE | RSR_RWTO |
+                                     RSR_LCS | RSR_RF)) {
                        GoodPacket = false;
-                       if (rxhdr.RxStatus & 0x01) {
+                       if (rxhdr.RxStatus & RSR_FOE) {
                                if (netif_msg_rx_err(db))
                                        dev_dbg(db->dev, "fifo error\n");
                                dev->stats.rx_fifo_errors++;
                        }
-                       if (rxhdr.RxStatus & 0x02) {
+                       if (rxhdr.RxStatus & RSR_CE) {
                                if (netif_msg_rx_err(db))
                                        dev_dbg(db->dev, "crc error\n");
                                dev->stats.rx_crc_errors++;
                        }
-                       if (rxhdr.RxStatus & 0x80) {
+                       if (rxhdr.RxStatus & RSR_RF) {
                                if (netif_msg_rx_err(db))
                                        dev_dbg(db->dev, "length error\n");
                                dev->stats.rx_length_errors++;
@@ -909,8 +1040,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);
 
@@ -921,6 +1052,12 @@ dm9000_rx(struct net_device *dev)
 
                        /* Pass to upper layer */
                        skb->protocol = eth_type_trans(skb, dev);
+                       if (db->rx_csum) {
+                               if ((((rxbyte & 0x1c) << 3) & rxbyte) == 0)
+                                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                               else
+                                       skb->ip_summed = CHECKSUM_NONE;
+                       }
                        netif_rx(skb);
                        dev->stats.rx_packets++;
 
@@ -929,21 +1066,23 @@ dm9000_rx(struct net_device *dev)
 
                        (db->dumpblk)(db->io_data, RxLen);
                }
-       } while (rxbyte == DM9000_PKT_RDY);
+       } while (rxbyte & DM9000_PKT_RDY);
 }
 
 static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
-       board_info_t *db = dev->priv;
+       board_info_t *db = netdev_priv(dev);
        int int_status;
+       unsigned long flags;
        u8 reg_save;
 
        dm9000_dbg(db, 3, "entering %s\n", __func__);
 
        /* A real interrupt coming */
 
-       spin_lock(&db->lock);
+       /* holders of db->lock must always block IRQs */
+       spin_lock_irqsave(&db->lock, flags);
 
        /* Save previous register address */
        reg_save = readb(db->io_addr);
@@ -979,11 +1118,46 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
        /* Restore previous register address */
        writeb(reg_save, db->io_addr);
 
-       spin_unlock(&db->lock);
+       spin_unlock_irqrestore(&db->lock, flags);
 
        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
@@ -1003,7 +1177,7 @@ static void dm9000_poll_controller(struct net_device *dev)
 static int
 dm9000_open(struct net_device *dev)
 {
-       board_info_t *db = dev->priv;
+       board_info_t *db = netdev_priv(dev);
        unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
 
        if (netif_msg_ifup(db))
@@ -1012,14 +1186,12 @@ dm9000_open(struct net_device *dev)
        /* If there is no IRQ type specified, default to something that
         * may work, and tell the user that this is a problem */
 
-       if (irqflags == IRQF_TRIGGER_NONE) {
+       if (irqflags == IRQF_TRIGGER_NONE)
                dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
-               irqflags = DEFAULT_TRIGGER;
-       }
-       
+
        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 */
@@ -1055,7 +1227,7 @@ static void dm9000_msleep(board_info_t *db, unsigned int ms)
 static int
 dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
 {
-       board_info_t *db = (board_info_t *) dev->priv;
+       board_info_t *db = netdev_priv(dev);
        unsigned long flags;
        unsigned int reg_save;
        int ret;
@@ -1070,7 +1242,7 @@ dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
        /* Fill the phyxcer register into REG_0C */
        iow(db, DM9000_EPAR, DM9000_PHY | reg);
 
-       iow(db, DM9000_EPCR, 0xc);      /* Issue phyxcer read command */
+       iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS);   /* Issue phyxcer read command */
 
        writeb(reg_save, db->io_addr);
        spin_unlock_irqrestore(&db->lock,flags);
@@ -1102,7 +1274,7 @@ static void
 dm9000_phy_write(struct net_device *dev,
                 int phyaddr_unused, int reg, int value)
 {
-       board_info_t *db = (board_info_t *) dev->priv;
+       board_info_t *db = netdev_priv(dev);
        unsigned long flags;
        unsigned long reg_save;
 
@@ -1121,7 +1293,7 @@ dm9000_phy_write(struct net_device *dev,
        iow(db, DM9000_EPDRL, value);
        iow(db, DM9000_EPDRH, value >> 8);
 
-       iow(db, DM9000_EPCR, 0xa);      /* Issue phyxcer write command */
+       iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW);   /* Issue phyxcer write command */
 
        writeb(reg_save, db->io_addr);
        spin_unlock_irqrestore(&db->lock, flags);
@@ -1143,7 +1315,7 @@ dm9000_phy_write(struct net_device *dev,
 static void
 dm9000_shutdown(struct net_device *dev)
 {
-       board_info_t *db = dev->priv;
+       board_info_t *db = netdev_priv(dev);
 
        /* RESET device */
        dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
@@ -1159,7 +1331,7 @@ dm9000_shutdown(struct net_device *dev)
 static int
 dm9000_stop(struct net_device *ndev)
 {
-       board_info_t *db = ndev->priv;
+       board_info_t *db = netdev_priv(ndev);
 
        if (netif_msg_ifdown(db))
                dev_dbg(db->dev, "shutting down %s\n", ndev->name);
@@ -1177,7 +1349,20 @@ dm9000_stop(struct net_device *ndev)
        return 0;
 }
 
-#define res_size(_r) (((_r)->end - (_r)->start) + 1)
+static const struct net_device_ops dm9000_netdev_ops = {
+       .ndo_open               = dm9000_open,
+       .ndo_stop               = dm9000_stop,
+       .ndo_start_xmit         = dm9000_start_xmit,
+       .ndo_tx_timeout         = dm9000_timeout,
+       .ndo_set_multicast_list = dm9000_hash_table,
+       .ndo_do_ioctl           = dm9000_ioctl,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = dm9000_poll_controller,
+#endif
+};
 
 /*
  * Search DM9000 board, allocate space and register it
@@ -1206,8 +1391,7 @@ dm9000_probe(struct platform_device *pdev)
        dev_dbg(&pdev->dev, "dm9000_probe()\n");
 
        /* setup board info structure */
-       db = ndev->priv;
-       memset(db, 0, sizeof(*db));
+       db = netdev_priv(ndev);
 
        db->dev = &pdev->dev;
        db->ndev = ndev;
@@ -1228,7 +1412,30 @@ dm9000_probe(struct platform_device *pdev)
                goto out;
        }
 
-       iosize = res_size(db->addr_res);
+       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);
 
@@ -1246,7 +1453,7 @@ dm9000_probe(struct platform_device *pdev)
                goto out;
        }
 
-       iosize = res_size(db->data_res);
+       iosize = resource_size(db->data_res);
        db->data_req = request_mem_region(db->data_res->start, iosize,
                                          pdev->name);
 
@@ -1341,23 +1548,21 @@ dm9000_probe(struct platform_device *pdev)
                db->type = TYPE_DM9000E;
        }
 
+       /* dm9000a/b are capable of hardware checksum offload */
+       if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
+               db->can_csum = 1;
+               db->rx_csum = 1;
+               ndev->features |= NETIF_F_IP_CSUM;
+       }
+
        /* from this point we assume that we have found a DM9000 */
 
        /* driver system function */
        ether_setup(ndev);
 
-       ndev->open               = &dm9000_open;
-       ndev->hard_start_xmit    = &dm9000_start_xmit;
-       ndev->tx_timeout         = &dm9000_timeout;
-       ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
-       ndev->stop               = &dm9000_stop;
-       ndev->set_multicast_list = &dm9000_hash_table;
-       ndev->ethtool_ops        = &dm9000_ethtool_ops;
-       ndev->do_ioctl           = &dm9000_ioctl;
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       ndev->poll_controller    = &dm9000_poll_controller;
-#endif
+       ndev->netdev_ops        = &dm9000_netdev_ops;
+       ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
+       ndev->ethtool_ops       = &dm9000_ethtool_ops;
 
        db->msg_enable       = NETIF_MSG_LINK;
        db->mii.phy_id_mask  = 0x1f;
@@ -1374,6 +1579,11 @@ dm9000_probe(struct platform_device *pdev)
        for (i = 0; i < 6; i += 2)
                dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
 
+       if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
+               mac_src = "platform data";
+               memcpy(ndev->dev_addr, pdata->dev_addr, 6);
+       }
+
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                /* try reading from mac */
                
@@ -1389,13 +1599,11 @@ dm9000_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, ndev);
        ret = register_netdev(ndev);
 
-       if (ret == 0) {
-               DECLARE_MAC_BUF(mac);
-               printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %s (%s)\n",
+       if (ret == 0)
+               printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
                       ndev->name, dm9000_type_to_char(db->type),
                       db->io_addr, db->io_data, ndev->irq,
-                      print_mac(mac, ndev->dev_addr), mac_src);
-       }
+                      ndev->dev_addr, mac_src);
        return 0;
 
 out:
@@ -1408,34 +1616,43 @@ out:
 }
 
 static int
-dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)
+dm9000_drv_suspend(struct device *dev)
 {
-       struct net_device *ndev = platform_get_drvdata(dev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
        board_info_t *db;
 
        if (ndev) {
-               db = (board_info_t *) ndev->priv;
+               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;
 }
 
 static int
-dm9000_drv_resume(struct platform_device *dev)
+dm9000_drv_resume(struct device *dev)
 {
-       struct net_device *ndev = platform_get_drvdata(dev);
-       board_info_t *db = (board_info_t *) ndev->priv;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       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);
                }
@@ -1445,6 +1662,11 @@ dm9000_drv_resume(struct platform_device *dev)
        return 0;
 }
 
+static const struct dev_pm_ops dm9000_drv_pm_ops = {
+       .suspend        = dm9000_drv_suspend,
+       .resume         = dm9000_drv_resume,
+};
+
 static int __devexit
 dm9000_drv_remove(struct platform_device *pdev)
 {
@@ -1453,7 +1675,7 @@ dm9000_drv_remove(struct platform_device *pdev)
        platform_set_drvdata(pdev, NULL);
 
        unregister_netdev(ndev);
-       dm9000_release_board(pdev, (board_info_t *) ndev->priv);
+       dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));
        free_netdev(ndev);              /* free device structure */
 
        dev_dbg(&pdev->dev, "released and freed device\n");
@@ -1464,11 +1686,10 @@ static struct platform_driver dm9000_driver = {
        .driver = {
                .name    = "dm9000",
                .owner   = THIS_MODULE,
+               .pm      = &dm9000_drv_pm_ops,
        },
        .probe   = dm9000_probe,
        .remove  = __devexit_p(dm9000_drv_remove),
-       .suspend = dm9000_drv_suspend,
-       .resume  = dm9000_drv_resume,
 };
 
 static int __init