[PATCH] drivers/net/Kconfig: indentation fix
[safe/jmp/linux-2.6] / drivers / net / skge.c
index 290d6aa..b538e30 100644 (file)
@@ -7,7 +7,7 @@
  * of the original driver such as link fail-over and link management because
  * those should be done at higher levels.
  *
- * Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
+ * Copyright (C) 2004, 2005 Stephen Hemminger <shemminger@osdl.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@
  */
 
 #include <linux/config.h>
+#include <linux/in.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/delay.h>
 #include <linux/crc32.h>
 #include <linux/dma-mapping.h>
+#include <linux/mii.h>
 #include <asm/irq.h>
 
 #include "skge.h"
 
 #define DRV_NAME               "skge"
-#define DRV_VERSION            "0.6"
+#define DRV_VERSION            "1.3"
 #define PFX                    DRV_NAME " "
 
 #define DEFAULT_TX_RING_SIZE   128
 #define DEFAULT_RX_RING_SIZE   512
 #define MAX_TX_RING_SIZE       1024
 #define MAX_RX_RING_SIZE       4096
+#define RX_COPY_THRESHOLD      128
+#define RX_BUF_SIZE            1536
 #define PHY_RETRIES            1000
 #define ETH_JUMBO_MTU          9000
 #define TX_WATCHDOG            (5 * HZ)
 #define NAPI_WEIGHT            64
-#define BLINK_HZ               (HZ/4)
+#define BLINK_MS               250
 
 MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");
 MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
@@ -73,28 +77,26 @@ static const struct pci_device_id skge_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940B) },
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_GE) },
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_YU) },
-       { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx  */
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T), },
        { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4320) },
        { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5005) }, /* Belkin */
        { PCI_DEVICE(PCI_VENDOR_ID_CNET, PCI_DEVICE_ID_CNET_GIGACARD) },
-       { PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1032) },
        { PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1064) },
+       { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015, },
        { 0 }
 };
 MODULE_DEVICE_TABLE(pci, skge_id_table);
 
 static int skge_up(struct net_device *dev);
 static int skge_down(struct net_device *dev);
+static void skge_phy_reset(struct skge_port *skge);
 static void skge_tx_clean(struct skge_port *skge);
-static void xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
-static void gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
+static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
+static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
 static void genesis_get_stats(struct skge_port *skge, u64 *data);
 static void yukon_get_stats(struct skge_port *skge, u64 *data);
 static void yukon_init(struct skge_hw *hw, int port);
-static void yukon_reset(struct skge_hw *hw, int port);
 static void genesis_mac_init(struct skge_hw *hw, int port);
-static void genesis_reset(struct skge_hw *hw, int port);
 static void genesis_link_up(struct skge_port *skge);
 
 /* Avoid conditionals by using array */
@@ -104,44 +106,31 @@ static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F };
 static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F };
 static const u32 portirqmask[] = { IS_PORT_1, IS_PORT_2 };
 
-/* Don't need to look at whole 16K.
- * last interesting register is descriptor poll timer.
- */
-#define SKGE_REGS_LEN  (29*128)
-
 static int skge_get_regs_len(struct net_device *dev)
 {
-       return SKGE_REGS_LEN;
+       return 0x4000;
 }
 
 /*
- * Returns copy of control register region
- * I/O region is divided into banks and certain regions are unreadable
+ * Returns copy of whole control register region
+ * Note: skip RAM address register because accessing it will
+ *      cause bus hangs!
  */
 static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs,
                          void *p)
 {
        const struct skge_port *skge = netdev_priv(dev);
-       unsigned long offs;
        const void __iomem *io = skge->hw->regs;
-       static const unsigned long bankmap
-               = (1<<0) | (1<<2) | (1<<8) | (1<<9)
-                 | (1<<12) | (1<<13) | (1<<14) | (1<<15) | (1<<16)
-                 | (1<<17) | (1<<20) | (1<<21) | (1<<22) | (1<<23)
-                 | (1<<24)  | (1<<25) | (1<<26) | (1<<27) | (1<<28);
 
        regs->version = 1;
-       for (offs = 0; offs < regs->len; offs += 128) {
-               u32 len = min_t(u32, 128, regs->len - offs);
+       memset(p, 0, regs->len);
+       memcpy_fromio(p, io, B3_RAM_ADDR);
 
-               if (bankmap & (1<<(offs/128)))
-                       memcpy_fromio(p + offs, io + offs, len);
-               else
-                       memset(p + offs, 0, len);
-       }
+       memcpy_fromio(p + B3_RI_WTO_R1, io + B3_RI_WTO_R1,
+                     regs->len - B3_RI_WTO_R1);
 }
 
