fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / net / smc91x.c
index fb9935b..e62a958 100644 (file)
@@ -66,7 +66,6 @@ static const char version[] =
 #endif
 
 
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -77,7 +76,7 @@ static const char version[] =
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/crc32.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
@@ -88,7 +87,6 @@ static const char version[] =
 #include <linux/skbuff.h>
 
 #include <asm/io.h>
-#include <asm/irq.h>
 
 #include "smc91x.h"
 
@@ -129,7 +127,7 @@ MODULE_PARM_DESC(nowait, "set to 1 for no wait state");
 /*
  * Transmit timeout, default 5 seconds.
  */
-static int watchdog = 5000;
+static int watchdog = 1000;
 module_param(watchdog, int, 0400);
 MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
 
@@ -154,6 +152,12 @@ MODULE_LICENSE("GPL");
  */
 #define MEMORY_WAIT_TIME       16
 
+/*
+ * The maximum number of processing loops allowed for each call to the
+ * IRQ handler.
+ */
+#define MAX_IRQ_LOOPS          8
+
 /*
  * This selects whether TX packets are sent one by one to the SMC91x internal
  * memory and throttled until transmission completes.  This may prevent
@@ -206,6 +210,7 @@ struct smc_local {
 
        /* work queue */
        struct work_struct phy_configure;
+       struct net_device *dev;
        int     work_pending;
 
        spinlock_t lock;
@@ -214,6 +219,8 @@ struct smc_local {
        /* DMA needs the physical address of the chip */
        u_long physaddr;
 #endif
+       void __iomem *base;
+       void __iomem *datacs;
 };
 
 #if SMC_DEBUG > 0
