[netdrvr] Use dev_printk() when ethernet interface isn't available
[linux-2.6.git] / drivers / net / cassini.c
index 45831fb377a0c399a896ed3ef4a696bf849cda84..428e2067738c50abf6612806411bb23c8dae9fa9 100644 (file)
@@ -66,8 +66,6 @@
  * by default, the selective clear mask is set up to process rx packets.  
  */
 
-#include <linux/config.h>
-#include <linux/version.h>
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -92,6 +90,7 @@
 #include <linux/mii.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
+#include <linux/mutex.h>
 
 #include <net/checksum.h>
 
 static char version[] __devinitdata =
        DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
 
+static int cassini_debug = -1; /* -1 == use CAS_DEF_MSG_ENABLE as value */
+static int link_mode;
+
 MODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)");
 MODULE_DESCRIPTION("Sun Cassini(+) ethernet driver");
 MODULE_LICENSE("GPL");
-MODULE_PARM(cassini_debug, "i");
+module_param(cassini_debug, int, 0);
 MODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value");
-MODULE_PARM(link_mode, "i");
+module_param(link_mode, int, 0);
 MODULE_PARM_DESC(link_mode, "default link mode");
 
 /*
@@ -209,7 +211,7 @@ MODULE_PARM_DESC(link_mode, "default link mode");
  * Value in seconds, for user input.
  */
 static int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT;
-MODULE_PARM(linkdown_timeout, "i");
+module_param(linkdown_timeout, int, 0);
 MODULE_PARM_DESC(linkdown_timeout,
 "min reset interval in sec. for PCS linkdown issue; disabled if not positive");
 