-/* Wake on Lan only supported on Yukon chps with rev 1 or above */
+/* Wake on Lan only supported on Yukon chips with rev 1 or above */
 static int wol_supported(const struct skge_hw *hw)
 {
        return !((hw->chip_id == CHIP_ID_GENESIS ||
@@ -181,14 +170,14 @@ static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
        return 0;
 }
 
-/* Determine supported/adverised modes based on hardware.
- * Note: ethtoool ADVERTISED_xxx == SUPPORTED_xxx
+/* Determine supported/advertised modes based on hardware.
+ * Note: ethtool ADVERTISED_xxx == SUPPORTED_xxx
  */
 static u32 skge_supported_modes(const struct skge_hw *hw)
 {
        u32 supported;
 
-       if (iscopper(hw)) {
+       if (hw->copper) {
                supported = SUPPORTED_10baseT_Half
                        | SUPPORTED_10baseT_Full
                        | SUPPORTED_100baseT_Half
@@ -221,7 +210,7 @@ static int skge_get_settings(struct net_device *dev,
        ecmd->transceiver = XCVR_INTERNAL;
        ecmd->supported = skge_supported_modes(hw);
 
-       if (iscopper(hw)) {
+       if (hw->copper) {
                ecmd->port = PORT_TP;
                ecmd->phy_address = hw->phy_addr;
        } else
@@ -247,7 +236,7 @@ static int skge_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
        } else {
                u32 setting;
 
-               switch(ecmd->speed) {
+               switch (ecmd->speed) {
                case SPEED_1000:
                        if (ecmd->duplex == DUPLEX_FULL)
                                setting = SUPPORTED_1000baseT_Full;
@@ -287,10 +276,9 @@ static int skge_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
        skge->autoneg = ecmd->autoneg;
        skge->advertising = ecmd->advertising;
 
-       if (netif_running(dev)) {
-               skge_down(dev);
-               skge_up(dev);
-       }
+       if (netif_running(dev))
+               skge_phy_reset(skge);
+
        return (0);
 }
 
@@ -410,6 +398,7 @@ static int skge_set_ring_param(struct net_device *dev,
                               struct ethtool_ringparam *p)
 {
        struct skge_port *skge = netdev_priv(dev);
+       int err;
 
        if (p->rx_pending == 0 || p->rx_pending > MAX_RX_RING_SIZE ||
            p->tx_pending == 0 || p->tx_pending > MAX_TX_RING_SIZE)
@@ -420,7 +409,9 @@ static int skge_set_ring_param(struct net_device *dev,
 
        if (netif_running(dev)) {
                skge_down(dev);
-               skge_up(dev);
+               err = skge_up(dev);
+               if (err)
+                       dev_close(dev);
        }
 
        return 0;
@@ -441,21 +432,11 @@ static void skge_set_msglevel(struct net_device *netdev, u32 value)
 static int skge_nway_reset(struct net_device *dev)
 {
        struct skge_port *skge = netdev_priv(dev);
-       struct skge_hw *hw = skge->hw;
-       int port = skge->port;
 
        if (skge->autoneg != AUTONEG_ENABLE || !netif_running(dev))
                return -EINVAL;
 
-       spin_lock_bh(&hw->phy_lock);
-       if (hw->chip_id == CHIP_ID_GENESIS) {
-               genesis_reset(hw, port);
-               genesis_mac_init(hw, port);
-       } else {
-               yukon_reset(hw, port);
-               yukon_init(hw, port);
-       }
-       spin_unlock_bh(&hw->phy_lock);
+       skge_phy_reset(skge);
        return 0;
 }
 
@@ -527,10 +508,8 @@ static int skge_set_pauseparam(struct net_device *dev,
        else
                skge->flow_control = FLOW_MODE_NONE;
 
-       if (netif_running(dev)) {
-               skge_down(dev);
-               skge_up(dev);
-       }
+       if (netif_running(dev))
+               skge_phy_reset(skge);
        return 0;
 }
 
@@ -543,13 +522,13 @@ static inline u32 hwkhz(const struct skge_hw *hw)
                return 78215; /* or:  78.125 MHz */
 }
 
-/* Chip hz to microseconds */
+/* Chip HZ to microseconds */
 static inline u32 skge_clk2usec(const struct skge_hw *hw, u32 ticks)
 {
        return (ticks * 1000) / hwkhz(hw);
 }
 
-/* Microseconds to chip hz */
+/* Microseconds to chip HZ */
 static inline u32 skge_usecs2clk(const struct skge_hw *hw, u32 usec)
 {
        return hwkhz(hw) * usec / 1000;
@@ -618,84 +597,98 @@ static int skge_set_coalesce(struct net_device *dev,
        return 0;
 }
 
-static void skge_led_on(struct skge_hw *hw, int port)
+enum led_mode { LED_MODE_OFF, LED_MODE_ON, LED_MODE_TST };
+static void skge_led(struct skge_port *skge, enum led_mode mode)
 {
+       struct skge_hw *hw = skge->hw;
+       int port = skge->port;
+
+       spin_lock_bh(&hw->phy_lock);
        if (hw->chip_id == CHIP_ID_GENESIS) {
-               skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON);
-               skge_write8(hw, B0_LED, LED_STAT_ON);
+               switch (mode) {
+               case LED_MODE_OFF:
+                       xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+                       skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
+                       skge_write32(hw, SK_REG(port, RX_LED_VAL), 0);
+                       skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_T_OFF);
+                       break;
 
-               skge_write8(hw, SK_REG(port, RX_LED_TST), LED_T_ON);
-               skge_write32(hw, SK_REG(port, RX_LED_VAL), 100);
-               skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
+               case LED_MODE_ON:
+                       skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON);
+                       skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_LINKSYNC_ON);
 
-               /* For Broadcom Phy only */
-               xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
-       } else {
-               gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
-               gm_phy_write(hw, port, PHY_MARV_LED_OVER,
-                                 PHY_M_LED_MO_DUP(MO_LED_ON)  |
-                                 PHY_M_LED_MO_10(MO_LED_ON)   |
-                                 PHY_M_LED_MO_100(MO_LED_ON)  |
-                                 PHY_M_LED_MO_1000(MO_LED_ON) |
-                                 PHY_M_LED_MO_RX(MO_LED_ON));
-       }
-}
+                       skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
+                       skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
 
-static void skge_led_off(struct skge_hw *hw, int port)
-{
-       if (hw->chip_id == CHIP_ID_GENESIS) {
-               skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
-               skge_write8(hw, B0_LED, LED_STAT_OFF);
+                       break;
 
-               skge_write32(hw, SK_REG(port, RX_LED_VAL), 0);
-               skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_T_OFF);
+               case LED_MODE_TST:
+                       skge_write8(hw, SK_REG(port, RX_LED_TST), LED_T_ON);
+                       skge_write32(hw, SK_REG(port, RX_LED_VAL), 100);
+                       skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
 
-               /* Broadcom only */
-               xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+                       xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
+                       break;
+               }
        } else {
-               gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
-               gm_phy_write(hw, port, PHY_MARV_LED_OVER,
-                                 PHY_M_LED_MO_DUP(MO_LED_OFF)  |
-                                 PHY_M_LED_MO_10(MO_LED_OFF)   |
-                                 PHY_M_LED_MO_100(MO_LED_OFF)  |
-                                 PHY_M_LED_MO_1000(MO_LED_OFF) |
-                                 PHY_M_LED_MO_RX(MO_LED_OFF));
+               switch (mode) {
+               case LED_MODE_OFF:
+                       gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
+                       gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+                                    PHY_M_LED_MO_DUP(MO_LED_OFF)  |
+                                    PHY_M_LED_MO_10(MO_LED_OFF)   |
+                                    PHY_M_LED_MO_100(MO_LED_OFF)  |
+                                    PHY_M_LED_MO_1000(MO_LED_OFF) |
+                                    PHY_M_LED_MO_RX(MO_LED_OFF));
+                       break;
+               case LED_MODE_ON:
+                       gm_phy_write(hw, port, PHY_MARV_LED_CTRL,
+                                    PHY_M_LED_PULS_DUR(PULS_170MS) |
+                                    PHY_M_LED_BLINK_RT(BLINK_84MS) |
+                                    PHY_M_LEDC_TX_CTRL |
+                                    PHY_M_LEDC_DP_CTRL);
+
+                       gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+                                    PHY_M_LED_MO_RX(MO_LED_OFF) |
+                                    (skge->speed == SPEED_100 ?
+                                     PHY_M_LED_MO_100(MO_LED_ON) : 0));
+                       break;
+               case LED_MODE_TST:
+                       gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
+                       gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+                                    PHY_M_LED_MO_DUP(MO_LED_ON)  |
+                                    PHY_M_LED_MO_10(MO_LED_ON)   |
+                                    PHY_M_LED_MO_100(MO_LED_ON)  |
+                                    PHY_M_LED_MO_1000(MO_LED_ON) |
+                                    PHY_M_LED_MO_RX(MO_LED_ON));
+               }
        }
-}
-
-static void skge_blink_timer(unsigned long data)
-{
-       struct skge_port *skge = (struct skge_port *) data;
-       struct skge_hw *hw = skge->hw;
-       unsigned long flags;
-
-       spin_lock_irqsave(&hw->phy_lock, flags);
-       if (skge->blink_on)
-               skge_led_on(hw, skge->port);
-       else
-               skge_led_off(hw, skge->port);
-       spin_unlock_irqrestore(&hw->phy_lock, flags);
-
-       skge->blink_on = !skge->blink_on;
-       mod_timer(&skge->led_blink, jiffies + BLINK_HZ);
+       spin_unlock_bh(&hw->phy_lock);
 }
 
 /* blink LED's for finding board */
 static int skge_phys_id(struct net_device *dev, u32 data)
 {
        struct skge_port *skge = netdev_priv(dev);
+       unsigned long ms;
+       enum led_mode mode = LED_MODE_TST;
 
        if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))
-               data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ);
+               ms = jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT / HZ) * 1000;
+       else
+               ms = data * 1000;
 
-       /* start blinking */
-       skge->blink_on = 1;
-       mod_timer(&skge->led_blink, jiffies+1);
+       while (ms > 0) {
+               skge_led(skge, mode);
+               mode ^= LED_MODE_TST;
 
-       msleep_interruptible(data * 1000);
-       del_timer_sync(&skge->led_blink);
+               if (msleep_interruptible(BLINK_MS))
+                       break;
+               ms -= BLINK_MS;
+       }
 
-       skge_led_off(skge->hw, skge->port);
+       /* back to regular LED state */
+       skge_led(skge, netif_running(dev) ? LED_MODE_ON : LED_MODE_OFF);
 
        return 0;
 }
@@ -728,6 +721,7 @@ static struct ethtool_ops skge_ethtool_ops = {
        .phys_id        = skge_phys_id,
        .get_stats_count = skge_get_stats_count,
        .get_ethtool_stats = skge_get_ethtool_stats,
+       .get_perm_addr  = ethtool_op_get_perm_addr,
 };
 
 /*
@@ -746,6 +740,7 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base)
 
        for (i = 0, e = ring->start, d = vaddr; i < ring->count; i++, e++, d++) {
                e->desc = d;
+               e->skb = NULL;
                if (i == ring->count - 1) {
                        e->next = ring->start;
                        d->next_offset = base;
@@ -759,25 +754,13 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base)
        return 0;
 }
 
-/* Setup buffer for receiving */
-static inline int skge_rx_alloc(struct skge_port *skge,
-                               struct skge_element *e)
+/* Allocate and setup a new buffer for receiving */
+static void skge_rx_setup(struct skge_port *skge, struct skge_element *e,
+                         struct sk_buff *skb, unsigned int bufsize)
 {
-       unsigned long bufsize = skge->netdev->mtu + ETH_HLEN; /* VLAN? */
        struct skge_rx_desc *rd = e->desc;
-       struct sk_buff *skb;
        u64 map;
 
-       skb = dev_alloc_skb(bufsize + NET_IP_ALIGN);
-       if (unlikely(!skb)) {
-               printk(KERN_DEBUG PFX "%s: out of memory for receive\n",
-                      skge->netdev->name);
-               return -ENOMEM;
-       }
-
-       skb->dev = skge->netdev;
-       skb_reserve(skb, NET_IP_ALIGN);
-
        map = pci_map_single(skge->hw->pdev, skb->data, bufsize,
                             PCI_DMA_FROMDEVICE);
 
@@ -794,59 +777,77 @@ static inline int skge_rx_alloc(struct skge_port *skge,
        rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize;
        pci_unmap_addr_set(e, mapaddr, map);
        pci_unmap_len_set(e, maplen, bufsize);
-       return 0;
 }
 
-/* Free all unused buffers in receive ring, assumes receiver stopped */
+/* Resume receiving using existing skb,
+ * Note: DMA address is not changed by chip.
+ *      MTU not changed while receiver active.
+ */
+static void skge_rx_reuse(struct skge_element *e, unsigned int size)
+{
+       struct skge_rx_desc *rd = e->desc;
+
+       rd->csum2 = 0;
+       rd->csum2_start = ETH_HLEN;
+
+       wmb();
+
+       rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | size;
+}
+
+
+/* Free all  buffers in receive ring, assumes receiver stopped */
 static void skge_rx_clean(struct skge_port *skge)
 {
        struct skge_hw *hw = skge->hw;
        struct skge_ring *ring = &skge->rx_ring;
        struct skge_element *e;
 
-       for (e = ring->to_clean; e != ring->to_use; e = e->next) {
+       e = ring->start;
+       do {
                struct skge_rx_desc *rd = e->desc;
                rd->control = 0;
-
-               pci_unmap_single(hw->pdev,
-                                pci_unmap_addr(e, mapaddr),
-                                pci_unmap_len(e, maplen),
-                                PCI_DMA_FROMDEVICE);
-               dev_kfree_skb(e->skb);
-               e->skb = NULL;
-       }
-       ring->to_clean = e;
+               if (e->skb) {
+                       pci_unmap_single(hw->pdev,
+                                        pci_unmap_addr(e, mapaddr),
+                                        pci_unmap_len(e, maplen),
+                                        PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(e->skb);
+                       e->skb = NULL;
+               }
+       } while ((e = e->next) != ring->start);
 }
 
+
 /* Allocate buffers for receive ring
- * For receive: to_use   is refill location
- *              to_clean is next received frame.
- *
- * if (to_use == to_clean)
- *      then ring all frames in ring need buffers
- * if (to_use->next == to_clean)
- *      then ring all frames in ring have buffers
+ * For receive:  to_clean is next received frame.
  */
 static int skge_rx_fill(struct skge_port *skge)
 {
        struct skge_ring *ring = &skge->rx_ring;
        struct skge_element *e;
-       int ret = 0;
 
-       for (e = ring->to_use; e->next != ring->to_clean; e = e->next) {
-               if (skge_rx_alloc(skge, e)) {
-                       ret = 1;
-                       break;
-               }
+       e = ring->start;
+       do {
+               struct sk_buff *skb;
 
-       }
-       ring->to_use = e;
+               skb = dev_alloc_skb(skge->rx_buf_size + NET_IP_ALIGN);
+               if (!skb)
+                       return -ENOMEM;
 
-       return ret;
+               skb_reserve(skb, NET_IP_ALIGN);
+               skge_rx_setup(skge, e, skb, skge->rx_buf_size);
+       } while ( (e = e->next) != ring->start);
+
+       ring->to_clean = ring->start;
+       return 0;
 }
 
 static void skge_link_up(struct skge_port *skge)
 {
+       skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG),
+                   LED_BLK_OFF|LED_SYNC_OFF|LED_ON);
+
        netif_carrier_on(skge->netdev);
        if (skge->tx_avail > MAX_SKB_FRAGS + 1)
                netif_wake_queue(skge->netdev);
@@ -865,6 +866,7 @@ static void skge_link_up(struct skge_port *skge)
 
 static void skge_link_down(struct skge_port *skge)
 {
+       skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
        netif_carrier_off(skge->netdev);
        netif_stop_queue(skge->netdev);
 
@@ -872,32 +874,37 @@ static void skge_link_down(struct skge_port *skge)
                printk(KERN_INFO PFX "%s: Link is down.\n", skge->netdev->name);
 }
 
-static u16 xm_phy_read(struct skge_hw *hw, int port, u16 reg)
+static int __xm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
 {
        int i;
-       u16 v;
 
        xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr);
-       v = xm_read16(hw, port, XM_PHY_DATA);
+       xm_read16(hw, port, XM_PHY_DATA);
 
        /* Need to wait for external PHY */
        for (i = 0; i < PHY_RETRIES; i++) {
                udelay(1);
-               if (xm_read16(hw, port, XM_MMU_CMD)
-                   & XM_MMU_PHY_RDY)
+               if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY)
                        goto ready;
        }
 