@@ -307,17 +314,27 @@ static void PRINT_PKT(u_char *buf, int length)
  */
 static void smc_reset(struct net_device *dev)
 {
-       unsigned long ioaddr = dev->base_addr;
        struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        unsigned int ctl, cfg;
+       struct sk_buff *pending_skb;
 
        DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
 
-       /* Disable all interrupts */
-       spin_lock(&lp->lock);
+       /* Disable all interrupts, block TX tasklet */
+       spin_lock_irq(&lp->lock);
        SMC_SELECT_BANK(2);
        SMC_SET_INT_MASK(0);
-       spin_unlock(&lp->lock);
+       pending_skb = lp->pending_tx_skb;
+       lp->pending_tx_skb = NULL;
+       spin_unlock_irq(&lp->lock);
+
+       /* free any pending tx skb */
+       if (pending_skb) {
+               dev_kfree_skb(pending_skb);
+               lp->stats.tx_errors++;
+               lp->stats.tx_aborted_errors++;
+       }
 
        /*
         * This resets the registers mostly to defaults, but doesn't
@@ -384,14 +401,6 @@ static void smc_reset(struct net_device *dev)
        SMC_SELECT_BANK(2);
        SMC_SET_MMU_CMD(MC_RESET);
        SMC_WAIT_MMU_BUSY();
-
-       /* clear anything saved */
-       if (lp->pending_tx_skb != NULL) {
-               dev_kfree_skb (lp->pending_tx_skb);
-               lp->pending_tx_skb = NULL;
-               lp->stats.tx_errors++;
-               lp->stats.tx_aborted_errors++;
-       }
 }
 
 /*
@@ -399,8 +408,8 @@ static void smc_reset(struct net_device *dev)
  */
 static void smc_enable(struct net_device *dev)
 {
-       unsigned long ioaddr = dev->base_addr;
        struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        int mask;
 
        DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
@@ -433,16 +442,21 @@ static void smc_enable(struct net_device *dev)
  */
 static void smc_shutdown(struct net_device *dev)
 {
-       unsigned long ioaddr = dev->base_addr;
        struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
+       struct sk_buff *pending_skb;
 
        DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__);
 
        /* no more interrupts for me */
-       spin_lock(&lp->lock);
+       spin_lock_irq(&lp->lock);
        SMC_SELECT_BANK(2);
        SMC_SET_INT_MASK(0);
-       spin_unlock(&lp->lock);
+       pending_skb = lp->pending_tx_skb;
+       lp->pending_tx_skb = NULL;
+       spin_unlock_irq(&lp->lock);
+       if (pending_skb)
+               dev_kfree_skb(pending_skb);
 
        /* and tell the card to stay away from that nasty outside world */
        SMC_SELECT_BANK(0);
@@ -462,7 +476,7 @@ static void smc_shutdown(struct net_device *dev)
 static inline void  smc_rcv(struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
        unsigned int packet_number, status, packet_len;
 
        DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
@@ -483,7 +497,19 @@ static inline void  smc_rcv(struct net_device *dev)
                dev->name, packet_number, status,
                packet_len, packet_len);
 
-       if (unlikely(status & RS_ERRORS)) {
+       back:
+       if (unlikely(packet_len < 6 || status & RS_ERRORS)) {
+               if (status & RS_TOOLONG && packet_len <= (1514 + 4 + 6)) {
+                       /* accept VLAN packets */
+                       status &= ~RS_TOOLONG;
+                       goto back;
+               }
+               if (packet_len < 6) {
+                       /* bloody hardware */
+                       printk(KERN_ERR "%s: fubar (rxlen %u status %x\n",
+                                       dev->name, packet_len, status);
+                       status |= RS_TOOSHORT;
+               }
                SMC_WAIT_MMU_BUSY();
                SMC_SET_MMU_CMD(MC_RELEASE);
                lp->stats.rx_errors++;
@@ -508,7 +534,7 @@ static inline void  smc_rcv(struct net_device *dev)
                 * (2 bytes, possibly containing the payload odd byte).
                 * Furthermore, we add 2 bytes to allow rounding up to
                 * multiple of 4 bytes on 32 bit buses.
-                * Ence packet_len - 6 + 2 + 2 + 2.
+                * Hence packet_len - 6 + 2 + 2 + 2.
                 */
                skb = dev_alloc_skb(packet_len);
                if (unlikely(skb == NULL)) {
@@ -596,7 +622,7 @@ static void smc_hardware_send_pkt(unsigned long data)
 {
        struct net_device *dev = (struct net_device *)data;
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
        struct sk_buff *skb;
        unsigned int packet_no, len;
        unsigned char *buf;
@@ -610,7 +636,12 @@ static void smc_hardware_send_pkt(unsigned long data)
        }
 
        skb = lp->pending_tx_skb;
+       if (unlikely(!skb)) {
+               smc_special_unlock(&lp->lock);
+               return;
+       }
        lp->pending_tx_skb = NULL;
+
        packet_no = SMC_GET_AR();
        if (unlikely(packet_no & AR_FAILED)) {
                printk("%s: Memory allocation failed.\n", dev->name);
@@ -643,20 +674,18 @@ static void smc_hardware_send_pkt(unsigned long data)
        SMC_outw(((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG);
 
        /*
-        * If THROTTLE_TX_PKTS is set, we look at the TX_EMPTY flag
-        * before queueing this packet for TX, and if it's clear then
-        * we stop the queue here.  This will have the effect of
-        * having at most 2 packets queued for TX in the chip's memory
-        * at all time. If THROTTLE_TX_PKTS is not set then the queue
-        * is stopped only when memory allocation (MC_ALLOC) does not
-        * succeed right away.
+        * If THROTTLE_TX_PKTS is set, we stop the queue here. This will
+        * have the effect of having at most one packet queued for TX
+        * in the chip's memory at all time.
+        *
+        * If THROTTLE_TX_PKTS is not set then the queue is stopped only
+        * when memory allocation (MC_ALLOC) does not succeed right away.
         */
-       if (THROTTLE_TX_PKTS && !(SMC_GET_INT() & IM_TX_EMPTY_INT))
+       if (THROTTLE_TX_PKTS)
                netif_stop_queue(dev);
 
        /* queue the packet for TX */
        SMC_SET_MMU_CMD(MC_ENQUEUE);
-       SMC_ACK_INT(IM_TX_EMPTY_INT);
        smc_special_unlock(&lp->lock);
 
        dev->trans_start = jiffies;
@@ -680,13 +709,12 @@ done:     if (!THROTTLE_TX_PKTS)
 static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
        unsigned int numPages, poll_count, status;
 
        DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
 
        BUG_ON(lp->pending_tx_skb != NULL);
-       lp->pending_tx_skb = skb;
 
        /*
         * The MMU wants the number of pages to be the number of 256 bytes
@@ -702,7 +730,6 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        numPages = ((skb->len & ~1) + (6 - 1)) >> 8;
        if (unlikely(numPages > 7)) {
                printk("%s: Far too big packet error.\n", dev->name);
-               lp->pending_tx_skb = NULL;
                lp->stats.tx_errors++;
                lp->stats.tx_dropped++;
                dev_kfree_skb(skb);
@@ -729,6 +756,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        smc_special_unlock(&lp->lock);
 
+       lp->pending_tx_skb = skb;
        if (!poll_count) {
                /* oh well, wait until the chip finds memory later */
                netif_stop_queue(dev);
@@ -738,7 +766,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                /*
                 * Allocation succeeded: push packet to the chip's own memory
                 * immediately.
-                */  
+                */
                smc_hardware_send_pkt((unsigned long)dev);
        }
 
@@ -752,8 +780,8 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
  */
 static void smc_tx(struct net_device *dev)
 {
-       unsigned long ioaddr = dev->base_addr;
        struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        unsigned int saved_packet, packet_no, tx_status, pkt_len;
 
        DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
@@ -775,17 +803,20 @@ static void smc_tx(struct net_device *dev)
        DBG(2, "%s: TX STATUS 0x%04x PNR 0x%02x\n",
                dev->name, tx_status, packet_no);
 
-       if (!(tx_status & TS_SUCCESS))
+       if (!(tx_status & ES_TX_SUC))
                lp->stats.tx_errors++;
-       if (tx_status & TS_LOSTCAR)
+
+       if (tx_status & ES_LOSTCARR)
                lp->stats.tx_carrier_errors++;
 
-       if (tx_status & TS_LATCOL) {
-               PRINTK("%s: late collision occurred on last xmit\n", dev->name);
+       if (tx_status & (ES_LATCOL | ES_16COL)) {
+               PRINTK("%s: %s occurred on last xmit\n", dev->name,
+                      (tx_status & ES_LATCOL) ?
+                       "late collision" : "too many collisions");
                lp->stats.tx_window_errors++;
                if (!(lp->stats.tx_window_errors & 63) && net_ratelimit()) {
-                       printk(KERN_INFO "%s: unexpectedly large numbers of "
-                              "late collisions. Please check duplex "
+                       printk(KERN_INFO "%s: unexpectedly large number of "
+                              "bad collisions. Please check duplex "
                               "setting.\n", dev->name);
                }
        }
@@ -809,7 +840,8 @@ static void smc_tx(struct net_device *dev)
 
 static void smc_mii_out(struct net_device *dev, unsigned int val, int bits)
 {
-       unsigned long ioaddr = dev->base_addr;
+       struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        unsigned int mii_reg, mask;
 
        mii_reg = SMC_GET_MII() & ~(MII_MCLK | MII_MDOE | MII_MDO);
@@ -830,7 +862,8 @@ static void smc_mii_out(struct net_device *dev, unsigned int val, int bits)
 
 static unsigned int smc_mii_in(struct net_device *dev, int bits)
 {
-       unsigned long ioaddr = dev->base_addr;
+       struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        unsigned int mii_reg, mask, val;
 
        mii_reg = SMC_GET_MII() & ~(MII_MCLK | MII_MDOE | MII_MDO);
@@ -854,7 +887,8 @@ static unsigned int smc_mii_in(struct net_device *dev, int bits)
  */
 static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg)
 {
-       unsigned long ioaddr = dev->base_addr;
+       struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        unsigned int phydata;
 
        SMC_SELECT_BANK(3);
@@ -884,7 +918,8 @@ static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg)
 static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg,
                          int phydata)
 {
-       unsigned long ioaddr = dev->base_addr;
+       struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
 
        SMC_SELECT_BANK(3);
 
@@ -946,7 +981,7 @@ static void smc_phy_detect(struct net_device *dev)
 static int smc_phy_fixed(struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
        int phyaddr = lp->mii.phy_id;
        int bmcr, cfg1;
 
@@ -1017,13 +1052,29 @@ static int smc_phy_reset(struct net_device *dev, int phy)
 /*
  * smc_phy_powerdown - powerdown phy
  * @dev: net device
- * @phy: phy address
  *
  * Power down the specified PHY
  */
-static void smc_phy_powerdown(struct net_device *dev, int phy)
+static void smc_phy_powerdown(struct net_device *dev)
 {
+       struct smc_local *lp = netdev_priv(dev);
        unsigned int bmcr;
+       int phy = lp->mii.phy_id;
+
+       if (lp->phy_type == 0)
+               return;
+
+       /* We need to ensure that no calls to smc_phy_configure are
+          pending.
+
+          flush_scheduled_work() cannot be called because we are
+          running with the netlink semaphore held (from
+          devinet_ioctl()) and the pending work queue contains
+          linkwatch_event() (scheduled by netif_carrier_off()
+          above). linkwatch_event() also wants the netlink semaphore.
+       */
+       while(lp->work_pending)
+               yield();
 
        bmcr = smc_phy_read(dev, phy, MII_BMCR);
        smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN);
@@ -1040,7 +1091,7 @@ static void smc_phy_powerdown(struct net_device *dev, int phy)
 static void smc_phy_check_media(struct net_device *dev, int init)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
 
        if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
                /* duplex state has changed */
@@ -1064,11 +1115,12 @@ static void smc_phy_check_media(struct net_device *dev, int init)
  * of autonegotiation.)  If the RPC ANEG bit is cleared, the selection
  * is controlled by the RPC SPEED and RPC DPLX bits.
  */
-static void smc_phy_configure(void *data)
+static void smc_phy_configure(struct work_struct *work)
 {
-       struct net_device *dev = data;
-       struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       struct smc_local *lp =
+               container_of(work, struct smc_local, phy_configure);
+       struct net_device *dev = lp->dev;
+       void __iomem *ioaddr = lp->base;
        int phyaddr = lp->mii.phy_id;
        int my_phy_caps; /* My PHY capabilities */
        int my_ad_caps; /* My Advertised capabilities */
@@ -1157,6 +1209,7 @@ static void smc_phy_configure(void *data)
        smc_phy_check_media(dev, 1);
 
 smc_phy_configure_exit:
+       SMC_SELECT_BANK(2);
        spin_unlock_irq(&lp->lock);
        lp->work_pending = 0;
 }
@@ -1193,13 +1246,13 @@ static void smc_phy_interrupt(struct net_device *dev)
 static void smc_10bt_check_media(struct net_device *dev, int init)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
        unsigned int old_carrier, new_carrier;
 
        old_carrier = netif_carrier_ok(dev) ? 1 : 0;
 
        SMC_SELECT_BANK(0);
-       new_carrier = SMC_inw(ioaddr, EPH_STATUS_REG) & ES_LINK_OK ? 1 : 0;
+       new_carrier = (SMC_GET_EPH_STATUS() & ES_LINK_OK) ? 1 : 0;
        SMC_SELECT_BANK(2);
 
        if (init || (old_carrier != new_carrier)) {
@@ -1216,7 +1269,8 @@ static void smc_10bt_check_media(struct net_device *dev, int init)
 
 static void smc_eph_interrupt(struct net_device *dev)
 {
-       unsigned long ioaddr = dev->base_addr;
+       struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        unsigned int ctl;
 
        smc_10bt_check_media(dev, 0);
@@ -1232,11 +1286,11 @@ static void smc_eph_interrupt(struct net_device *dev)
  * This is the main routine of the driver, to handle the device when
  * it needs some attention.
  */
-static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t smc_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
-       unsigned long ioaddr = dev->base_addr;
        struct smc_local *lp = netdev_priv(dev);
+       void __iomem *ioaddr = lp->base;
        int status, mask, timeout, card_stats;
        int saved_pointer;
 
@@ -1254,7 +1308,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        SMC_SET_INT_MASK(0);
 
        /* set a timeout value, so I don't stay here forever */
-       timeout = 8;
+       timeout = MAX_IRQ_LOOPS;
 
        do {
                status = SMC_GET_INT();
@@ -1270,15 +1324,16 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                if (!status)
                        break;
 
-               if (status & IM_RCV_INT) {
-                       DBG(3, "%s: RX irq\n", dev->name);
-                       smc_rcv(dev);
-               } else if (status & IM_TX_INT) {
+               if (status & IM_TX_INT) {
+                       /* do this before RX as it will free memory quickly */
                        DBG(3, "%s: TX int\n", dev->name);
                        smc_tx(dev);
                        SMC_ACK_INT(IM_TX_INT);
                        if (THROTTLE_TX_PKTS)
                                netif_wake_queue(dev);
+               } else if (status & IM_RCV_INT) {
+                       DBG(3, "%s: RX irq\n", dev->name);
+                       smc_rcv(dev);
                } else if (status & IM_ALLOC_INT) {
                        DBG(3, "%s: Allocation irq\n", dev->name);
                        tasklet_hi_schedule(&lp->tx_task);
@@ -1299,7 +1354,10 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                        /* multiple collisions */
                        lp->stats.collisions += card_stats & 0xF;
                } else if (status & IM_RX_OVRN_INT) {
-                       DBG(1, "%s: RX overrun\n", dev->name);
+                       DBG(1, "%s: RX overrun (EPH_ST 0x%04x)\n", dev->name,
+                              ({ int eph_st; SMC_SELECT_BANK(0);
+                                 eph_st = SMC_GET_EPH_STATUS();
+                                 SMC_SELECT_BANK(2); eph_st; }) );
                        SMC_ACK_INT(IM_RX_OVRN_INT);
                        lp->stats.rx_errors++;
                        lp->stats.rx_fifo_errors++;
@@ -1317,10 +1375,13 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        /* restore register states */
        SMC_SET_PTR(saved_pointer);
        SMC_SET_INT_MASK(mask);
-
        spin_unlock(&lp->lock);
 
-       DBG(3, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout);
+       if (timeout == MAX_IRQ_LOOPS)
+               PRINTK("%s: spurious interrupt (mask = 0x%02x)\n",
+                      dev->name, mask);
+       DBG(3, "%s: Interrupt done (%d loops)\n",
+              dev->name, MAX_IRQ_LOOPS - timeout);
 
        /*
         * We return IRQ_HANDLED unconditionally here even if there was
@@ -1341,7 +1402,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 static void smc_poll_controller(struct net_device *dev)
 {
        disable_irq(dev->irq);
-       smc_interrupt(dev->irq, dev, NULL);
+       smc_interrupt(dev->irq, dev);
        enable_irq(dev->irq);
 }
 #endif
@@ -1350,8 +1411,8 @@ static void smc_poll_controller(struct net_device *dev)
 static void smc_timeout(struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
-       int status, mask, meminfo, fifo;
+       void __iomem *ioaddr = lp->base;
+       int status, mask, eph_st, meminfo, fifo;
 
        DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
 
@@ -1360,11 +1421,13 @@ static void smc_timeout(struct net_device *dev)
        mask = SMC_GET_INT_MASK();
        fifo = SMC_GET_FIFO();
        SMC_SELECT_BANK(0);
+       eph_st = SMC_GET_EPH_STATUS();
        meminfo = SMC_GET_MIR();
        SMC_SELECT_BANK(2);
        spin_unlock_irq(&lp->lock);
-       PRINTK( "%s: INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
-               dev->name, status, mask, meminfo, fifo );
+       PRINTK( "%s: TX timeout (INT 0x%02x INTMASK 0x%02x "
+               "MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n",
+               dev->name, status, mask, meminfo, fifo, eph_st );
 
        smc_reset(dev);
        smc_enable(dev);
@@ -1394,7 +1457,7 @@ static void smc_timeout(struct net_device *dev)
 static void smc_set_multicast_list(struct net_device *dev)
 {
        struct smc_local *lp = netdev_priv(dev);
-       unsigned long ioaddr = dev->base_addr;
+       void __iomem *ioaddr = lp->base;
        unsigned char multicast_table[8];
        int update_multicast = 0;
 
@@ -1531,7 +1594,7 @@ smc_open(struct net_device *dev)
 
        /* Configure the PHY, initialize the link state */
        if (lp->phy_type != 0)
-               smc_phy_configure(dev);
+               smc_phy_configure(&lp->phy_configure);
        else {
                spin_lock_irq(&lp->lock);
                smc_10bt_check_media(dev, 1);
@@ -1560,28 +1623,8 @@ static int smc_close(struct net_device *dev)
 
        /* clear everything */
        smc_shutdown(dev);
-
-       if (lp->phy_type != 0) {
-               /* We need to ensure that no calls to
-                  smc_phy_configure are pending.
-
-                  flush_scheduled_work() cannot be called because we
-                  are running with the netlink semaphore held (from
-                  devinet_ioctl()) and the pending work queue
-                  contains linkwatch_event() (scheduled by
-                  netif_carrier_off() above). linkwatch_event() also
-                  wants the netlink semaphore.
-               */
-               while(lp->work_pending)
-                       schedule();
-               smc_phy_powerdown(dev, lp->mii.phy_id);
-       }
-
-       if (lp->pending_tx_skb) {
-               dev_kfree_skb(lp->pending_tx_skb);
-               lp->pending_tx_skb = NULL;
-       }
-
+       tasklet_kill(&lp->tx_task);
+       smc_phy_powerdown(dev);
        return 0;
 }
 
@@ -1698,7 +1741,7 @@ static void smc_ethtool_setmsglevel(struct net_device *dev, u32 level)
        lp->msg_enable = level;
 }
 
-static struct ethtool_ops smc_ethtool_ops = {
+static const struct ethtool_ops smc_ethtool_ops = {
        .get_settings   = smc_ethtool_getsettings,
        .set_settings   = smc_ethtool_setsettings,
        .get_drvinfo    = smc_ethtool_getdrvinfo,
@@ -1723,7 +1766,7 @@ static struct ethtool_ops smc_ethtool_ops = {
  * I just deleted auto_irq.c, since it was never built...
  *   --jgarzik
  */
-static int __init smc_findirq(unsigned long ioaddr)
+static int __init smc_findirq(void __iomem *ioaddr)
 {
        int timeout = 20;
        unsigned long cookie;
@@ -1796,7 +1839,7 @@ static int __init smc_findirq(unsigned long ioaddr)
  * o  actually GRAB the irq.
  * o  GRAB the region
  */
-static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
+static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr)
 {
        struct smc_local *lp = netdev_priv(dev);
        static int version_printed = 0;
@@ -1813,7 +1856,7 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
                if ((val & 0xFF) == 0x33) {
                        printk(KERN_WARNING
                                "%s: Detected possible byte-swapped interface"
-                               " at IOADDR 0x%lx\n", CARDNAME, ioaddr);
+                               " at IOADDR %p\n", CARDNAME, ioaddr);
                }
                retval = -ENODEV;
                goto err_out;
@@ -1839,8 +1882,8 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
        SMC_SELECT_BANK(1);
        val = SMC_GET_BASE();
        val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
-       if ((ioaddr & ((PAGE_SIZE-1)<<SMC_IO_SHIFT)) != val) {
-               printk("%s: IOADDR %lx doesn't match configuration (%x).\n",
+       if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) {
+               printk("%s: IOADDR %p doesn't match configuration (%x).\n",
                        CARDNAME, ioaddr, val);
        }
 
@@ -1855,7 +1898,7 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
        version_string = chip_ids[ (revision_register >> 4) & 0xF];
        if (!version_string || (revision_register & 0xff00) != 0x3300) {
                /* I don't recognize this chip, so... */
-               printk("%s: IO 0x%lx: Unrecognized revision register 0x%04x"
+               printk("%s: IO %p: Unrecognized revision register 0x%04x"
                        ", Contact author.\n", CARDNAME,
                        ioaddr, revision_register);
 
@@ -1868,7 +1911,8 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
                printk("%s", version);
 
        /* fill in some of the fields */
-       dev->base_addr = ioaddr;
+       dev->base_addr = (unsigned long)ioaddr;
+       lp->base = ioaddr;
        lp->version = revision_register & 0xff;
        spin_lock_init(&lp->lock);
 
@@ -1930,7 +1974,8 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
 #endif
 
        tasklet_init(&lp->tx_task, smc_hardware_send_pkt, (unsigned long)dev);
-       INIT_WORK(&lp->phy_configure, smc_phy_configure, dev);
+       INIT_WORK(&lp->phy_configure, smc_phy_configure);
+       lp->dev = dev;
        lp->mii.phy_id_mask = 0x1f;
        lp->mii.reg_num_mask = 0x1f;
        lp->mii.force_media = 0;
@@ -1945,6 +1990,10 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
        if (lp->version >= (CHIP_91100 << 4))
                smc_phy_detect(dev);
 
+       /* then shut everything down to save power */
+       smc_shutdown(dev);
+       smc_phy_powerdown(dev);
+
        /* Set default parameters */
        lp->msg_enable = NETIF_MSG_LINK;
        lp->ctl_rfduplx = 0;
@@ -1956,12 +2005,10 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
        }
 
        /* Grab the IRQ */
-       retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
+       retval = request_irq(dev->irq, &smc_interrupt, SMC_IRQ_FLAGS, dev->name, dev);
        if (retval)
                goto err_out;
 
-       set_irq_type(dev->irq, IRQT_RISING);
-
 #ifdef SMC_USE_PXA_DMA
        {
                int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW,
@@ -1974,9 +2021,9 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
        retval = register_netdev(dev);
        if (retval == 0) {
                /* now, print out the card info, in a short format.. */
-               printk("%s: %s (rev %d) at %#lx IRQ %d",
+               printk("%s: %s (rev %d) at %p IRQ %d",
                        dev->name, version_string, revision_register & 0x0f,
-                       dev->base_addr, dev->irq);
+                       lp->base, dev->irq);
 
                if (dev->dma != (unsigned char)-1)
                        printk(" DMA %d", dev->dma);
@@ -2012,16 +2059,21 @@ err_out:
        return retval;
 }
 
-static int smc_enable_device(unsigned long attrib_phys)
+static int smc_enable_device(struct platform_device *pdev)
 {
        unsigned long flags;
        unsigned char ecor, ecsr;
-       void *addr;
+       void __iomem *addr;
+       struct resource * res;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+       if (!res)
+               return 0;
 
        /*
         * Map the attribute space.  This is overkill, but clean.
         */
-       addr = ioremap(attrib_phys, ATTRIB_SIZE);
+       addr = ioremap(res->start, ATTRIB_SIZE);
        if (!addr)
                return -ENOMEM;
 
@@ -2051,9 +2103,8 @@ static int smc_enable_device(unsigned long attrib_phys)
         * Set the appropriate byte/word mode.
         */
        ecsr = readb(addr + (ECSR << SMC_IO_SHIFT)) & ~ECSR_IOIS8;
-#ifndef SMC_CAN_USE_16BIT
-       ecsr |= ECSR_IOIS8;
-#endif
+       if (!SMC_CAN_USE_16BIT)
+               ecsr |= ECSR_IOIS8;
        writeb(ecsr, addr + (ECSR << SMC_IO_SHIFT));
        local_irq_restore(flags);
 
@@ -2069,6 +2120,61 @@ static int smc_enable_device(unsigned long attrib_phys)
        return 0;
 }
 
+static int smc_request_attrib(struct platform_device *pdev)
+{
+       struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+
+       if (!res)
+               return 0;
+
+       if (!request_mem_region(res->start, ATTRIB_SIZE, CARDNAME))
+               return -EBUSY;
+
+       return 0;
+}
+
+static void smc_release_attrib(struct platform_device *pdev)
+{
+       struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+
+       if (res)
+               release_mem_region(res->start, ATTRIB_SIZE);
+}
+
+static inline void smc_request_datacs(struct platform_device *pdev, struct net_device *ndev)
+{
+       if (SMC_CAN_USE_DATACS) {
+               struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32");
+               struct smc_local *lp = netdev_priv(ndev);
+
+               if (!res)
+                       return;
+
+               if(!request_mem_region(res->start, SMC_DATA_EXTENT, CARDNAME)) {
+                       printk(KERN_INFO "%s: failed to request datacs memory region.\n", CARDNAME);
+                       return;
+               }
+
+               lp->datacs = ioremap(res->start, SMC_DATA_EXTENT);
+       }
+}
+
+static void smc_release_datacs(struct platform_device *pdev, struct net_device *ndev)
+{
+       if (SMC_CAN_USE_DATACS) {
+               struct smc_local *lp = netdev_priv(ndev);
+               struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32");
+
+               if (lp->datacs)
+                       iounmap(lp->datacs);
+
+               lp->datacs = NULL;
+
+               if (res)
+                       release_mem_region(res->start, SMC_DATA_EXTENT);
+       }
+}
+
 /*
  * smc_init(void)
  *   Input parameters:
@@ -2080,24 +2186,23 @@ static int smc_enable_device(unsigned long attrib_phys)
  *     0 --> there is a device
  *     anything else, error
  */
-static int smc_drv_probe(struct device *dev)
+static int smc_drv_probe(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
        struct net_device *ndev;
-       struct resource *res, *ext = NULL;
-       unsigned int *addr;
+       struct resource *res;
+       unsigned int __iomem *addr;
        int ret;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs");
+       if (!res)
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                ret = -ENODEV;
                goto out;
        }
 
-       /*
-        * Request the regions.
-        */
-       if (!request_mem_region(res->start, SMC_IO_EXTENT, "smc91x")) {
+
+       if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) {
                ret = -EBUSY;
                goto out;
        }
@@ -2106,50 +2211,38 @@ static int smc_drv_probe(struct device *dev)
        if (!ndev) {
                printk("%s: could not allocate device.\n", CARDNAME);
                ret = -ENOMEM;
-               goto release_1;
+               goto out_release_io;
        }
        SET_MODULE_OWNER(ndev);
-       SET_NETDEV_DEV(ndev, dev);
+       SET_NETDEV_DEV(ndev, &pdev->dev);
 
        ndev->dma = (unsigned char)-1;
        ndev->irq = platform_get_irq(pdev, 0);
+       if (ndev->irq < 0) {
+               ret = -ENODEV;
+               goto out_free_netdev;
+       }
 
-       ext = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (ext) {
-               if (!request_mem_region(ext->start, ATTRIB_SIZE, ndev->name)) {
-                       ret = -EBUSY;
-                       goto release_1;
-               }
-
+       ret = smc_request_attrib(pdev);
+       if (ret)
+               goto out_free_netdev;
 #if defined(CONFIG_SA1100_ASSABET)
-               NCR_0 |= NCR_ENET_OSC_EN;
+       NCR_0 |= NCR_ENET_OSC_EN;
 #endif
-
-               ret = smc_enable_device(ext->start);
-               if (ret)
-                       goto release_both;
-       }
+       ret = smc_enable_device(pdev);
+       if (ret)
+               goto out_release_attrib;
 
        addr = ioremap(res->start, SMC_IO_EXTENT);
        if (!addr) {
                ret = -ENOMEM;
-               goto release_both;
+               goto out_release_attrib;
        }
 
-       dev_set_drvdata(dev, ndev);
-       ret = smc_probe(ndev, (unsigned long)addr);
-       if (ret != 0) {
-               dev_set_drvdata(dev, NULL);
-               iounmap(addr);
- release_both:
-               if (ext)
-                       release_mem_region(ext->start, ATTRIB_SIZE);
-               free_netdev(ndev);
- release_1:
-               release_mem_region(res->start, SMC_IO_EXTENT);
- out:
-               printk("%s: not found (%d).\n", CARDNAME, ret);
-       }
+       platform_set_drvdata(pdev, ndev);
+       ret = smc_probe(ndev, addr);
+       if (ret != 0)
+               goto out_iounmap;
 #ifdef SMC_USE_PXA_DMA
        else {
                struct smc_local *lp = netdev_priv(ndev);
@@ -2157,16 +2250,32 @@ static int smc_drv_probe(struct device *dev)
        }
 #endif
 
+       smc_request_datacs(pdev, ndev);
+
+       return 0;
+
+ out_iounmap:
+       platform_set_drvdata(pdev, NULL);
+       iounmap(addr);
+ out_release_attrib:
+       smc_release_attrib(pdev);
+ out_free_netdev:
+       free_netdev(ndev);
+ out_release_io:
+       release_mem_region(res->start, SMC_IO_EXTENT);
+ out:
+       printk("%s: not found (%d).\n", CARDNAME, ret);
+
        return ret;
 }
 
-static int smc_drv_remove(struct device *dev)
+static int smc_drv_remove(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct net_device *ndev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct smc_local *lp = netdev_priv(ndev);
        struct resource *res;
 
-       dev_set_drvdata(dev, NULL);
+       platform_set_drvdata(pdev, NULL);
 
        unregister_netdev(ndev);
 
@@ -2176,11 +2285,14 @@ static int smc_drv_remove(struct device *dev)
        if (ndev->dma != (unsigned char)-1)
                pxa_free_dma(ndev->dma);
 #endif
-       iounmap((void *)ndev->base_addr);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (res)
-               release_mem_region(res->start, ATTRIB_SIZE);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       iounmap(lp->base);
+
+       smc_release_datacs(pdev,ndev);
+       smc_release_attrib(pdev);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs");
+       if (!res)
+               platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(res->start, SMC_IO_EXTENT);
 
        free_netdev(ndev);
@@ -2188,47 +2300,46 @@ static int smc_drv_remove(struct device *dev)
        return 0;
 }
 
-static int smc_drv_suspend(struct device *dev, u32 state, u32 level)
+static int smc_drv_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct net_device *ndev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(dev);
 
-       if (ndev && level == SUSPEND_DISABLE) {
+       if (ndev) {
                if (netif_running(ndev)) {
                        netif_device_detach(ndev);
                        smc_shutdown(ndev);
+                       smc_phy_powerdown(ndev);
                }
        }
        return 0;
 }
 
-static int smc_drv_resume(struct device *dev, u32 level)
+static int smc_drv_resume(struct platform_device *dev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
-       struct net_device *ndev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(dev);
 
-       if (ndev && level == RESUME_ENABLE) {
+       if (ndev) {
                struct smc_local *lp = netdev_priv(ndev);
-
-               if (pdev->num_resources == 3)
-                       smc_enable_device(pdev->resource[2].start);
+               smc_enable_device(dev);
                if (netif_running(ndev)) {
                        smc_reset(ndev);
                        smc_enable(ndev);
                        if (lp->phy_type != 0)
-                               smc_phy_configure(ndev);
+                               smc_phy_configure(&lp->phy_configure);
                        netif_device_attach(ndev);
                }
        }
        return 0;
 }
 
-static struct device_driver smc_driver = {
-       .name           = CARDNAME,
-       .bus            = &platform_bus_type,
+static struct platform_driver smc_driver = {
        .probe          = smc_drv_probe,
        .remove         = smc_drv_remove,
        .suspend        = smc_drv_suspend,
        .resume         = smc_drv_resume,
+       .driver         = {
+               .name   = CARDNAME,
+       },
 };
 
 static int __init smc_init(void)
@@ -2236,18 +2347,18 @@ static int __init smc_init(void)
 #ifdef MODULE
 #ifdef CONFIG_ISA
        if (io == -1)
-               printk(KERN_WARNING 
+               printk(KERN_WARNING
                        "%s: You shouldn't use auto-probing with insmod!\n",
                        CARDNAME);
 #endif
 #endif
 
-       return driver_register(&smc_driver);
+       return platform_driver_register(&smc_driver);
 }
 
 static void __exit smc_cleanup(void)
 {
-       driver_unregister(&smc_driver);
+       platform_driver_unregister(&smc_driver);
 }
 
 module_init(smc_init);