@@ -221,8 +223,6 @@ MODULE_PARM_DESC(linkdown_timeout,
 static int link_transition_timeout;
 
 
-static int cassini_debug = -1; /* -1 == use CAS_DEF_MSG_ENABLE as value */
-static int link_mode;
 
 static u16 link_modes[] __devinitdata = {
        BMCR_ANENABLE,                   /* 0 : autoneg */
@@ -336,6 +336,30 @@ static inline void cas_mask_intr(struct cas *cp)
                cas_disable_irq(cp, i);
 }
 
+static inline void cas_buffer_init(cas_page_t *cp)
+{
+       struct page *page = cp->buffer;
+       atomic_set((atomic_t *)&page->lru.next, 1);
+}
+
+static inline int cas_buffer_count(cas_page_t *cp)
+{
+       struct page *page = cp->buffer;
+       return atomic_read((atomic_t *)&page->lru.next);
+}
+
+static inline void cas_buffer_inc(cas_page_t *cp)
+{
+       struct page *page = cp->buffer;
+       atomic_inc((atomic_t *)&page->lru.next);
+}
+
+static inline void cas_buffer_dec(cas_page_t *cp)
+{
+       struct page *page = cp->buffer;
+       atomic_dec((atomic_t *)&page->lru.next);
+}
+
 static void cas_enable_irq(struct cas *cp, const int ring)
 {
        if (ring == 0) { /* all but TX_DONE */
@@ -473,6 +497,7 @@ static int cas_page_free(struct cas *cp, cas_page_t *page)
 {
        pci_unmap_page(cp->pdev, page->dma_addr, cp->page_size, 
                       PCI_DMA_FROMDEVICE);
+       cas_buffer_dec(page);
        __free_pages(page->buffer, cp->page_order);
        kfree(page);
        return 0;
@@ -489,7 +514,7 @@ static int cas_page_free(struct cas *cp, cas_page_t *page)
 /* local page allocation routines for the receive buffers. jumbo pages
  * require at least 8K contiguous and 8K aligned buffers.
  */
-static cas_page_t *cas_page_alloc(struct cas *cp, const int flags)
+static cas_page_t *cas_page_alloc(struct cas *cp, const gfp_t flags)
 {
        cas_page_t *page;
 
@@ -502,6 +527,7 @@ static cas_page_t *cas_page_alloc(struct cas *cp, const int flags)
        page->buffer = alloc_pages(flags, cp->page_order);
        if (!page->buffer)
                goto page_err;
+       cas_buffer_init(page);
        page->dma_addr = pci_map_page(cp->pdev, page->buffer, 0,
                                      cp->page_size, PCI_DMA_FROMDEVICE);
        return page;
@@ -561,7 +587,7 @@ static void cas_spare_free(struct cas *cp)
 }
 
 /* replenish spares if needed */
-static void cas_spare_recover(struct cas *cp, const int flags)
+static void cas_spare_recover(struct cas *cp, const gfp_t flags)
 {
        struct list_head list, *elem, *tmp;
        int needed, i;
@@ -580,7 +606,7 @@ static void cas_spare_recover(struct cas *cp, const int flags)
        list_for_each_safe(elem, tmp, &list) {
                cas_page_t *page = list_entry(elem, cas_page_t, list);
 
-               if (page_count(page->buffer) > 1) 
+               if (cas_buffer_count(page) > 1)
                        continue;
 
                list_del(elem);
@@ -1348,7 +1374,7 @@ static inline cas_page_t *cas_page_spare(struct cas *cp, const int index)
        cas_page_t *page = cp->rx_pages[1][index];
        cas_page_t *new;
 
-       if (page_count(page->buffer) == 1)
+       if (cas_buffer_count(page) == 1)
                return page;
 
        new = cas_page_dequeue(cp);
@@ -1368,7 +1394,7 @@ static cas_page_t *cas_page_swap(struct cas *cp, const int ring,
        cas_page_t **page1 = cp->rx_pages[1];
 
        /* swap if buffer is in use */
-       if (page_count(page0[index]->buffer) > 1) {
+       if (cas_buffer_count(page0[index]) > 1) {
                cas_page_t *new = cas_page_spare(cp, index);
                if (new) {
                        page1[index] = page0[index];
@@ -1926,8 +1952,8 @@ static void cas_tx(struct net_device *dev, struct cas *cp,
        u64 compwb = le64_to_cpu(cp->init_block->tx_compwb);
 #endif
        if (netif_msg_intr(cp))
-               printk(KERN_DEBUG "%s: tx interrupt, status: 0x%x, %lx\n",
-                       cp->dev->name, status, compwb);
+               printk(KERN_DEBUG "%s: tx interrupt, status: 0x%x, %llx\n",
+                       cp->dev->name, status, (unsigned long long)compwb);
        /* process all the rings */
        for (ring = 0; ring < N_TX_RINGS; ring++) {
 #ifdef USE_TX_COMPWB
@@ -2040,6 +2066,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc,
                skb->len      += hlen - swivel;
 
                get_page(page->buffer);
+               cas_buffer_inc(page);
                frag->page = page->buffer;
                frag->page_offset = off;
                frag->size = hlen - swivel;
@@ -2064,6 +2091,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc,
                        frag++;
 
                        get_page(page->buffer);
+                       cas_buffer_inc(page);
                        frag->page = page->buffer;
                        frag->page_offset = 0;
                        frag->size = hlen;
@@ -2226,7 +2254,7 @@ static int cas_post_rxds_ringN(struct cas *cp, int ring, int num)
        released = 0;
        while (entry != last) {
                /* make a new buffer if it's still in use */
-               if (page_count(page[entry]->buffer) > 1) {
+               if (cas_buffer_count(page[entry]) > 1) {
                        cas_page_t *new = cas_page_dequeue(cp);
                        if (!new) {
                                /* let the timer know that we need to 
@@ -2886,8 +2914,7 @@ static int cas_start_xmit(struct sk_buff *skb, struct net_device *dev)
         */
        static int ring; 
 
-       skb = skb_padto(skb, cp->min_frame_size);
-       if (!skb)
+       if (skb_padto(skb, cp->min_frame_size))
                return 0;
 
        /* XXX: we need some higher-level QoS hooks to steer packets to
@@ -3865,7 +3892,7 @@ static void cas_reset(struct cas *cp, int blkflag)
        spin_unlock(&cp->stat_lock[N_TX_RINGS]);
 }
 
-/* Shut down the chip, must be called with pm_sem held.  */
+/* Shut down the chip, must be called with pm_mutex held.  */
 static void cas_shutdown(struct cas *cp)
 {
        unsigned long flags;
@@ -4284,11 +4311,11 @@ static int cas_open(struct net_device *dev)
        int hw_was_up, err;
        unsigned long flags;
 
-       down(&cp->pm_sem);
+       mutex_lock(&cp->pm_mutex);
 
        hw_was_up = cp->hw_running;
 
-       /* The power-management semaphore protects the hw_running
+       /* The power-management mutex protects the hw_running
         * etc. state so it is safe to do this bit without cp->lock
         */
        if (!cp->hw_running) {
@@ -4322,7 +4349,7 @@ static int cas_open(struct net_device *dev)
         * mapping to expose them
         */
        if (request_irq(cp->pdev->irq, cas_interrupt,
-                       SA_SHIRQ, dev->name, (void *) dev)) {
+                       IRQF_SHARED, dev->name, (void *) dev)) {
                printk(KERN_ERR "%s: failed to request irq !\n", 
                       cp->dev->name);
                err = -EAGAIN;
@@ -4337,7 +4364,7 @@ static int cas_open(struct net_device *dev)
        cas_unlock_all_restore(cp, flags);
 
        netif_start_queue(dev);
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
        return 0;
 
 err_spare:
@@ -4345,7 +4372,7 @@ err_spare:
        cas_free_rxds(cp);
 err_tx_tiny:
        cas_tx_tiny_free(cp);
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
        return err;
 }
 
@@ -4355,7 +4382,7 @@ static int cas_close(struct net_device *dev)
        struct cas *cp = netdev_priv(dev);
 
        /* Make sure we don't get distracted by suspend/resume */
-       down(&cp->pm_sem);
+       mutex_lock(&cp->pm_mutex);
 
        netif_stop_queue(dev);
 
@@ -4372,7 +4399,7 @@ static int cas_close(struct net_device *dev)
        cas_spare_free(cp);
        cas_free_rxds(cp);
        cas_tx_tiny_free(cp);
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
        return 0;
 }
 
@@ -4423,18 +4450,14 @@ static struct {
 #define CAS_REG_LEN    (sizeof(ethtool_register_table)/sizeof(int))
 #define CAS_MAX_REGS   (sizeof (u32)*CAS_REG_LEN)
 
-static u8 *cas_get_regs(struct cas *cp)
+static void cas_read_regs(struct cas *cp, u8 *ptr, int len)
 {
-       u8 *ptr = kmalloc(CAS_MAX_REGS, GFP_KERNEL);
        u8 *p;
        int i;
        unsigned long flags;
 
-       if (!ptr)
-               return NULL;
-
        spin_lock_irqsave(&cp->lock, flags);
-       for (i = 0, p = ptr; i < CAS_REG_LEN ; i ++, p += sizeof(u32)) {
+       for (i = 0, p = ptr; i < len ; i ++, p += sizeof(u32)) {
                u16 hval;
                u32 val;
                if (ethtool_register_table[i].offsets < 0) {
@@ -4447,8 +4470,6 @@ static u8 *cas_get_regs(struct cas *cp)
                memcpy(p, (u8 *)&val, sizeof(u32));
        }
        spin_unlock_irqrestore(&cp->lock, flags);
-
-       return ptr;
 }
 
 static struct net_device_stats *cas_get_stats(struct net_device *dev)
@@ -4561,316 +4582,251 @@ static void cas_set_multicast(struct net_device *dev)
        spin_unlock_irqrestore(&cp->lock, flags);
 }
 
-/* Eventually add support for changing the advertisement
- * on autoneg.
- */
-static int cas_ethtool_ioctl(struct net_device *dev, void __user *ep_user)
+static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct cas *cp = netdev_priv(dev);
+       strncpy(info->driver, DRV_MODULE_NAME, ETHTOOL_BUSINFO_LEN);
+       strncpy(info->version, DRV_MODULE_VERSION, ETHTOOL_BUSINFO_LEN);
+       info->fw_version[0] = '\0';
+       strncpy(info->bus_info, pci_name(cp->pdev), ETHTOOL_BUSINFO_LEN);
+       info->regdump_len = cp->casreg_len < CAS_MAX_REGS ?
+               cp->casreg_len : CAS_MAX_REGS;
+       info->n_stats = CAS_NUM_STAT_KEYS;
+}
+
+static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
        struct cas *cp = netdev_priv(dev);
        u16 bmcr;
        int full_duplex, speed, pause;
-       struct ethtool_cmd ecmd;
        unsigned long flags;
        enum link_state linkstate = link_up;
 
-       if (copy_from_user(&ecmd, ep_user, sizeof(ecmd)))
-               return -EFAULT;
-               
-       switch(ecmd.cmd) {
-        case ETHTOOL_GDRVINFO: {
-               struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO };
-
-               strncpy(info.driver, DRV_MODULE_NAME,
-                       ETHTOOL_BUSINFO_LEN);
-               strncpy(info.version, DRV_MODULE_VERSION,
-                       ETHTOOL_BUSINFO_LEN);
-               info.fw_version[0] = '\0';
-               strncpy(info.bus_info, pci_name(cp->pdev),
-                       ETHTOOL_BUSINFO_LEN);
-               info.regdump_len = cp->casreg_len < CAS_MAX_REGS ?
-                       cp->casreg_len : CAS_MAX_REGS;
-               info.n_stats = CAS_NUM_STAT_KEYS;
-               if (copy_to_user(ep_user, &info, sizeof(info)))
-                       return -EFAULT;
-
-               return 0;
+       cmd->advertising = 0;
+       cmd->supported = SUPPORTED_Autoneg;
+       if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
+               cmd->supported |= SUPPORTED_1000baseT_Full;
+               cmd->advertising |= ADVERTISED_1000baseT_Full;
        }
 
-       case ETHTOOL_GSET:
-               ecmd.advertising = 0;
-               ecmd.supported = SUPPORTED_Autoneg;
-               if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
-                       ecmd.supported |= SUPPORTED_1000baseT_Full;
-                       ecmd.advertising |= ADVERTISED_1000baseT_Full;
+       /* Record PHY settings if HW is on. */
+       spin_lock_irqsave(&cp->lock, flags);
+       bmcr = 0;
+       linkstate = cp->lstate;
+       if (CAS_PHY_MII(cp->phy_type)) {
+               cmd->port = PORT_MII;
+               cmd->transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ?
+                       XCVR_INTERNAL : XCVR_EXTERNAL;
+               cmd->phy_address = cp->phy_addr;
+               cmd->advertising |= ADVERTISED_TP | ADVERTISED_MII |
+                       ADVERTISED_10baseT_Half | 
+                       ADVERTISED_10baseT_Full | 
+                       ADVERTISED_100baseT_Half | 
+                       ADVERTISED_100baseT_Full;
+
+               cmd->supported |=
+                       (SUPPORTED_10baseT_Half | 
+                        SUPPORTED_10baseT_Full |
+                        SUPPORTED_100baseT_Half | 
+                        SUPPORTED_100baseT_Full |
+                        SUPPORTED_TP | SUPPORTED_MII);
+
+               if (cp->hw_running) {
+                       cas_mif_poll(cp, 0);
+                       bmcr = cas_phy_read(cp, MII_BMCR);
+                       cas_read_mii_link_mode(cp, &full_duplex, 
+                                              &speed, &pause);
+                       cas_mif_poll(cp, 1);
                }
 
-               /* Record PHY settings if HW is on. */
-               spin_lock_irqsave(&cp->lock, flags);
-               bmcr = 0;
-               linkstate = cp->lstate;
-               if (CAS_PHY_MII(cp->phy_type)) {
-                       ecmd.port = PORT_MII;
-                       ecmd.transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ?
-                               XCVR_INTERNAL : XCVR_EXTERNAL;
-                       ecmd.phy_address = cp->phy_addr;
-                       ecmd.advertising |= ADVERTISED_TP | ADVERTISED_MII |
-                               ADVERTISED_10baseT_Half | 
-                               ADVERTISED_10baseT_Full | 
-                               ADVERTISED_100baseT_Half | 
-                               ADVERTISED_100baseT_Full;
-
-                       ecmd.supported |=
-                               (SUPPORTED_10baseT_Half | 
-                                SUPPORTED_10baseT_Full |
-                                SUPPORTED_100baseT_Half | 
-                                SUPPORTED_100baseT_Full |
-                                SUPPORTED_TP | SUPPORTED_MII);
-
-                       if (cp->hw_running) {
-                               cas_mif_poll(cp, 0);
-                               bmcr = cas_phy_read(cp, MII_BMCR);
-                               cas_read_mii_link_mode(cp, &full_duplex, 
-                                                      &speed, &pause);
-                               cas_mif_poll(cp, 1);
-                       }
-
-               } else {
-                       ecmd.port = PORT_FIBRE;
-                       ecmd.transceiver = XCVR_INTERNAL;
-                       ecmd.phy_address = 0;
-                       ecmd.supported   |= SUPPORTED_FIBRE;
-                       ecmd.advertising |= ADVERTISED_FIBRE;
-
-                       if (cp->hw_running) {
-                               /* pcs uses the same bits as mii */ 
-                               bmcr = readl(cp->regs + REG_PCS_MII_CTRL);
-                               cas_read_pcs_link_mode(cp, &full_duplex, 
-                                                      &speed, &pause);
-                       }
+       } else {
+               cmd->port = PORT_FIBRE;
+               cmd->transceiver = XCVR_INTERNAL;
+               cmd->phy_address = 0;
+               cmd->supported   |= SUPPORTED_FIBRE;
+               cmd->advertising |= ADVERTISED_FIBRE;
+
+               if (cp->hw_running) {
+                       /* pcs uses the same bits as mii */ 
+                       bmcr = readl(cp->regs + REG_PCS_MII_CTRL);
+                       cas_read_pcs_link_mode(cp, &full_duplex, 
+                                              &speed, &pause);
                }
-               spin_unlock_irqrestore(&cp->lock, flags);
+       }
+       spin_unlock_irqrestore(&cp->lock, flags);
 
-               if (bmcr & BMCR_ANENABLE) {
-                       ecmd.advertising |= ADVERTISED_Autoneg;
-                       ecmd.autoneg = AUTONEG_ENABLE;
-                       ecmd.speed = ((speed == 10) ?
-                                     SPEED_10 :
-                                     ((speed == 1000) ?
-                                      SPEED_1000 : SPEED_100));
-                       ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+       if (bmcr & BMCR_ANENABLE) {
+               cmd->advertising |= ADVERTISED_Autoneg;
+               cmd->autoneg = AUTONEG_ENABLE;
+               cmd->speed = ((speed == 10) ?
+                             SPEED_10 :
+                             ((speed == 1000) ?
+                              SPEED_1000 : SPEED_100));
+               cmd->duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+       } else {
+               cmd->autoneg = AUTONEG_DISABLE;
+               cmd->speed =
+                       (bmcr & CAS_BMCR_SPEED1000) ?
+                       SPEED_1000 : 
+                       ((bmcr & BMCR_SPEED100) ? SPEED_100: 
+                        SPEED_10);
+               cmd->duplex =
+                       (bmcr & BMCR_FULLDPLX) ?
+                       DUPLEX_FULL : DUPLEX_HALF;
+       }
+       if (linkstate != link_up) {
+               /* Force these to "unknown" if the link is not up and
+                * autonogotiation in enabled. We can set the link 
+                * speed to 0, but not cmd->duplex,
+                * because its legal values are 0 and 1.  Ethtool will
+                * print the value reported in parentheses after the
+                * word "Unknown" for unrecognized values.
+                *
+                * If in forced mode, we report the speed and duplex
+                * settings that we configured.
+                */
+               if (cp->link_cntl & BMCR_ANENABLE) {
+                       cmd->speed = 0;
+                       cmd->duplex = 0xff;
                } else {
-                       ecmd.autoneg = AUTONEG_DISABLE;
-                       ecmd.speed =
-                               (bmcr & CAS_BMCR_SPEED1000) ?
-                               SPEED_1000 : 
-                               ((bmcr & BMCR_SPEED100) ? SPEED_100: 
-                                SPEED_10);
-                       ecmd.duplex =
-                               (bmcr & BMCR_FULLDPLX) ?
-                               DUPLEX_FULL : DUPLEX_HALF;
-               }
-               if (linkstate != link_up) {
-                       /* Force these to "unknown" if the link is not up and
-                        * autonogotiation in enabled. We can set the link 
-                        * speed to 0, but not ecmd.duplex,
-                        * because its legal values are 0 and 1.  Ethtool will
-                        * print the value reported in parentheses after the
-                        * word "Unknown" for unrecognized values.
-                        *
-                        * If in forced mode, we report the speed and duplex
-                        * settings that we configured.
-                        */
-                       if (cp->link_cntl & BMCR_ANENABLE) {
-                               ecmd.speed = 0;
-                               ecmd.duplex = 0xff;
-                       } else {
-                               ecmd.speed = SPEED_10;
-                               if (cp->link_cntl & BMCR_SPEED100) {
-                                       ecmd.speed = SPEED_100;
-                               } else if (cp->link_cntl & CAS_BMCR_SPEED1000) {
-                                       ecmd.speed = SPEED_1000;
-                               }
-                               ecmd.duplex = (cp->link_cntl & BMCR_FULLDPLX)?
-                                       DUPLEX_FULL : DUPLEX_HALF;
+                       cmd->speed = SPEED_10;
+                       if (cp->link_cntl & BMCR_SPEED100) {
+                               cmd->speed = SPEED_100;
+                       } else if (cp->link_cntl & CAS_BMCR_SPEED1000) {
+                               cmd->speed = SPEED_1000;
                        }
+                       cmd->duplex = (cp->link_cntl & BMCR_FULLDPLX)?
+                               DUPLEX_FULL : DUPLEX_HALF;
                }
-               if (copy_to_user(ep_user, &ecmd, sizeof(ecmd)))
-                       return -EFAULT;
-               return 0;
+       }
+       return 0;
+}
 
-       case ETHTOOL_SSET:
-               if (!capable(CAP_NET_ADMIN))
-                       return -EPERM;
+static int cas_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct cas *cp = netdev_priv(dev);
+       unsigned long flags;
 
-               /* Verify the settings we care about. */
-               if (ecmd.autoneg != AUTONEG_ENABLE &&
-                   ecmd.autoneg != AUTONEG_DISABLE)
-                       return -EINVAL;
+       /* Verify the settings we care about. */
+       if (cmd->autoneg != AUTONEG_ENABLE &&
+           cmd->autoneg != AUTONEG_DISABLE)
+               return -EINVAL;
 
-               if (ecmd.autoneg == AUTONEG_DISABLE &&
-                   ((ecmd.speed != SPEED_1000 &&
-                     ecmd.speed != SPEED_100 &&
-                     ecmd.speed != SPEED_10) ||
-                    (ecmd.duplex != DUPLEX_HALF &&
-                     ecmd.duplex != DUPLEX_FULL)))
-                       return -EINVAL;
+       if (cmd->autoneg == AUTONEG_DISABLE &&
+           ((cmd->speed != SPEED_1000 &&
+             cmd->speed != SPEED_100 &&
+             cmd->speed != SPEED_10) ||
+            (cmd->duplex != DUPLEX_HALF &&
+             cmd->duplex != DUPLEX_FULL)))
+               return -EINVAL;
 
-               /* Apply settings and restart link process. */
-               spin_lock_irqsave(&cp->lock, flags);
-               cas_begin_auto_negotiation(cp, &ecmd);
-               spin_unlock_irqrestore(&cp->lock, flags);
-               return 0;
+       /* Apply settings and restart link process. */
+       spin_lock_irqsave(&cp->lock, flags);
+       cas_begin_auto_negotiation(cp, cmd);
+       spin_unlock_irqrestore(&cp->lock, flags);
+       return 0;
+}
 
-       case ETHTOOL_NWAY_RST:
-               if ((cp->link_cntl & BMCR_ANENABLE) == 0)
-                       return -EINVAL;
+static int cas_nway_reset(struct net_device *dev)
+{
+       struct cas *cp = netdev_priv(dev);
+       unsigned long flags;
 
-               /* Restart link process. */
-               spin_lock_irqsave(&cp->lock, flags);
-               cas_begin_auto_negotiation(cp, NULL);
-               spin_unlock_irqrestore(&cp->lock, flags);
+       if ((cp->link_cntl & BMCR_ANENABLE) == 0)
+               return -EINVAL;
 
-               return 0;
+       /* Restart link process. */
+       spin_lock_irqsave(&cp->lock, flags);
+       cas_begin_auto_negotiation(cp, NULL);
+       spin_unlock_irqrestore(&cp->lock, flags);
 
-       case ETHTOOL_GWOL:
-       case ETHTOOL_SWOL:
-               break; /* doesn't exist */
+       return 0;
+}
 
-       /* get link status */
-       case ETHTOOL_GLINK: {
-               struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
+static u32 cas_get_link(struct net_device *dev)
+{
+       struct cas *cp = netdev_priv(dev);
+       return cp->lstate == link_up;
+}
 
-               edata.data = (cp->lstate == link_up);
-               if (copy_to_user(ep_user, &edata, sizeof(edata)))
-                       return -EFAULT;
-               return 0;
-       }
+static u32 cas_get_msglevel(struct net_device *dev)
+{
+       struct cas *cp = netdev_priv(dev);
+       return cp->msg_enable;
+}
 
-       /* get message-level */
-       case ETHTOOL_GMSGLVL: {
-               struct ethtool_value edata = { .cmd = ETHTOOL_GMSGLVL };
+static void cas_set_msglevel(struct net_device *dev, u32 value)
+{
+       struct cas *cp = netdev_priv(dev);
+       cp->msg_enable = value;
+}
 
-               edata.data = cp->msg_enable;
-               if (copy_to_user(ep_user, &edata, sizeof(edata)))
-                       return -EFAULT;
-               return 0;
-       }
+static int cas_get_regs_len(struct net_device *dev)
+{
+       struct cas *cp = netdev_priv(dev);
+       return cp->casreg_len < CAS_MAX_REGS ? cp->casreg_len: CAS_MAX_REGS;
+}
 
-       /* set message-level */
-       case ETHTOOL_SMSGLVL: {
-               struct ethtool_value edata;
+static void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+                            void *p)
+{
+       struct cas *cp = netdev_priv(dev);
+       regs->version = 0;
+       /* cas_read_regs handles locks (cp->lock).  */
+       cas_read_regs(cp, p, regs->len / sizeof(u32));
+}
 
-               if (!capable(CAP_NET_ADMIN)) {
-                       return (-EPERM);
-               }
-               if (copy_from_user(&edata, ep_user, sizeof(edata)))
-                       return -EFAULT;
-               cp->msg_enable = edata.data;
-               return 0;
-       }
+static int cas_get_stats_count(struct net_device *dev)
+{
+       return CAS_NUM_STAT_KEYS;
+}
 
-       case ETHTOOL_GREGS: {
-               struct ethtool_regs edata;
-               u8 *ptr;
-               int len = cp->casreg_len < CAS_MAX_REGS ?
-                       cp->casreg_len: CAS_MAX_REGS;
-
-               if (copy_from_user(&edata, ep_user, sizeof (edata)))
-                       return -EFAULT;
-
-               if (edata.len > len)
-                       edata.len = len;
-               edata.version = 0;
-               if (copy_to_user (ep_user, &edata, sizeof(edata)))
-                       return -EFAULT;
-
-               /* cas_get_regs handles locks (cp->lock).  */
-               ptr = cas_get_regs(cp);
-               if (ptr == NULL)
-                       return -ENOMEM;
-               if (copy_to_user(ep_user + sizeof (edata), ptr, edata.len))
-                       return -EFAULT;
-
-               kfree(ptr);
-               return (0);
-       }
-       case ETHTOOL_GSTRINGS: {
-               struct ethtool_gstrings edata;
-               int len;
-
-               if (copy_from_user(&edata, ep_user, sizeof(edata)))
-                       return -EFAULT;
-
-               len = edata.len;
-               switch(edata.string_set) {
-               case ETH_SS_STATS:
-                       edata.len = (len < CAS_NUM_STAT_KEYS) ?
-                               len : CAS_NUM_STAT_KEYS;
-                       if (copy_to_user(ep_user, &edata, sizeof(edata)))
-                               return -EFAULT;
-
-                       if (copy_to_user(ep_user + sizeof(edata),
-                                        &ethtool_cassini_statnames, 
-                                        (edata.len * ETH_GSTRING_LEN)))
-                               return -EFAULT;
-                       return 0;
-               default:
-                       return -EINVAL;
-               }
-       }
-       case ETHTOOL_GSTATS: {
-               int i = 0;
-               u64 *tmp;
-               struct ethtool_stats edata;
-               struct net_device_stats *stats;
-               int len;
-
-               if (copy_from_user(&edata, ep_user, sizeof(edata)))
-                       return -EFAULT;
-
-               len = edata.n_stats;
-               stats = cas_get_stats(cp->dev);
-               edata.cmd = ETHTOOL_GSTATS;
-               edata.n_stats = (len < CAS_NUM_STAT_KEYS) ?
-                       len : CAS_NUM_STAT_KEYS;
-               if (copy_to_user(ep_user, &edata, sizeof (edata)))
-                       return -EFAULT;
-
-               tmp = kmalloc(sizeof(u64)*CAS_NUM_STAT_KEYS, GFP_KERNEL);
-               if (tmp) {
-                       tmp[i++] = stats->collisions;
-                       tmp[i++] = stats->rx_bytes;
-                       tmp[i++] = stats->rx_crc_errors;
-                       tmp[i++] = stats->rx_dropped;
-                       tmp[i++] = stats->rx_errors;
-                       tmp[i++] = stats->rx_fifo_errors;
-                       tmp[i++] = stats->rx_frame_errors;
-                       tmp[i++] = stats->rx_length_errors;
-                       tmp[i++] = stats->rx_over_errors;
-                       tmp[i++] = stats->rx_packets;
-                       tmp[i++] = stats->tx_aborted_errors;
-                       tmp[i++] = stats->tx_bytes;
-                       tmp[i++] = stats->tx_dropped;
-                       tmp[i++] = stats->tx_errors;
-                       tmp[i++] = stats->tx_fifo_errors;
-                       tmp[i++] = stats->tx_packets;
-                       BUG_ON(i != CAS_NUM_STAT_KEYS);
-
-                       i = copy_to_user(ep_user + sizeof(edata),
-                                        tmp, sizeof(u64)*edata.n_stats);
-                       kfree(tmp);
-               } else {
-                       return -ENOMEM;
-               }
-               if (i)
-                       return -EFAULT;
-               return 0;
-       }
-       }
+static void cas_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+        memcpy(data, &ethtool_cassini_statnames, 
+                                        CAS_NUM_STAT_KEYS * ETH_GSTRING_LEN);
+}
 
-       return -EOPNOTSUPP;
+static void cas_get_ethtool_stats(struct net_device *dev,
+                                     struct ethtool_stats *estats, u64 *data)
+{
+       struct cas *cp = netdev_priv(dev);
+       struct net_device_stats *stats = cas_get_stats(cp->dev);
+       int i = 0;
+       data[i++] = stats->collisions;
+       data[i++] = stats->rx_bytes;
+       data[i++] = stats->rx_crc_errors;
+       data[i++] = stats->rx_dropped;
+       data[i++] = stats->rx_errors;
+       data[i++] = stats->rx_fifo_errors;
+       data[i++] = stats->rx_frame_errors;
+       data[i++] = stats->rx_length_errors;
+       data[i++] = stats->rx_over_errors;
+       data[i++] = stats->rx_packets;
+       data[i++] = stats->tx_aborted_errors;
+       data[i++] = stats->tx_bytes;
+       data[i++] = stats->tx_dropped;
+       data[i++] = stats->tx_errors;
+       data[i++] = stats->tx_fifo_errors;
+       data[i++] = stats->tx_packets;
+       BUG_ON(i != CAS_NUM_STAT_KEYS);
 }
 
+static struct ethtool_ops cas_ethtool_ops = {
+       .get_drvinfo            = cas_get_drvinfo,
+       .get_settings           = cas_get_settings,
+       .set_settings           = cas_set_settings,
+       .nway_reset             = cas_nway_reset,
+       .get_link               = cas_get_link,
+       .get_msglevel           = cas_get_msglevel,
+       .set_msglevel           = cas_set_msglevel,
+       .get_regs_len           = cas_get_regs_len,
+       .get_regs               = cas_get_regs,
+       .get_stats_count        = cas_get_stats_count,
+       .get_strings            = cas_get_strings,
+       .get_ethtool_stats      = cas_get_ethtool_stats,
+};
+
 static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct cas *cp = netdev_priv(dev);
@@ -4878,15 +4834,11 @@ static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        unsigned long flags;
        int rc = -EOPNOTSUPP;
        
-       /* Hold the PM semaphore while doing ioctl's or we may collide
+       /* Hold the PM mutex while doing ioctl's or we may collide
         * with open/close and power management and oops.
         */
-       down(&cp->pm_sem);
+       mutex_lock(&cp->pm_mutex);
        switch (cmd) {
-       case SIOCETHTOOL:
-               rc = cas_ethtool_ioctl(dev, ifr->ifr_data);
-               break;
-
        case SIOCGMIIPHY:               /* Get address of MII PHY in use. */
                data->phy_id = cp->phy_addr;
                /* Fallthrough... */
@@ -4915,7 +4867,7 @@ static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                break;
        };
 
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
        return rc;
 }
 
@@ -4923,7 +4875,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
                                  const struct pci_device_id *ent)
 {
        static int cas_version_printed = 0;
-       unsigned long casreg_base, casreg_len;
+       unsigned long casreg_len;
        struct net_device *dev;
        struct cas *cp;
        int i, err, pci_using_dac;
@@ -4935,13 +4887,13 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
 
        err = pci_enable_device(pdev);
        if (err) {
-               printk(KERN_ERR PFX "Cannot enable PCI device, "
+               dev_printk(KERN_ERR, &pdev->dev, "Cannot enable PCI device, "
                       "aborting.\n");
                return err;
        }
 
        if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-               printk(KERN_ERR PFX "Cannot find proper PCI device "
+               dev_printk(KERN_ERR, &pdev->dev, "Cannot find proper PCI device "
                       "base address, aborting.\n");
                err = -ENODEV;
                goto err_out_disable_pdev;
@@ -4949,7 +4901,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
 
        dev = alloc_etherdev(sizeof(*cp));
        if (!dev) {
-               printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
+               dev_printk(KERN_ERR, &pdev->dev, "Etherdev alloc failed, aborting.\n");
                err = -ENOMEM;
                goto err_out_disable_pdev;
        }
@@ -4958,7 +4910,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
 
        err = pci_request_regions(pdev, dev->name);
        if (err) {
-               printk(KERN_ERR PFX "Cannot obtain PCI resources, "
+               dev_printk(KERN_ERR, &pdev->dev, "Cannot obtain PCI resources, "
                       "aborting.\n");
                goto err_out_free_netdev;
        }
@@ -4989,7 +4941,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
                if (pci_write_config_byte(pdev, 
                                          PCI_CACHE_LINE_SIZE, 
                                          cas_cacheline_size)) {
-                       printk(KERN_ERR PFX "Could not set PCI cache "
+                       dev_printk(KERN_ERR, &pdev->dev, "Could not set PCI cache "
                               "line size\n");
                        goto err_write_cacheline;
                }
@@ -5003,7 +4955,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
                err = pci_set_consistent_dma_mask(pdev,
                                                  DMA_64BIT_MASK);
                if (err < 0) {
-                       printk(KERN_ERR PFX "Unable to obtain 64-bit DMA "
+                       dev_printk(KERN_ERR, &pdev->dev, "Unable to obtain 64-bit DMA "
                               "for consistent allocations\n");
                        goto err_out_free_res;
                }
@@ -5011,14 +4963,13 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
        } else {
                err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
                if (err) {
-                       printk(KERN_ERR PFX "No usable DMA configuration, "
+                       dev_printk(KERN_ERR, &pdev->dev, "No usable DMA configuration, "
                               "aborting.\n");
                        goto err_out_free_res;
                }
                pci_using_dac = 0;
        }
 
-       casreg_base = pci_resource_start(pdev, 0);
        casreg_len = pci_resource_len(pdev, 0);
 
        cp = netdev_priv(dev);
@@ -5042,7 +4993,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
                spin_lock_init(&cp->tx_lock[i]);
        }
        spin_lock_init(&cp->stat_lock[N_TX_RINGS]);
-       init_MUTEX(&cp->pm_sem);
+       mutex_init(&cp->pm_mutex);
 
        init_timer(&cp->link_timer);
        cp->link_timer.function = cas_link_timer;
@@ -5070,9 +5021,9 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
        cp->timer_ticks = 0;
 
        /* give us access to cassini registers */
-       cp->regs = ioremap(casreg_base, casreg_len);
+       cp->regs = pci_iomap(pdev, 0, casreg_len);
        if (cp->regs == 0UL) {
-               printk(KERN_ERR PFX "Cannot map device registers, "
+               dev_printk(KERN_ERR, &pdev->dev, "Cannot map device registers, "
                       "aborting.\n");
                goto err_out_free_res;
        }
@@ -5089,7 +5040,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
                pci_alloc_consistent(pdev, sizeof(struct cas_init_block),
                                     &cp->block_dvma);
        if (!cp->init_block) {
-               printk(KERN_ERR PFX "Cannot allocate init block, "
+               dev_printk(KERN_ERR, &pdev->dev, "Cannot allocate init block, "
                       "aborting.\n");
                goto err_out_iounmap;
        }
@@ -5112,6 +5063,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
        dev->get_stats = cas_get_stats;
        dev->set_multicast_list = cas_set_multicast;
        dev->do_ioctl = cas_ioctl;
+       dev->ethtool_ops = &cas_ethtool_ops;
        dev->tx_timeout = cas_tx_timeout;
        dev->watchdog_timeo = CAS_TX_TIMEOUT;
        dev->change_mtu = cas_change_mtu;
@@ -5133,7 +5085,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
                dev->features |= NETIF_F_HIGHDMA;
 
        if (register_netdev(dev)) {
-               printk(KERN_ERR PFX "Cannot register net device, "
+               dev_printk(KERN_ERR, &pdev->dev, "Cannot register net device, "
                       "aborting.\n");
                goto err_out_free_consistent;
        }
@@ -5163,12 +5115,12 @@ err_out_free_consistent:
                            cp->init_block, cp->block_dvma);
 
 err_out_iounmap:
-       down(&cp->pm_sem);
+       mutex_lock(&cp->pm_mutex);
        if (cp->hw_running)
                cas_shutdown(cp);
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
 
-       iounmap(cp->regs);
+       pci_iounmap(pdev, cp->regs);
 
 
 err_out_free_res:
@@ -5199,11 +5151,11 @@ static void __devexit cas_remove_one(struct pci_dev *pdev)
        cp = netdev_priv(dev);
        unregister_netdev(dev);
 
-       down(&cp->pm_sem);
+       mutex_lock(&cp->pm_mutex);
        flush_scheduled_work();
        if (cp->hw_running)
                cas_shutdown(cp);
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
 
 #if 1
        if (cp->orig_cacheline_size) {
@@ -5216,7 +5168,7 @@ static void __devexit cas_remove_one(struct pci_dev *pdev)
 #endif
        pci_free_consistent(pdev, sizeof(struct cas_init_block),
                            cp->init_block, cp->block_dvma);
-       iounmap(cp->regs);
+       pci_iounmap(pdev, cp->regs);
        free_netdev(dev);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
@@ -5230,10 +5182,7 @@ static int cas_suspend(struct pci_dev *pdev, pm_message_t state)
        struct cas *cp = netdev_priv(dev);
        unsigned long flags;
 
-       /* We hold the PM semaphore during entire driver
-        * sleep time
-        */
-       down(&cp->pm_sem);
+       mutex_lock(&cp->pm_mutex);
        
        /* If the driver is opened, we stop the DMA */
        if (cp->opened) {
@@ -5253,6 +5202,7 @@ static int cas_suspend(struct pci_dev *pdev, pm_message_t state)
 
        if (cp->hw_running)
                cas_shutdown(cp);
+       mutex_unlock(&cp->pm_mutex);
 
        return 0;
 }
@@ -5264,6 +5214,7 @@ static int cas_resume(struct pci_dev *pdev)
 
        printk(KERN_INFO "%s: resuming\n", dev->name);
 
+       mutex_lock(&cp->pm_mutex);
        cas_hard_reset(cp);
        if (cp->opened) {
                unsigned long flags;
@@ -5276,7 +5227,7 @@ static int cas_resume(struct pci_dev *pdev)
 
                netif_device_attach(dev);
        }
-       up(&cp->pm_sem);
+       mutex_unlock(&cp->pm_mutex);
        return 0;
 }
 #endif /* CONFIG_PM */