-       printk(KERN_WARNING PFX "%s: phy read timed out\n",
-              hw->dev[port]->name);
-       return 0;
+       return -ETIMEDOUT;
  ready:
-       v = xm_read16(hw, port, XM_PHY_DATA);
+       *val = xm_read16(hw, port, XM_PHY_DATA);
 
+       return 0;
+}
+
+static u16 xm_phy_read(struct skge_hw *hw, int port, u16 reg)
+{
+       u16 v = 0;
+       if (__xm_phy_read(hw, port, reg, &v))
+               printk(KERN_WARNING PFX "%s: phy read timed out\n",
+                      hw->dev[port]->name);
        return v;
 }
 
-static void xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
+static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
 {
        int i;
 
@@ -907,19 +914,11 @@ static void xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
                        goto ready;
                udelay(1);
        }
-       printk(KERN_WARNING PFX "%s: phy write failed to come ready\n",
-              hw->dev[port]->name);
-
+       return -EIO;
 
  ready:
        xm_write16(hw, port, XM_PHY_DATA, val);
-       for (i = 0; i < PHY_RETRIES; i++) {
-               udelay(1);
-               if (!(xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY))
-                       return;
-       }
-       printk(KERN_WARNING PFX "%s: phy write timed out\n",
-                      hw->dev[port]->name);
+       return 0;
 }
 
 static void genesis_init(struct skge_hw *hw)
@@ -954,6 +953,8 @@ static void genesis_reset(struct skge_hw *hw, int port)
 {
        const u8 zero[8]  = { 0 };
 
+       skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+
        /* reset the statistics module */
        xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT);
        xm_write16(hw, port, XM_IMSK, 0xffff);  /* disable XMAC IRQs */
@@ -988,8 +989,6 @@ static void bcom_check_link(struct skge_hw *hw, int port)
        (void) xm_phy_read(hw, port, PHY_BCOM_STAT);
        status = xm_phy_read(hw, port, PHY_BCOM_STAT);
 
-       pr_debug("bcom_check_link status=0x%x\n", status);
-
        if ((status & PHY_ST_LSYNC) == 0) {
                u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
                cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
@@ -1012,7 +1011,7 @@ static void bcom_check_link(struct skge_hw *hw, int port)
                        }
 
                        /* Check Duplex mismatch */
-                       switch(aux & PHY_B_AS_AN_RES_MSK) {
+                       switch (aux & PHY_B_AS_AN_RES_MSK) {
                        case PHY_B_RES_1000FD:
                                skge->duplex = DUPLEX_FULL;
                                break;
@@ -1073,8 +1072,6 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
                { 0x17, 0x0013 }, { 0x15, 0x0A04 }, { 0x18, 0x0420 },
        };
 
-       pr_debug("bcom_phy_init\n");
-
        /* read Id from external PHY (all have the same address) */
        id1 = xm_phy_read(hw, port, PHY_XMAC_ID1);
 
@@ -1083,7 +1080,7 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
        r |=  XM_MMU_NO_PRE;
        xm_write16(hw, port, XM_MMU_CMD,r);
 
-       switch(id1) {
+       switch (id1) {
        case PHY_BCOM_ID1_C0:
                /*
                 * Workaround BCOM Errata for the C0 type.
@@ -1156,7 +1153,7 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
        xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, ext);
        xm_phy_write(hw, port, PHY_BCOM_CTRL, ctl);
 
-       /* Use link status change interrrupt */
+       /* Use link status change interrupt */
        xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
 
        bcom_check_link(hw, port);
@@ -1178,13 +1175,6 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
        xm_write16(hw, port, XM_STAT_CMD,
                        XM_SC_CLR_RXC | XM_SC_CLR_TXC);
 
-       /* initialize Rx, Tx and Link LED */
-       skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON);
-       skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_LINKSYNC_ON);
-
-       skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
-       skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
-
        /* Unreset the XMAC. */
        skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST);
 
@@ -1193,7 +1183,6 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
         * namely for the 1000baseTX cards that use the XMAC's
         * GMII mode.
         */
-       spin_lock_bh(&hw->phy_lock);
        /* Take external Phy out of reset */
        r = skge_read32(hw, B2_GP_IO);
        if (port == 0)
@@ -1203,9 +1192,8 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
 
        skge_write32(hw, B2_GP_IO, r);
        skge_read32(hw, B2_GP_IO);
-       spin_unlock_bh(&hw->phy_lock);
 
-       /* Enable GMII interfac */
+       /* Enable GMII interface */
        xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
 
        bcom_phy_init(skge, jumbo);
@@ -1256,7 +1244,7 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
         * that jumbo frames larger than 8192 bytes will be
         * truncated. Disabling all bad frame filtering causes
         * the RX FIFO to operate in streaming mode, in which
-        * case the XMAC will start transfering frames out of the
+        * case the XMAC will start transferring frames out of the
         * RX FIFO as soon as the FIFO threshold is reached.
         */
        xm_write32(hw, port, XM_MODE, XM_DEF_MODE);
@@ -1316,12 +1304,14 @@ static void genesis_stop(struct skge_port *skge)
        int port = skge->port;
        u32 reg;
 
+       genesis_reset(hw, port);
+
        /* Clear Tx packet arbiter timeout IRQ */
        skge_write16(hw, B3_PA_CTRL,
                     port == 0 ? PA_CLR_TO_TX1 : PA_CLR_TO_TX2);
 
        /*
-        * If the transfer stucks at the MAC the STOP command will not
+        * If the transfer sticks at the MAC the STOP command will not
         * terminate if we don't flush the XMAC's transmit FIFO !
         */
        xm_write32(hw, port, XM_MODE,
@@ -1398,42 +1388,6 @@ static void genesis_mac_intr(struct skge_hw *hw, int port)
        }
 }
 
-static void gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
-{
-       int i;
-
-       gma_write16(hw, port, GM_SMI_DATA, val);
-       gma_write16(hw, port, GM_SMI_CTRL,
-                        GM_SMI_CT_PHY_AD(hw->phy_addr) | GM_SMI_CT_REG_AD(reg));
-       for (i = 0; i < PHY_RETRIES; i++) {
-               udelay(1);
-
-               if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY))
-                       break;
-       }
-}
-
-static u16 gm_phy_read(struct skge_hw *hw, int port, u16 reg)
-{
-       int i;
-
-       gma_write16(hw, port, GM_SMI_CTRL,
-                        GM_SMI_CT_PHY_AD(hw->phy_addr)
-                        | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
-
-       for (i = 0; i < PHY_RETRIES; i++) {
-               udelay(1);
-               if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL)
-                       goto ready;
-       }
-
-       printk(KERN_WARNING PFX "%s: phy read timeout\n",
-              hw->dev[port]->name);
-       return 0;
- ready:
-       return gma_read16(hw, port, GM_SMI_DATA);
-}
-
 static void genesis_link_up(struct skge_port *skge)
 {
        struct skge_hw *hw = skge->hw;
@@ -1441,7 +1395,6 @@ static void genesis_link_up(struct skge_port *skge)
        u16 cmd;
        u32 mode, msk;
 
-       pr_debug("genesis_link_up\n");
        cmd = xm_read16(hw, port, XM_MMU_CMD);
 
        /*
@@ -1548,14 +1501,60 @@ static inline void bcom_phy_intr(struct skge_port *skge)
 
 }
 
-/* Marvell Phy Initailization */
+static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val)
+{
+       int i;
+
+       gma_write16(hw, port, GM_SMI_DATA, val);
+       gma_write16(hw, port, GM_SMI_CTRL,
+                        GM_SMI_CT_PHY_AD(hw->phy_addr) | GM_SMI_CT_REG_AD(reg));
+       for (i = 0; i < PHY_RETRIES; i++) {
+               udelay(1);
+
+               if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY))
+                       return 0;
+       }
+
+       printk(KERN_WARNING PFX "%s: phy write timeout\n",
+              hw->dev[port]->name);
+       return -EIO;
+}
+
+static int __gm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
+{
+       int i;
+
+       gma_write16(hw, port, GM_SMI_CTRL,
+                        GM_SMI_CT_PHY_AD(hw->phy_addr)
+                        | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
+
+       for (i = 0; i < PHY_RETRIES; i++) {
+               udelay(1);
+               if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL)
+                       goto ready;
+       }
+
+       return -ETIMEDOUT;
+ ready:
+       *val = gma_read16(hw, port, GM_SMI_DATA);
+       return 0;
+}
+
+static u16 gm_phy_read(struct skge_hw *hw, int port, u16 reg)
+{
+       u16 v = 0;
+       if (__gm_phy_read(hw, port, reg, &v))
+               printk(KERN_WARNING PFX "%s: phy read timeout\n",
+              hw->dev[port]->name);
+       return v;
+}
+
+/* Marvell Phy Initialization */
 static void yukon_init(struct skge_hw *hw, int port)
 {
        struct skge_port *skge = netdev_priv(hw->dev[port]);
        u16 ctrl, ct1000, adv;
-       u16 ledctrl, ledover;
 
-       pr_debug("yukon_init\n");
        if (skge->autoneg == AUTONEG_ENABLE) {
                u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
 
@@ -1580,7 +1579,7 @@ static void yukon_init(struct skge_hw *hw, int port)
        adv = PHY_AN_CSMA;
 
        if (skge->autoneg == AUTONEG_ENABLE) {
-               if (iscopper(hw)) {
+               if (hw->copper) {
                        if (skge->advertising & ADVERTISED_1000baseT_Full)
                                ct1000 |= PHY_M_1000C_AFD;
                        if (skge->advertising & ADVERTISED_1000baseT_Half)
@@ -1625,32 +1624,11 @@ static void yukon_init(struct skge_hw *hw, int port)
        gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv);
        gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
 
-       /* Setup Phy LED's */
-       ledctrl = PHY_M_LED_PULS_DUR(PULS_170MS);
-       ledover = 0;
-
-       ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) | PHY_M_LEDC_TX_CTRL;
-
-       /* turn off the Rx LED (LED_RX) */
-       ledover |= PHY_M_LED_MO_RX(MO_LED_OFF);
-
-       /* disable blink mode (LED_DUPLEX) on collisions */
-       ctrl |= PHY_M_LEDC_DP_CTRL;
-       gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
-
-       if (skge->autoneg == AUTONEG_DISABLE || skge->speed == SPEED_100) {
-               /* turn on 100 Mbps LED (LED_LINK100) */
-               ledover |= PHY_M_LED_MO_100(MO_LED_ON);
-       }
-
-       if (ledover)
-               gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
-
        /* Enable phy interrupt on autonegotiation complete (or link up) */
        if (skge->autoneg == AUTONEG_ENABLE)
-               gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
+               gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_MSK);
        else
-               gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+               gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_DEF_MSK);
 }
 
 static void yukon_reset(struct skge_hw *hw, int port)
@@ -1666,6 +1644,22 @@ static void yukon_reset(struct skge_hw *hw, int port)
                         | GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
 }
 
+/* Apparently, early versions of Yukon-Lite had wrong chip_id? */
+static int is_yukon_lite_a0(struct skge_hw *hw)
+{
+       u32 reg;
+       int ret;
+
+       if (hw->chip_id != CHIP_ID_YUKON)
+               return 0;
+
+       reg = skge_read32(hw, B2_FAR);
+       skge_write8(hw, B2_FAR + 3, 0xff);
+       ret = (skge_read8(hw, B2_FAR + 3) != 0);
+       skge_write32(hw, B2_FAR, reg);
+       return ret;
+}
+
 static void yukon_mac_init(struct skge_hw *hw, int port)
 {
        struct skge_port *skge = netdev_priv(hw->dev[port]);
@@ -1675,9 +1669,11 @@ static void yukon_mac_init(struct skge_hw *hw, int port)
 
        /* WA code for COMA mode -- set PHY reset */
        if (hw->chip_id == CHIP_ID_YUKON_LITE &&
-           hw->chip_rev == CHIP_REV_YU_LITE_A3)
-               skge_write32(hw, B2_GP_IO,
-                            (skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9));
+           hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+               reg = skge_read32(hw, B2_GP_IO);
+               reg |= GP_DIR_9 | GP_IO_9;
+               skge_write32(hw, B2_GP_IO, reg);
+       }
 
        /* hard reset */
        skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
@@ -1685,15 +1681,17 @@ static void yukon_mac_init(struct skge_hw *hw, int port)
 
        /* WA code for COMA mode -- clear PHY reset */
        if (hw->chip_id == CHIP_ID_YUKON_LITE &&
-           hw->chip_rev == CHIP_REV_YU_LITE_A3)
-               skge_write32(hw, B2_GP_IO,
-                            (skge_read32(hw, B2_GP_IO) | GP_DIR_9)
-                            & ~GP_IO_9);
+           hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
+               reg = skge_read32(hw, B2_GP_IO);
+               reg |= GP_DIR_9;
+               reg &= ~GP_IO_9;
+               skge_write32(hw, B2_GP_IO, reg);
+       }
 
        /* Set hardware config mode */
        reg = GPC_INT_POL_HI | GPC_DIS_FC | GPC_DIS_SLEEP |
                GPC_ENA_XC | GPC_ANEG_ADV_ALL_M | GPC_ENA_PAUSE;
-       reg |= iscopper(hw) ? GPC_HWCFG_GMII_COP : GPC_HWCFG_GMII_FIB;
+       reg |= hw->copper ? GPC_HWCFG_GMII_COP : GPC_HWCFG_GMII_FIB;
 
        /* Clear GMC reset */
        skge_write32(hw, SK_REG(port, GPHY_CTRL), reg | GPC_RST_SET);
@@ -1727,11 +1725,9 @@ static void yukon_mac_init(struct skge_hw *hw, int port)
        }
 
        gma_write16(hw, port, GM_GP_CTRL, reg);
-       skge_read16(hw, GMAC_IRQ_SRC);
+       skge_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
 
-       spin_lock_bh(&hw->phy_lock);
        yukon_init(hw, port);
-       spin_unlock_bh(&hw->phy_lock);
 
        /* MIB clear */
        reg = gma_read16(hw, port, GM_PHY_ADDR);
@@ -1779,37 +1775,62 @@ static void yukon_mac_init(struct skge_hw *hw, int port)
        /* Configure Rx MAC FIFO */
        skge_write16(hw, SK_REG(port, RX_GMF_FL_MSK), RX_FF_FL_DEF_MSK);
        reg = GMF_OPER_ON | GMF_RX_F_FL_ON;
-       if (hw->chip_id == CHIP_ID_YUKON_LITE &&
-           hw->chip_rev == CHIP_REV_YU_LITE_A3)
+
+       /* disable Rx GMAC FIFO Flush for YUKON-Lite Rev. A0 only */
+       if (is_yukon_lite_a0(hw))
                reg &= ~GMF_RX_F_FL_ON;
+
        skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR);
        skge_write16(hw, SK_REG(port, RX_GMF_CTRL_T), reg);
-       skge_write16(hw, SK_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF);
+       /*
+        * because Pause Packet Truncation in GMAC is not working
+        * we have to increase the Flush Threshold to 64 bytes
+        * in order to flush pause packets in Rx FIFO on Yukon-1
+        */
+       skge_write16(hw, SK_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF+1);
 
        /* Configure Tx MAC FIFO */
        skge_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR);
        skge_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
 }
 
+/* Go into power down mode */
+static void yukon_suspend(struct skge_hw *hw, int port)
+{
+       u16 ctrl;
+
+       ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+       ctrl |= PHY_M_PC_POL_R_DIS;
+       gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+       ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+       ctrl |= PHY_CT_RESET;
+       gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+       /* switch IEEE compatible power down mode on */
+       ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+       ctrl |= PHY_CT_PDOWN;
+       gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+}
+
 static void yukon_stop(struct skge_port *skge)
 {
        struct skge_hw *hw = skge->hw;
        int port = skge->port;
 
-       if (hw->chip_id == CHIP_ID_YUKON_LITE &&
-           hw->chip_rev == CHIP_REV_YU_LITE_A3) {
-               skge_write32(hw, B2_GP_IO,
-                            skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9);
-       }
+       skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+       yukon_reset(hw, port);
 
        gma_write16(hw, port, GM_GP_CTRL,
                         gma_read16(hw, port, GM_GP_CTRL)
-                        & ~(GM_GPCR_RX_ENA|GM_GPCR_RX_ENA));
+                        & ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA));
        gma_read16(hw, port, GM_GP_CTRL);
 
+       yukon_suspend(hw, port);
+
        /* set GPHY Control reset */
-       gma_write32(hw, port, GPHY_CTRL, GPC_RST_SET);
-       gma_write32(hw, port, GMAC_CTRL, GMC_RST_SET);
+       skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+       skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
 }
 
 static void yukon_get_stats(struct skge_port *skge, u64 *data)
@@ -1840,11 +1861,12 @@ static void yukon_mac_intr(struct skge_hw *hw, int port)
 
        if (status & GM_IS_RX_FF_OR) {
                ++skge->net_stats.rx_fifo_errors;
-               gma_write8(hw, port, RX_GMF_CTRL_T, GMF_CLI_RX_FO);
+               skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
        }
+
        if (status & GM_IS_TX_FF_UR) {
                ++skge->net_stats.tx_fifo_errors;
-               gma_write8(hw, port, TX_GMF_CTRL_T, GMF_CLI_TX_FU);
+               skge_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
        }
 
 }
@@ -1867,10 +1889,8 @@ static void yukon_link_up(struct skge_port *skge)
        int port = skge->port;
        u16 reg;
 
-       pr_debug("yukon_link_up\n");
-
        /* Enable Transmit FIFO Underrun */
-       skge_write8(hw, GMAC_IRQ_MSK, GMAC_DEF_MSK);
+       skge_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
 
        reg = gma_read16(hw, port, GM_GP_CTRL);
        if (skge->duplex == DUPLEX_FULL || skge->autoneg == AUTONEG_ENABLE)
@@ -1880,7 +1900,7 @@ static void yukon_link_up(struct skge_port *skge)
        reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
        gma_write16(hw, port, GM_GP_CTRL, reg);
 
-       gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+       gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_DEF_MSK);
        skge_link_up(skge);
 }
 
@@ -1888,12 +1908,13 @@ static void yukon_link_down(struct skge_port *skge)
 {
        struct skge_hw *hw = skge->hw;
        int port = skge->port;
+       u16 ctrl;
 
-       pr_debug("yukon_link_down\n");
        gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
-       gm_phy_write(hw, port, GM_GP_CTRL,
-                         gm_phy_read(hw, port, GM_GP_CTRL)
-                         & ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA));
+
+       ctrl = gma_read16(hw, port, GM_GP_CTRL);
+       ctrl &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
+       gma_write16(hw, port, GM_GP_CTRL, ctrl);
 
        if (skge->flow_control == FLOW_MODE_REM_SEND) {
                /* restore Asymmetric Pause bit */
@@ -1988,6 +2009,70 @@ static void yukon_phy_intr(struct skge_port *skge)
        /* XXX restart autonegotiation? */
 }
 
+static void skge_phy_reset(struct skge_port *skge)
+{
+       struct skge_hw *hw = skge->hw;
+       int port = skge->port;
+
+       netif_stop_queue(skge->netdev);
+       netif_carrier_off(skge->netdev);
+
+       spin_lock_bh(&hw->phy_lock);
+       if (hw->chip_id == CHIP_ID_GENESIS) {
+               genesis_reset(hw, port);
+               genesis_mac_init(hw, port);
+       } else {
+               yukon_reset(hw, port);
+               yukon_init(hw, port);
+       }
+       spin_unlock_bh(&hw->phy_lock);
+}
+
+/* Basic MII support */
+static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct mii_ioctl_data *data = if_mii(ifr);
+       struct skge_port *skge = netdev_priv(dev);
+       struct skge_hw *hw = skge->hw;
+       int err = -EOPNOTSUPP;
+
+       if (!netif_running(dev))
+               return -ENODEV; /* Phy still in reset */
+
+       switch(cmd) {
+       case SIOCGMIIPHY:
+               data->phy_id = hw->phy_addr;
+
+               /* fallthru */
+       case SIOCGMIIREG: {
+               u16 val = 0;
+               spin_lock_bh(&hw->phy_lock);
+               if (hw->chip_id == CHIP_ID_GENESIS)
+                       err = __xm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val);
+               else
+                       err = __gm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val);
+               spin_unlock_bh(&hw->phy_lock);
+               data->val_out = val;
+               break;
+       }
+
+       case SIOCSMIIREG:
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               spin_lock_bh(&hw->phy_lock);
+               if (hw->chip_id == CHIP_ID_GENESIS)
+                       err = xm_phy_write(hw, skge->port, data->reg_num & 0x1f,
+                                  data->val_in);
+               else
+                       err = gm_phy_write(hw, skge->port, data->reg_num & 0x1f,
+                                  data->val_in);
+               spin_unlock_bh(&hw->phy_lock);
+               break;
+       }
+       return err;
+}
+
 static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, size_t len)
 {
        u32 end;
@@ -2048,6 +2133,12 @@ static int skge_up(struct net_device *dev)
        if (netif_msg_ifup(skge))
                printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
 
+       if (dev->mtu > RX_BUF_SIZE)
+               skge->rx_buf_size = dev->mtu + ETH_HLEN + NET_IP_ALIGN;
+       else
+               skge->rx_buf_size = RX_BUF_SIZE;
+
+
        rx_size = skge->rx_ring.count * sizeof(struct skge_rx_desc);
        tx_size = skge->tx_ring.count * sizeof(struct skge_tx_desc);
        skge->mem_size = tx_size + rx_size;
@@ -2060,7 +2151,8 @@ static int skge_up(struct net_device *dev)
        if ((err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma)))
                goto free_pci_mem;
 
-       if (skge_rx_fill(skge))
+       err = skge_rx_fill(skge);
+       if (err)
                goto free_rx_ring;
 
        if ((err = skge_ring_alloc(&skge->tx_ring, skge->mem + rx_size,
@@ -2073,11 +2165,13 @@ static int skge_up(struct net_device *dev)
        hw->intr_mask |= portirqmask[port];
        skge_write32(hw, B0_IMSK, hw->intr_mask);
 
-       /* Initialze MAC */
+       /* Initialize MAC */
+       spin_lock_bh(&hw->phy_lock);
        if (hw->chip_id == CHIP_ID_GENESIS)
                genesis_mac_init(hw, port);
        else
                yukon_mac_init(hw, port);
+       spin_unlock_bh(&hw->phy_lock);
 
        /* Configure RAMbuffers */
        chunk = hw->ram_size / ((hw->ports + 1)*2);
@@ -2093,8 +2187,8 @@ static int skge_up(struct net_device *dev)
        /* Start receiver BMU */
        wmb();
        skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F);
+       skge_led(skge, LED_MODE_ON);
 
-       pr_debug("skge_up completed\n");
        return 0;
 
  free_rx_ring:
@@ -2102,6 +2196,7 @@ static int skge_up(struct net_device *dev)
        kfree(skge->rx_ring.start);
  free_pci_mem:
        pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma);
+       skge->mem = NULL;
 
        return err;
 }
@@ -2112,22 +2207,28 @@ static int skge_down(struct net_device *dev)
        struct skge_hw *hw = skge->hw;
        int port = skge->port;
 
+       if (skge->mem == NULL)
+               return 0;
+
        if (netif_msg_ifdown(skge))
                printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
 
        netif_stop_queue(dev);
 
-       del_timer_sync(&skge->led_blink);
+       skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
+       if (hw->chip_id == CHIP_ID_GENESIS)
+               genesis_stop(skge);
+       else
+               yukon_stop(skge);
+
+       hw->intr_mask &= ~portirqmask[skge->port];
+       skge_write32(hw, B0_IMSK, hw->intr_mask);
 
        /* Stop transmitter */
        skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP);
        skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
                     RB_RST_SET|RB_DIS_OP_MD);
 
-       if (hw->chip_id == CHIP_ID_GENESIS)
-               genesis_stop(skge);
-       else
-               yukon_stop(skge);
 
        /* Disable Force Sync bit and Enable Alloc bit */
        skge_write8(hw, SK_REG(port, TXA_CTRL),
@@ -2152,15 +2253,12 @@ static int skge_down(struct net_device *dev)
        if (hw->chip_id == CHIP_ID_GENESIS) {
                skge_write8(hw, SK_REG(port, TX_MFF_CTRL2), MFF_RST_SET);
                skge_write8(hw, SK_REG(port, RX_MFF_CTRL2), MFF_RST_SET);
-               skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_STOP);
-               skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_STOP);
        } else {
                skge_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
                skge_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
        }
 
-       /* turn off led's */
-       skge_write16(hw, B0_LED, LED_STAT_OFF);
+       skge_led(skge, LED_MODE_OFF);
 
        skge_tx_clean(skge);
        skge_rx_clean(skge);
@@ -2168,6 +2266,7 @@ static int skge_down(struct net_device *dev)
        kfree(skge->rx_ring.start);
        kfree(skge->tx_ring.start);
        pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma);
+       skge->mem = NULL;
        return 0;
 }
 
@@ -2195,11 +2294,13 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        }
 
        if (unlikely(skge->tx_avail < skb_shinfo(skb)->nr_frags +1)) {
-               netif_stop_queue(dev);
-               spin_unlock_irqrestore(&skge->tx_lock, flags);
+               if (!netif_queue_stopped(dev)) {
+                       netif_stop_queue(dev);
 
-               printk(KERN_WARNING PFX "%s: ring full when queue awake!\n",
-                      dev->name);
+                       printk(KERN_WARNING PFX "%s: ring full when queue awake!\n",
+                              dev->name);
+               }
+               spin_unlock_irqrestore(&skge->tx_lock, flags);
                return NETDEV_TX_BUSY;
        }
 
@@ -2215,14 +2316,12 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        td->dma_hi = map >> 32;
 
        if (skb->ip_summed == CHECKSUM_HW) {
-               const struct iphdr *ip
-                       = (const struct iphdr *) (skb->data + ETH_HLEN);
                int offset = skb->h.raw - skb->data;
 
                /* This seems backwards, but it is what the sk98lin
                 * does.  Looks like hardware is wrong?
                 */
-               if (ip->protocol == IPPROTO_UDP
+               if (skb->h.ipiph->protocol == IPPROTO_UDP
                    && hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON)
                        control = BMU_TCP_CHECK;
                else
@@ -2284,6 +2383,7 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
 
 static inline void skge_tx_free(struct skge_hw *hw, struct skge_element *e)
 {
+       /* This ring element can be skb or fragment */
        if (e->skb) {
                pci_unmap_single(hw->pdev,
                               pci_unmap_addr(e, mapaddr),
@@ -2327,17 +2427,23 @@ static void skge_tx_timeout(struct net_device *dev)
 
 static int skge_change_mtu(struct net_device *dev, int new_mtu)
 {
-       int err = 0;
+       int err;
 
        if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
                return -EINVAL;
 
+       if (!netif_running(dev)) {
+               dev->mtu = new_mtu;
+               return 0;
+       }
+
+       skge_down(dev);
+
        dev->mtu = new_mtu;
 
-       if (netif_running(dev)) {
-               skge_down(dev);
-               skge_up(dev);
-       }
+       err = skge_up(dev);
+       if (err)
+               dev_close(dev);
 
        return err;
 }
@@ -2352,8 +2458,6 @@ static void genesis_set_multicast(struct net_device *dev)
        u32 mode;
        u8 filter[8];
 
-       pr_debug("genesis_set_multicast flags=%x count=%d\n", dev->flags, dev->mc_count);
-
        mode = xm_read32(hw, port, XM_MODE);
        mode |= XM_MD_ENA_HASH;
        if (dev->flags & IFF_PROMISC)
@@ -2391,7 +2495,7 @@ static void yukon_set_multicast(struct net_device *dev)
        reg = gma_read16(hw, port, GM_RX_CTRL);
        reg |= GM_RXCR_UCF_ENA;
 
-       if (dev->flags & IFF_PROMISC)           /* promiscious */
+       if (dev->flags & IFF_PROMISC)           /* promiscuous */
                reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
        else if (dev->flags & IFF_ALLMULTI)     /* all multicast */
                memset(filter, 0xff, sizeof(filter));
@@ -2420,6 +2524,14 @@ static void yukon_set_multicast(struct net_device *dev)
        gma_write16(hw, port, GM_RX_CTRL, reg);
 }
 
+static inline u16 phy_length(const struct skge_hw *hw, u32 status)
+{
+       if (hw->chip_id == CHIP_ID_GENESIS)
+               return status >> XMR_FS_LEN_SHIFT;
+       else
+               return status >> GMR_FS_LEN_SHIFT;
+}
+
 static inline int bad_phy_status(const struct skge_hw *hw, u32 status)
 {
        if (hw->chip_id == CHIP_ID_GENESIS)
@@ -2429,35 +2541,102 @@ static inline int bad_phy_status(const struct skge_hw *hw, u32 status)
                        (status & GMR_FS_RX_OK) == 0;
 }
 
-static void skge_rx_error(struct skge_port *skge, int slot,
-                         u32 control, u32 status)
+
+/* Get receive buffer from descriptor.
+ * Handles copy of small buffers and reallocation failures
+ */
+static inline struct sk_buff *skge_rx_get(struct skge_port *skge,
+                                         struct skge_element *e,
+                                         u32 control, u32 status, u16 csum)
 {
-       if (netif_msg_rx_err(skge))
-               printk(KERN_DEBUG PFX "%s: rx err, slot %d control 0x%x status 0x%x\n",
-                      skge->netdev->name, slot, control, status);
+       struct sk_buff *skb;
+       u16 len = control & BMU_BBC;
+
+       if (unlikely(netif_msg_rx_status(skge)))
+               printk(KERN_DEBUG PFX "%s: rx slot %td status 0x%x len %d\n",
+                      skge->netdev->name, e - skge->rx_ring.start,
+                      status, len);
+
+       if (len > skge->rx_buf_size)
+               goto error;
+
+       if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF))
+               goto error;
+
+       if (bad_phy_status(skge->hw, status))
+               goto error;
+
+       if (phy_length(skge->hw, status) != len)
+               goto error;
+
+       if (len < RX_COPY_THRESHOLD) {
+               skb = dev_alloc_skb(len + 2);
+               if (!skb)
+                       goto resubmit;
+
+               skb_reserve(skb, 2);
+               pci_dma_sync_single_for_cpu(skge->hw->pdev,
+                                           pci_unmap_addr(e, mapaddr),
+                                           len, PCI_DMA_FROMDEVICE);
+               memcpy(skb->data, e->skb->data, len);
+               pci_dma_sync_single_for_device(skge->hw->pdev,
+                                              pci_unmap_addr(e, mapaddr),
+                                              len, PCI_DMA_FROMDEVICE);
+               skge_rx_reuse(e, skge->rx_buf_size);
+       } else {
+               struct sk_buff *nskb;
+               nskb = dev_alloc_skb(skge->rx_buf_size + NET_IP_ALIGN);
+               if (!nskb)
+                       goto resubmit;
 
-       if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)
-           || (control & BMU_BBC) > skge->netdev->mtu + VLAN_ETH_HLEN)
-               skge->net_stats.rx_length_errors++;
-       else {
-               if (skge->hw->chip_id == CHIP_ID_GENESIS) {
-                       if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR))
-                               skge->net_stats.rx_length_errors++;
-                       if (status & XMR_FS_FRA_ERR)
-                               skge->net_stats.rx_frame_errors++;
-                       if (status & XMR_FS_FCS_ERR)
-                               skge->net_stats.rx_crc_errors++;
-               } else {
-                       if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE))
-                               skge->net_stats.rx_length_errors++;
-                       if (status & GMR_FS_FRAGMENT)
-                               skge->net_stats.rx_frame_errors++;
-                       if (status & GMR_FS_CRC_ERR)
-                               skge->net_stats.rx_crc_errors++;
-               }
+               pci_unmap_single(skge->hw->pdev,
+                                pci_unmap_addr(e, mapaddr),
+                                pci_unmap_len(e, maplen),
+                                PCI_DMA_FROMDEVICE);
+               skb = e->skb;
+               prefetch(skb->data);
+               skge_rx_setup(skge, e, nskb, skge->rx_buf_size);
        }
+
+       skb_put(skb, len);
+       skb->dev = skge->netdev;
+       if (skge->rx_csum) {
+               skb->csum = csum;
+               skb->ip_summed = CHECKSUM_HW;
+       }
+
+       skb->protocol = eth_type_trans(skb, skge->netdev);
+
+       return skb;
+error:
+
+       if (netif_msg_rx_err(skge))
+               printk(KERN_DEBUG PFX "%s: rx err, slot %td control 0x%x status 0x%x\n",
+                      skge->netdev->name, e - skge->rx_ring.start,
+                      control, status);
+
+       if (skge->hw->chip_id == CHIP_ID_GENESIS) {
+               if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR))
+                       skge->net_stats.rx_length_errors++;
+               if (status & XMR_FS_FRA_ERR)
+                       skge->net_stats.rx_frame_errors++;
+               if (status & XMR_FS_FCS_ERR)
+                       skge->net_stats.rx_crc_errors++;
+       } else {
+               if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE))
+                       skge->net_stats.rx_length_errors++;
+               if (status & GMR_FS_FRAGMENT)
+                       skge->net_stats.rx_frame_errors++;
+               if (status & GMR_FS_CRC_ERR)
+                       skge->net_stats.rx_crc_errors++;
+       }
+
+resubmit:
+       skge_rx_reuse(e, skge->rx_buf_size);
+       return NULL;
 }
 
+
 static int skge_poll(struct net_device *dev, int *budget)
 {
        struct skge_port *skge = netdev_priv(dev);
@@ -2466,78 +2645,46 @@ static int skge_poll(struct net_device *dev, int *budget)
        struct skge_element *e;
        unsigned int to_do = min(dev->quota, *budget);
        unsigned int work_done = 0;
-       int done;
-
-       pr_debug("skge_poll\n");
 
-       for (e = ring->to_clean; e != ring->to_use && work_done < to_do;
-            e = e->next) {
+       for (e = ring->to_clean; prefetch(e->next), work_done < to_do; e = e->next) {
                struct skge_rx_desc *rd = e->desc;
-               struct sk_buff *skb = e->skb;
-               u32 control, len, status;
+               struct sk_buff *skb;
+               u32 control;
 
                rmb();
                control = rd->control;
                if (control & BMU_OWN)
                        break;
 
-               len = control & BMU_BBC;
-               e->skb = NULL;
-
-               pci_unmap_single(hw->pdev,
-                                pci_unmap_addr(e, mapaddr),
-                                pci_unmap_len(e, maplen),
-                                PCI_DMA_FROMDEVICE);
-
-               status = rd->status;
-               if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)
-                    || len > dev->mtu + VLAN_ETH_HLEN
-                    || bad_phy_status(hw, status)) {
-                       skge_rx_error(skge, e - ring->start, control, status);
-                       dev_kfree_skb(skb);
-                       continue;
-               }
-
-               if (netif_msg_rx_status(skge))
-                   printk(KERN_DEBUG PFX "%s: rx slot %td status 0x%x len %d\n",
-                          dev->name, e - ring->start, rd->status, len);
-
-               skb_put(skb, len);
-               skb->protocol = eth_type_trans(skb, dev);
-
-               if (skge->rx_csum) {
-                       skb->csum = le16_to_cpu(rd->csum2);
-                       skb->ip_summed = CHECKSUM_HW;
-               }
-
-               dev->last_rx = jiffies;
-               netif_receive_skb(skb);
+               skb = skge_rx_get(skge, e, control, rd->status,
+                                 le16_to_cpu(rd->csum2));
+               if (likely(skb)) {
+                       dev->last_rx = jiffies;
+                       netif_receive_skb(skb);
 
-               ++work_done;
+                       ++work_done;
+               } else
+                       skge_rx_reuse(e, skge->rx_buf_size);
        }
        ring->to_clean = e;
 
-       *budget -= work_done;
-       dev->quota -= work_done;
-       done = work_done < to_do;
-
-       if (skge_rx_fill(skge))
-               done = 0;
-
        /* restart receiver */
        wmb();
        skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR),
                    CSR_START | CSR_IRQ_CL_F);
 
-       if (done) {
-               local_irq_disable();
-               __netif_rx_complete(dev);
-               hw->intr_mask |= portirqmask[skge->port];
-               skge_write32(hw, B0_IMSK, hw->intr_mask);
-               local_irq_enable();
-       }
+       *budget -= work_done;
+       dev->quota -= work_done;
+
+       if (work_done >=  to_do)
+               return 1; /* not done */
 
-       return !done;
+       netif_rx_complete(dev);
+       hw->intr_mask |= portirqmask[skge->port];
+       skge_write32(hw, B0_IMSK, hw->intr_mask);
+       skge_read32(hw, B0_IMSK);
+
+       return 0;
 }
 
 static inline void skge_tx_intr(struct net_device *dev)
@@ -2548,7 +2695,7 @@ static inline void skge_tx_intr(struct net_device *dev)
        struct skge_element *e;
 
        spin_lock(&skge->tx_lock);
-       for (e = ring->to_clean; e != ring->to_use; e = e->next) {
+       for (e = ring->to_clean; prefetch(e->next), e != ring->to_use; e = e->next) {
                struct skge_tx_desc *td = e->desc;
                u32 control;
 
@@ -2574,11 +2721,17 @@ static inline void skge_tx_intr(struct net_device *dev)
        spin_unlock(&skge->tx_lock);
 }
 
+/* Parity errors seem to happen when Genesis is connected to a switch
+ * with no other ports present. Heartbeat error??
+ */
 static void skge_mac_parity(struct skge_hw *hw, int port)
 {
-       printk(KERN_ERR PFX "%s: mac data parity error\n",
-              hw->dev[port] ? hw->dev[port]->name
-              : (port == 0 ? "(port A)": "(port B"));
+       struct net_device *dev = hw->dev[port];
+
+       if (dev) {
+               struct skge_port *skge = netdev_priv(dev);
+               ++skge->net_stats.tx_heartbeat_errors;
+       }
 
        if (hw->chip_id == CHIP_ID_GENESIS)
                skge_write16(hw, SK_REG(port, TX_MFF_CTRL1),
@@ -2617,25 +2770,13 @@ static void skge_error_irq(struct skge_hw *hw)
        if (hw->chip_id == CHIP_ID_GENESIS) {
                /* clear xmac errors */
                if (hwstatus & (IS_NO_STAT_M1|IS_NO_TIST_M1))
-                       skge_write16(hw, SK_REG(0, RX_MFF_CTRL1), MFF_CLR_INSTAT);
+                       skge_write16(hw, RX_MFF_CTRL1, MFF_CLR_INSTAT);
                if (hwstatus & (IS_NO_STAT_M2|IS_NO_TIST_M2))
-                       skge_write16(hw, SK_REG(0, RX_MFF_CTRL2), MFF_CLR_INSTAT);
+                       skge_write16(hw, RX_MFF_CTRL2, MFF_CLR_INSTAT);
        } else {
                /* Timestamp (unused) overflow */
                if (hwstatus & IS_IRQ_TIST_OV)
                        skge_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
-
-               if (hwstatus & IS_IRQ_SENSOR) {
-                       /* no sensors on 32-bit Yukon */
-                       if (!(skge_read16(hw, B0_CTST) & CS_BUS_SLOT_SZ)) {
-                               printk(KERN_ERR PFX "ignoring bogus sensor interrups\n");
-                               skge_write32(hw, B0_HWE_IMSK,
-                                            IS_ERR_MSK & ~IS_IRQ_SENSOR);
-                       } else
-                               printk(KERN_WARNING PFX "sensor interrupt\n");
-               }
-
-
        }
 
        if (hwstatus & IS_RAM_RD_PAR) {
@@ -2666,9 +2807,10 @@ static void skge_error_irq(struct skge_hw *hw)
 
                skge_pci_clear(hw);
 
+               /* if error still set then just ignore it */
                hwstatus = skge_read32(hw, B0_HWE_ISRC);
                if (hwstatus & IS_IRQ_STAT) {
-                       printk(KERN_WARNING PFX "IRQ status %x: still set ignoring hardware errors\n",
+                       pr_debug("IRQ status %x: still set ignoring hardware errors\n",
                               hwstatus);
                        hw->intr_mask &= ~IS_HW_ERR;
                }
@@ -2676,7 +2818,7 @@ static void skge_error_irq(struct skge_hw *hw)
 }
 
 /*
- * Interrrupt from PHY are handled in tasklet (soft irq)
+ * Interrupt from PHY are handled in tasklet (soft irq)
  * because accessing phy registers requires spin wait which might
  * cause excess interrupt latency.
  */
@@ -2706,6 +2848,14 @@ static void skge_extirq(unsigned long data)
        local_irq_enable();
 }
 
+static inline void skge_wakeup(struct net_device *dev)
+{
+       struct skge_port *skge = netdev_priv(dev);
+
+       prefetch(skge->rx_ring.to_clean);
+       netif_rx_schedule(dev);
+}
+
 static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct skge_hw *hw = dev_id;
@@ -2717,12 +2867,12 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
        status &= hw->intr_mask;
        if (status & IS_R1_F) {
                hw->intr_mask &= ~IS_R1_F;
-               netif_rx_schedule(hw->dev[0]);
+               skge_wakeup(hw->dev[0]);
        }
 
        if (status & IS_R2_F) {
                hw->intr_mask &= ~IS_R2_F;
-               netif_rx_schedule(hw->dev[1]);
+               skge_wakeup(hw->dev[1]);
        }
 
        if (status & IS_XA1_F)
@@ -2731,6 +2881,24 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
        if (status & IS_XA2_F)
                skge_tx_intr(hw->dev[1]);
 
+       if (status & IS_PA_TO_RX1) {
+               struct skge_port *skge = netdev_priv(hw->dev[0]);
+               ++skge->net_stats.rx_over_errors;
+               skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_RX1);
+       }
+
+       if (status & IS_PA_TO_RX2) {
+               struct skge_port *skge = netdev_priv(hw->dev[1]);
+               ++skge->net_stats.rx_over_errors;
+               skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_RX2);
+       }
+
+       if (status & IS_PA_TO_TX1)
+               skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_TX1);
+
+       if (status & IS_PA_TO_TX2)
+               skge_write16(hw, B3_PA_CTRL, PA_CLR_TO_TX2);
+
        if (status & IS_MAC1)
                skge_mac_intr(hw, 0);
 
@@ -2764,21 +2932,29 @@ static void skge_netpoll(struct net_device *dev)
 static int skge_set_mac_address(struct net_device *dev, void *p)
 {
        struct skge_port *skge = netdev_priv(dev);
-       struct sockaddr *addr = p;
-       int err = 0;
+       struct skge_hw *hw = skge->hw;
+       unsigned port = skge->port;
+       const struct sockaddr *addr = p;
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
-       skge_down(dev);
+       spin_lock_bh(&hw->phy_lock);
        memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
-       memcpy_toio(skge->hw->regs + B2_MAC_1 + skge->port*8,
+       memcpy_toio(hw->regs + B2_MAC_1 + port*8,
                    dev->dev_addr, ETH_ALEN);
-       memcpy_toio(skge->hw->regs + B2_MAC_2 + skge->port*8,
+       memcpy_toio(hw->regs + B2_MAC_2 + port*8,
                    dev->dev_addr, ETH_ALEN);
-       if (dev->flags & IFF_UP)
-               err = skge_up(dev);
-       return err;
+
+       if (hw->chip_id == CHIP_ID_GENESIS)
+               xm_outaddr(hw, port, XM_SA, dev->dev_addr);
+       else {
+               gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr);
+               gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr);
+       }
+       spin_unlock_bh(&hw->phy_lock);
+
+       return 0;
 }
 
 static const struct {
@@ -2811,8 +2987,9 @@ static const char *skge_board_name(const struct skge_hw *hw)
  */
 static int skge_reset(struct skge_hw *hw)
 {
+       u32 reg;
        u16 ctst;
-       u8 t8, mac_cfg;
+       u8 t8, mac_cfg, pmd_type, phy_type;
        int i;
 
        ctst = skge_read16(hw, B0_CTST);
@@ -2831,18 +3008,19 @@ static int skge_reset(struct skge_hw *hw)
                     ctst & (CS_CLK_RUN_HOT|CS_CLK_RUN_RST|CS_CLK_RUN_ENA));
 
        hw->chip_id = skge_read8(hw, B2_CHIP_ID);
-       hw->phy_type = skge_read8(hw, B2_E_1) & 0xf;
-       hw->pmd_type = skge_read8(hw, B2_PMD_TYP);
+       phy_type = skge_read8(hw, B2_E_1) & 0xf;
+       pmd_type = skge_read8(hw, B2_PMD_TYP);
+       hw->copper = (pmd_type == 'T' || pmd_type == '1');
 
        switch (hw->chip_id) {
        case CHIP_ID_GENESIS:
-               switch (hw->phy_type) {
+               switch (phy_type) {
                case SK_PHY_BCOM:
                        hw->phy_addr = PHY_ADDR_BCOM;
                        break;
                default:
                        printk(KERN_ERR PFX "%s: unsupported phy type 0x%x\n",
-                              pci_name(hw->pdev), hw->phy_type);
+                              pci_name(hw->pdev), phy_type);
                        return -EOPNOTSUPP;
                }
                break;
@@ -2850,13 +3028,10 @@ static int skge_reset(struct skge_hw *hw)
        case CHIP_ID_YUKON:
        case CHIP_ID_YUKON_LITE:
        case CHIP_ID_YUKON_LP:
-               if (hw->phy_type < SK_PHY_MARV_COPPER && hw->pmd_type != 'S')
-                       hw->phy_type = SK_PHY_MARV_COPPER;
+               if (phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
+                       hw->copper = 1;
 
                hw->phy_addr = PHY_ADDR_MARV;
-               if (!iscopper(hw))
-                       hw->phy_type = SK_PHY_MARV_FIBER;
-
                break;
 
        default:
@@ -2884,12 +3059,29 @@ static int skge_reset(struct skge_hw *hw)
        else
                hw->ram_size = t8 * 4096;
 
+       hw->intr_mask = IS_HW_ERR | IS_EXT_REG;
        if (hw->chip_id == CHIP_ID_GENESIS)
                genesis_init(hw);
        else {
                /* switch power to VCC (WA for VAUX problem) */
                skge_write8(hw, B0_POWER_CTRL,
                            PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
+
+               /* avoid boards with stuck Hardware error bits */
+               if ((skge_read32(hw, B0_ISRC) & IS_HW_ERR) &&
+                   (skge_read32(hw, B0_HWE_ISRC) & IS_IRQ_SENSOR)) {
+                       printk(KERN_WARNING PFX "stuck hardware sensor bit\n");
+                       hw->intr_mask &= ~IS_HW_ERR;
+               }
+
+               /* Clear PHY COMA */
+               skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+               pci_read_config_dword(hw->pdev, PCI_DEV_REG1, &reg);
+               reg &= ~PCI_PHY_COMA;
+               pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg);
+               skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+
                for (i = 0; i < hw->ports; i++) {
                        skge_write16(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET);
                        skge_write16(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR);
@@ -2930,12 +3122,8 @@ static int skge_reset(struct skge_hw *hw)
        skge_write32(hw, B2_IRQM_INI, skge_usecs2clk(hw, 100));
        skge_write32(hw, B2_IRQM_CTRL, TIM_START);
 
-       hw->intr_mask = IS_HW_ERR | IS_EXT_REG;
        skge_write32(hw, B0_IMSK, hw->intr_mask);
 
-       if (hw->chip_id != CHIP_ID_GENESIS)
-               skge_write8(hw, GMAC_IRQ_MSK, 0);
-
        spin_lock_bh(&hw->phy_lock);
        for (i = 0; i < hw->ports; i++) {
                if (hw->chip_id == CHIP_ID_GENESIS)
@@ -2964,6 +3152,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
        SET_NETDEV_DEV(dev, &hw->pdev->dev);
        dev->open = skge_up;
        dev->stop = skge_down;
+       dev->do_ioctl = skge_ioctl;
        dev->hard_start_xmit = skge_xmit_frame;
        dev->get_stats = skge_get_stats;
        if (hw->chip_id == CHIP_ID_GENESIS)
@@ -3006,10 +3195,6 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
 
        spin_lock_init(&skge->tx_lock);
 
-       init_timer(&skge->led_blink);
-       skge->led_blink.function = skge_blink_timer;
-       skge->led_blink.data = (unsigned long) skge;
-
        if (hw->chip_id != CHIP_ID_GENESIS) {
                dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
                skge->rx_csum = 1;
@@ -3017,6 +3202,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
 
        /* read the mac address */
        memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port*8, ETH_ALEN);
+       memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
 
        /* device is off until link detection */
        netif_carrier_off(dev);
@@ -3066,7 +3252,7 @@ static int __devinit skge_probe(struct pci_dev *pdev,
        }
 
 #ifdef __BIG_ENDIAN
-       /* byte swap decriptors in hardware */
+       /* byte swap descriptors in hardware */
        {
                u32 reg;
 
@@ -3077,14 +3263,13 @@ static int __devinit skge_probe(struct pci_dev *pdev,
 #endif
 
        err = -ENOMEM;
-       hw = kmalloc(sizeof(*hw), GFP_KERNEL);
+       hw = kzalloc(sizeof(*hw), GFP_KERNEL);
        if (!hw) {
                printk(KERN_ERR PFX "%s: cannot allocate hardware struct\n",
                       pci_name(pdev));
                goto err_out_free_regions;
        }
 
-       memset(hw, 0, sizeof(*hw));
        hw->pdev = pdev;
        spin_lock_init(&hw->phy_lock);
        tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw);
@@ -3107,7 +3292,7 @@ static int __devinit skge_probe(struct pci_dev *pdev,
        if (err)
                goto err_out_free_irq;
 
-       printk(KERN_INFO PFX "addr 0x%lx irq %d chip %s rev %d\n",
+       printk(KERN_INFO PFX DRV_VERSION " addr 0x%lx irq %d chip %s rev %d\n",
               pci_resource_start(pdev, 0), pdev->irq,
               skge_board_name(hw), hw->chip_rev);
 
@@ -3167,6 +3352,11 @@ static void __devexit skge_remove(struct pci_dev *pdev)
        dev0 = hw->dev[0];
        unregister_netdev(dev0);
 
+       skge_write32(hw, B0_IMSK, 0);
+       skge_write16(hw, B0_LED, LED_STAT_OFF);
+       skge_pci_clear(hw);
+       skge_write8(hw, B0_CTST, CS_RST_SET);
+
        tasklet_kill(&hw->ext_tasklet);
 
        free_irq(pdev->irq, hw);
@@ -3175,14 +3365,14 @@ static void __devexit skge_remove(struct pci_dev *pdev)
        if (dev1)
                free_netdev(dev1);
        free_netdev(dev0);
-       skge_write16(hw, B0_LED, LED_STAT_OFF);
+
        iounmap(hw->regs);
        kfree(hw);
        pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
-static int skge_suspend(struct pci_dev *pdev, u32 state)
+static int skge_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct skge_hw *hw  = pci_get_drvdata(pdev);
        int i, wol = 0;
@@ -3194,7 +3384,10 @@ static int skge_suspend(struct pci_dev *pdev, u32 state)
                        struct skge_port *skge = netdev_priv(dev);
                        if (netif_running(dev)) {
                                netif_carrier_off(dev);
-                               skge_down(dev);
+                               if (skge->wol)
+                                       netif_stop_queue(dev);
+                               else
+                                       skge_down(dev);
                        }
                        netif_device_detach(dev);
                        wol |= skge->wol;
@@ -3202,7 +3395,7 @@ static int skge_suspend(struct pci_dev *pdev, u32 state)
        }
 
        pci_save_state(pdev);
-       pci_enable_wake(pdev, state, wol);
+       pci_enable_wake(pdev, pci_choose_state(pdev, state), wol);
        pci_disable_device(pdev);
        pci_set_power_state(pdev, pci_choose_state(pdev, state));
 
@@ -3224,8 +3417,8 @@ static int skge_resume(struct pci_dev *pdev)
                struct net_device *dev = hw->dev[i];
                if (dev) {
                        netif_device_attach(dev);
-                       if (netif_running(dev))
-                               skge_up(dev);
+                       if (netif_running(dev) && skge_up(dev))
+                               dev_close(dev);
                }
        }
        return 0;