[PATCH] kzalloc() conversion in drivers/block
[safe/jmp/linux-2.6] / drivers / net / skge.c
index 6b889a3..35dbf05 100644 (file)
@@ -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.7"
+#define DRV_VERSION            "1.5"
 #define PFX                    DRV_NAME " "
 
 #define DEFAULT_TX_RING_SIZE   128
@@ -79,23 +81,22 @@ static const struct pci_device_id skge_id_table[] = {
        { 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 */
@@ -103,46 +104,32 @@ static const int txqaddr[] = { Q_XA1, Q_XA2 };
 static const int rxqaddr[] = { Q_R1, Q_R2 };
 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 ||
@@ -182,14 +169,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
@@ -222,7 +209,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
@@ -288,10 +275,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);
 }
 
@@ -371,7 +357,7 @@ static struct net_device_stats *skge_get_stats(struct net_device *dev)
        skge->net_stats.rx_bytes = data[1];
        skge->net_stats.tx_packets = data[2] + data[4] + data[6];
        skge->net_stats.rx_packets = data[3] + data[5] + data[7];
-       skge->net_stats.multicast = data[5] + data[7];
+       skge->net_stats.multicast = data[3] + data[5];
        skge->net_stats.collisions = data[10];
        skge->net_stats.tx_aborted_errors = data[12];
 
@@ -411,6 +397,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)
@@ -421,7 +408,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;
@@ -442,21 +431,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;
 }
 
@@ -528,10 +507,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;
 }
 
@@ -544,13 +521,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;
@@ -669,7 +646,7 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
                                     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 ?
@@ -743,25 +720,25 @@ 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,
 };
 
 /*
  * Allocate ring elements and chain them together
  * One-to-one association of board descriptors with ring elements
  */
-static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base)
+static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u32 base)
 {
        struct skge_tx_desc *d;
        struct skge_element *e;
        int i;
 
-       ring->start = kmalloc(sizeof(*e)*ring->count, GFP_KERNEL);
+       ring->start = kcalloc(sizeof(*e), ring->count, GFP_KERNEL);
        if (!ring->start)
                return -ENOMEM;
 
        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;
@@ -775,17 +752,6 @@ static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base)
        return 0;
 }
 
-static struct sk_buff *skge_rx_alloc(struct net_device *dev, unsigned int size)
-{
-       struct sk_buff *skb = dev_alloc_skb(size);
-
-       if (likely(skb)) {
-               skb->dev = dev;
-               skb_reserve(skb, NET_IP_ALIGN);
-       }
-       return skb;
-}
-
 /* 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)
@@ -815,7 +781,7 @@ static void skge_rx_setup(struct skge_port *skge, struct skge_element *e,
  * 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)
+static inline void skge_rx_reuse(struct skge_element *e, unsigned int size)
 {
        struct skge_rx_desc *rd = e->desc;
 
@@ -858,16 +824,17 @@ static int skge_rx_fill(struct skge_port *skge)
 {
        struct skge_ring *ring = &skge->rx_ring;
        struct skge_element *e;
-       unsigned int bufsize = skge->rx_buf_size;
 
        e = ring->start;
        do {
-               struct sk_buff *skb = skge_rx_alloc(skge->netdev, bufsize);
+               struct sk_buff *skb;
 
+               skb = alloc_skb(skge->rx_buf_size + NET_IP_ALIGN, GFP_KERNEL);
                if (!skb)
                        return -ENOMEM;
 
-               skge_rx_setup(skge, e, skb, bufsize);
+               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;
@@ -876,9 +843,11 @@ static int skge_rx_fill(struct skge_port *skge)
 
 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);
+       netif_wake_queue(skge->netdev);
 
        if (netif_msg_link(skge))
                printk(KERN_INFO PFX
@@ -894,6 +863,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);
 
@@ -901,32 +871,36 @@ 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);
+       *val = 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;
+               udelay(1);
        }
 
-       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;
 
@@ -936,19 +910,16 @@ 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;
+                       return 0;
+               udelay(1);
        }
-       printk(KERN_WARNING PFX "%s: phy write timed out\n",
-                      hw->dev[port]->name);
+       return -ETIMEDOUT;
 }
 
 static void genesis_init(struct skge_hw *hw)
@@ -983,6 +954,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 */
@@ -1017,8 +990,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);
@@ -1102,8 +1073,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);
 
@@ -1185,7 +1154,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);
@@ -1200,13 +1169,17 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
        u32 r;
        const u8 zero[6]  = { 0 };
 
-       /* Clear MIB counters */
-       xm_write16(hw, port, XM_STAT_CMD,
-                       XM_SC_CLR_RXC | XM_SC_CLR_TXC);
-       /* Clear two times according to Errata #3 */
-       xm_write16(hw, port, XM_STAT_CMD,
-                       XM_SC_CLR_RXC | XM_SC_CLR_TXC);
+       for (i = 0; i < 10; i++) {
+               skge_write16(hw, SK_REG(port, TX_MFF_CTRL1),
+                            MFF_SET_MAC_RST);
+               if (skge_read16(hw, SK_REG(port, TX_MFF_CTRL1)) & MFF_SET_MAC_RST)
+                       goto reset_ok;
+               udelay(1);
+       }
 
+       printk(KERN_WARNING PFX "%s: genesis reset failed\n", dev->name);
+
+ reset_ok:
        /* Unreset the XMAC. */
        skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST);
 
@@ -1223,9 +1196,9 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
                r |= GP_DIR_2|GP_IO_2;
 
        skge_write32(hw, B2_GP_IO, r);
-       skge_read32(hw, B2_GP_IO);
 
-       /* Enable GMII interfac */
+
+       /* Enable GMII interface */
        xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
 
        bcom_phy_init(skge, jumbo);
@@ -1237,6 +1210,13 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
        for (i = 1; i < 16; i++)
                xm_outaddr(hw, port, XM_EXM(i), zero);
 
+       /* Clear MIB counters */
+       xm_write16(hw, port, XM_STAT_CMD,
+                       XM_SC_CLR_RXC | XM_SC_CLR_TXC);
+       /* Clear two times according to Errata #3 */
+       xm_write16(hw, port, XM_STAT_CMD,
+                       XM_SC_CLR_RXC | XM_SC_CLR_TXC);
+
        /* configure Rx High Water Mark (XM_RX_HI_WM) */
        xm_write16(hw, port, XM_RX_HI_WM, 1450);
 
@@ -1276,7 +1256,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);
@@ -1336,12 +1316,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,
@@ -1418,42 +1400,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;
@@ -1461,7 +1407,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);
 
        /*
@@ -1568,13 +1513,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;
 
-       pr_debug("yukon_init\n");
        if (skge->autoneg == AUTONEG_ENABLE) {
                u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
 
@@ -1599,7 +1591,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)
@@ -1664,6 +1656,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]);
@@ -1673,9 +1681,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);
@@ -1683,20 +1693,23 @@ 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);
        skge_write32(hw, SK_REG(port, GPHY_CTRL), reg | GPC_RST_CLR);
        skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON | GMC_RST_CLR);
+
        if (skge->autoneg == AUTONEG_DISABLE) {
                reg = GM_GPCR_AU_ALL_DIS;
                gma_write16(hw, port, GM_GP_CTRL,
@@ -1704,16 +1717,23 @@ static void yukon_mac_init(struct skge_hw *hw, int port)
 
                switch (skge->speed) {
                case SPEED_1000:
+                       reg &= ~GM_GPCR_SPEED_100;
                        reg |= GM_GPCR_SPEED_1000;
-                       /* fallthru */
+                       break;
                case SPEED_100:
+                       reg &= ~GM_GPCR_SPEED_1000;
                        reg |= GM_GPCR_SPEED_100;
+                       break;
+               case SPEED_10:
+                       reg &= ~(GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100);
+                       break;
                }
 
                if (skge->duplex == DUPLEX_FULL)
                        reg |= GM_GPCR_DUP_FULL;
        } else
                reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL;
+
        switch (skge->flow_control) {
        case FLOW_MODE_NONE:
                skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
@@ -1725,7 +1745,7 @@ 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));
 
        yukon_init(hw, port);
 
@@ -1775,37 +1795,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_TX_ENA|GM_GPCR_RX_ENA));
        gma_read16(hw, port, GM_GP_CTRL);
 
+       yukon_suspend(hw, port);
+
        /* set GPHY Control reset */
-       skge_write32(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
-       skge_write32(hw, SK_REG(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)
@@ -1864,10 +1909,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)
@@ -1887,7 +1930,6 @@ static void yukon_link_down(struct skge_port *skge)
        int port = skge->port;
        u16 ctrl;
 
-       pr_debug("yukon_link_down\n");
        gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
 
        ctrl = gma_read16(hw, port, GM_GP_CTRL);
@@ -1987,6 +2029,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,7 +2154,7 @@ static int skge_up(struct net_device *dev)
                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;
+               skge->rx_buf_size = dev->mtu + ETH_HLEN;
        else
                skge->rx_buf_size = RX_BUF_SIZE;
 
@@ -2060,26 +2166,30 @@ static int skge_up(struct net_device *dev)
        if (!skge->mem)
                return -ENOMEM;
 
+       BUG_ON(skge->dma & 7);
+
+       if ((u64)skge->dma >> 32 != ((u64) skge->dma + skge->mem_size) >> 32) {
+               printk(KERN_ERR PFX "pci_alloc_consistent region crosses 4G boundary\n");
+               err = -EINVAL;
+               goto free_pci_mem;
+       }
+
        memset(skge->mem, 0, skge->mem_size);
 
-       if ((err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma)))
+       err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma);
+       if (err)
                goto free_pci_mem;
 
        err = skge_rx_fill(skge);
        if (err)
                goto free_rx_ring;
 
-       if ((err = skge_ring_alloc(&skge->tx_ring, skge->mem + rx_size,
-                                  skge->dma + rx_size)))
+       err = skge_ring_alloc(&skge->tx_ring, skge->mem + rx_size,
+                             skge->dma + rx_size);
+       if (err)
                goto free_rx_ring;
 
-       skge->tx_avail = skge->tx_ring.count - 1;
-
-       /* Enable IRQ from port */
-       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);
@@ -2103,7 +2213,6 @@ static int skge_up(struct net_device *dev)
        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:
@@ -2111,6 +2220,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;
 }
@@ -2121,20 +2231,25 @@ 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);
 
+       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);
+
        /* 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),
@@ -2172,9 +2287,16 @@ 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;
 }
 
+static inline int skge_avail(const struct skge_ring *ring)
+{
+       return ((ring->to_clean > ring->to_use) ? 0 : ring->count)
+               + (ring->to_clean - ring->to_use) - 1;
+}
+
 static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
 {
        struct skge_port *skge = netdev_priv(dev);
@@ -2185,25 +2307,24 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        int i;
        u32 control, len;
        u64 map;
-       unsigned long flags;
 
        skb = skb_padto(skb, ETH_ZLEN);
        if (!skb)
                return NETDEV_TX_OK;
 
-       local_irq_save(flags);
        if (!spin_trylock(&skge->tx_lock)) {
-               /* Collision - tell upper layer to requeue */
-               local_irq_restore(flags);
-               return NETDEV_TX_LOCKED;
-       }
+               /* Collision - tell upper layer to requeue */
+               return NETDEV_TX_LOCKED;
+       }
 
-       if (unlikely(skge->tx_avail < skb_shinfo(skb)->nr_frags +1)) {
-               netif_stop_queue(dev);
-               spin_unlock_irqrestore(&skge->tx_lock, flags);
+       if (unlikely(skge_avail(&skge->tx_ring) < skb_shinfo(skb)->nr_frags + 1)) {
+               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(&skge->tx_lock);
                return NETDEV_TX_BUSY;
        }
 
@@ -2219,14 +2340,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
@@ -2274,49 +2393,51 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
                       dev->name, e - ring->start, skb->len);
 
        ring->to_use = e->next;
-       skge->tx_avail -= skb_shinfo(skb)->nr_frags + 1;
-       if (skge->tx_avail <= MAX_SKB_FRAGS + 1) {
+       if (skge_avail(&skge->tx_ring) <= MAX_SKB_FRAGS + 1) {
                pr_debug("%s: transmit queue full\n", dev->name);
                netif_stop_queue(dev);
        }
 
+       mmiowb();
+       spin_unlock(&skge->tx_lock);
+
        dev->trans_start = jiffies;
-       spin_unlock_irqrestore(&skge->tx_lock, flags);
 
        return NETDEV_TX_OK;
 }
 
-static inline void skge_tx_free(struct skge_hw *hw, struct skge_element *e)
+static void skge_tx_complete(struct skge_port *skge, struct skge_element *last)
 {
-       /* This ring element can be skb or fragment */
-       if (e->skb) {
-               pci_unmap_single(hw->pdev,
-                              pci_unmap_addr(e, mapaddr),
-                              pci_unmap_len(e, maplen),
-                              PCI_DMA_TODEVICE);
-               dev_kfree_skb_any(e->skb);
+       struct pci_dev *pdev = skge->hw->pdev;
+       struct skge_element *e;
+
+       for (e = skge->tx_ring.to_clean; e != last; e = e->next) {
+               struct sk_buff *skb = e->skb;
+               int i;
+
                e->skb = NULL;
-       } else {
-               pci_unmap_page(hw->pdev,
-                              pci_unmap_addr(e, mapaddr),
-                              pci_unmap_len(e, maplen),
-                              PCI_DMA_TODEVICE);
+               pci_unmap_single(pdev, pci_unmap_addr(e, mapaddr),
+                                skb_headlen(skb), PCI_DMA_TODEVICE);
+
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       e = e->next;
+                       pci_unmap_page(pdev, pci_unmap_addr(e, mapaddr),
+                                      skb_shinfo(skb)->frags[i].size,
+                                      PCI_DMA_TODEVICE);
+               }
+
+               dev_kfree_skb(skb);
        }
+       skge->tx_ring.to_clean = e;
 }
 
 static void skge_tx_clean(struct skge_port *skge)
 {
-       struct skge_ring *ring = &skge->tx_ring;
-       struct skge_element *e;
-       unsigned long flags;
 
-       spin_lock_irqsave(&skge->tx_lock, flags);
-       for (e = ring->to_clean; e != ring->to_use; e = e->next) {
-               ++skge->tx_avail;
-               skge_tx_free(skge->hw, e);
-       }
-       ring->to_clean = e;
-       spin_unlock_irqrestore(&skge->tx_lock, flags);
+       spin_lock_bh(&skge->tx_lock);
+       skge_tx_complete(skge, skge->tx_ring.to_use);
+       netif_wake_queue(skge->netdev);
+       spin_unlock_bh(&skge->tx_lock);
 }
 
 static void skge_tx_timeout(struct net_device *dev)
@@ -2332,18 +2453,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 running = netif_running(dev);
+       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);
 
-       if (running)
-               skge_down(dev);
        dev->mtu = new_mtu;
-       if (running)
-               skge_up(dev);
+
+       err = skge_up(dev);
+       if (err)
+               dev_close(dev);
 
        return err;
 }
@@ -2358,8 +2484,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)
@@ -2397,7 +2521,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));
@@ -2426,6 +2550,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)
@@ -2435,82 +2567,132 @@ 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)
-{
-       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);
-
-       if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF))
-               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++;
-       }
-}
 
 /* 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,
-                                         unsigned int len)
+                                         u32 control, u32 status, u16 csum)
 {
-       struct sk_buff *nskb, *skb;
+       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) {
-               nskb = skge_rx_alloc(skge->netdev, len + NET_IP_ALIGN);
-               if (unlikely(!nskb))
-                       return NULL;
+               skb = alloc_skb(len + 2, GFP_ATOMIC);
+               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(nskb->data, e->skb->data, len);
+               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);
-
-               if (skge->rx_csum) {
-                       struct skge_rx_desc *rd = e->desc;
-                       nskb->csum = le16_to_cpu(rd->csum2);
-                       nskb->ip_summed = CHECKSUM_HW;
-               }
                skge_rx_reuse(e, skge->rx_buf_size);
-               return nskb;
        } else {
-               nskb = skge_rx_alloc(skge->netdev, skge->rx_buf_size);
-               if (unlikely(!nskb))
-                       return NULL;
+               struct sk_buff *nskb;
+               nskb = alloc_skb(skge->rx_buf_size + NET_IP_ALIGN, GFP_ATOMIC);
+               if (!nskb)
+                       goto resubmit;
 
+               skb_reserve(nskb, NET_IP_ALIGN);
                pci_unmap_single(skge->hw->pdev,
                                 pci_unmap_addr(e, mapaddr),
                                 pci_unmap_len(e, maplen),
                                 PCI_DMA_FROMDEVICE);
                skb = e->skb;
-               if (skge->rx_csum) {
-                       struct skge_rx_desc *rd = e->desc;
-                       skb->csum = le16_to_cpu(rd->csum2);
-                       skb->ip_summed = CHECKSUM_HW;
-               }
-
+               prefetch(skb->data);
                skge_rx_setup(skge, e, nskb, skge->rx_buf_size);
-               return skb;
        }
+
+       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 void skge_tx_done(struct skge_port *skge)
+{
+       struct skge_ring *ring = &skge->tx_ring;
+       struct skge_element *e, *last;
+
+       spin_lock(&skge->tx_lock);
+       last = ring->to_clean;
+       for (e = ring->to_clean; e != ring->to_use; e = e->next) {
+               struct skge_tx_desc *td = e->desc;
+
+               if (td->control & BMU_OWN)
+                       break;
+
+               if (td->control & BMU_EOF) {
+                       last = e->next;
+                       if (unlikely(netif_msg_tx_done(skge)))
+                               printk(KERN_DEBUG PFX "%s: tx done slot %td\n",
+                                      skge->netdev->name, e - ring->start);
+               }
+       }
+
+       skge_tx_complete(skge, last);
+
+       skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F);
+
+       if (skge_avail(&skge->tx_ring) > MAX_SKB_FRAGS + 1)
+               netif_wake_queue(skge->netdev);
+
+       spin_unlock(&skge->tx_lock);
+}
 
 static int skge_poll(struct net_device *dev, int *budget)
 {
@@ -2518,53 +2700,35 @@ static int skge_poll(struct net_device *dev, int *budget)
        struct skge_hw *hw = skge->hw;
        struct skge_ring *ring = &skge->rx_ring;
        struct skge_element *e;
-       unsigned int to_do = min(dev->quota, *budget);
-       unsigned int work_done = 0;
+       int to_do = min(dev->quota, *budget);
+       int work_done = 0;
 
-       pr_debug("skge_poll\n");
+       skge_tx_done(skge);
 
-       for (e = ring->to_clean; 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;
-               u32 control, len, status;
+               u32 control;
 
                rmb();
                control = rd->control;
                if (control & BMU_OWN)
                        break;
 
-               len = control & BMU_BBC;
-               status = rd->status;
-
-               if (unlikely((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF)
-                            || bad_phy_status(hw, status))) {
-                       skge_rx_error(skge, e - ring->start, control, status);
-                       skge_rx_reuse(e, skge->rx_buf_size);
-                       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 = skge_rx_get(skge, e, len);
+               skb = skge_rx_get(skge, e, control, rd->status,
+                                 le16_to_cpu(rd->csum2));
                if (likely(skb)) {
-                       skb_put(skb, len);
-                       skb->protocol = eth_type_trans(skb, dev);
-
                        dev->last_rx = jiffies;
                        netif_receive_skb(skb);
 
                        ++work_done;
-               } else
-                       skge_rx_reuse(e, skge->rx_buf_size);
+               }
        }
        ring->to_clean = e;
 
        /* restart receiver */
        wmb();
-       skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR),
-                   CSR_START | CSR_IRQ_CL_F);
+       skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), CSR_START);
 
        *budget -= work_done;
        dev->quota -= work_done;
@@ -2572,46 +2736,13 @@ static int skge_poll(struct net_device *dev, int *budget)
        if (work_done >=  to_do)
                return 1; /* not 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();
-       return 0;
-}
-
-static inline void skge_tx_intr(struct net_device *dev)
-{
-       struct skge_port *skge = netdev_priv(dev);
-       struct skge_hw *hw = skge->hw;
-       struct skge_ring *ring = &skge->tx_ring;
-       struct skge_element *e;
-
-       spin_lock(&skge->tx_lock);
-       for (e = ring->to_clean; e != ring->to_use; e = e->next) {
-               struct skge_tx_desc *td = e->desc;
-               u32 control;
-
-               rmb();
-               control = td->control;
-               if (control & BMU_OWN)
-                       break;
+       netif_rx_complete(dev);
+       mmiowb();
 
-               if (unlikely(netif_msg_tx_done(skge)))
-                       printk(KERN_DEBUG PFX "%s: tx done slot %td status 0x%x\n",
-                              dev->name, e - ring->start, td->status);
+       hw->intr_mask |= skge->port == 0 ? (IS_R1_F|IS_XA1_F) : (IS_R2_F|IS_XA2_F);
+       skge_write32(hw, B0_IMSK, hw->intr_mask);
 
-               skge_tx_free(hw, e);
-               e->skb = NULL;
-               ++skge->tx_avail;
-       }
-       ring->to_clean = e;
-       skge_write8(hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F);
-
-       if (skge->tx_avail > MAX_SKB_FRAGS + 1)
-               netif_wake_queue(dev);
-
-       spin_unlock(&skge->tx_lock);
+       return 0;
 }
 
 /* Parity errors seem to happen when Genesis is connected to a switch
@@ -2636,17 +2767,6 @@ static void skge_mac_parity(struct skge_hw *hw, int port)
                            ? GMF_CLI_TX_FC : GMF_CLI_TX_PE);
 }
 
-static void skge_pci_clear(struct skge_hw *hw)
-{
-       u16 status;
-
-       pci_read_config_word(hw->pdev, PCI_STATUS, &status);
-       skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
-       pci_write_config_word(hw->pdev, PCI_STATUS,
-                             status | PCI_STATUS_ERROR_BITS);
-       skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
-}
-
 static void skge_mac_intr(struct skge_hw *hw, int port)
 {
        if (hw->chip_id == CHIP_ID_GENESIS)
@@ -2663,25 +2783,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) {
@@ -2700,29 +2808,46 @@ static void skge_error_irq(struct skge_hw *hw)
        if (hwstatus & IS_M2_PAR_ERR)
                skge_mac_parity(hw, 1);
 
-       if (hwstatus & IS_R1_PAR_ERR)
+       if (hwstatus & IS_R1_PAR_ERR) {
+               printk(KERN_ERR PFX "%s: receive queue parity error\n",
+                      hw->dev[0]->name);
                skge_write32(hw, B0_R1_CSR, CSR_IRQ_CL_P);
+       }
 
-       if (hwstatus & IS_R2_PAR_ERR)
+       if (hwstatus & IS_R2_PAR_ERR) {
+               printk(KERN_ERR PFX "%s: receive queue parity error\n",
+                      hw->dev[1]->name);
                skge_write32(hw, B0_R2_CSR, CSR_IRQ_CL_P);
+       }
 
        if (hwstatus & (IS_IRQ_MST_ERR|IS_IRQ_STAT)) {
-               printk(KERN_ERR PFX "hardware error detected (status 0x%x)\n",
-                      hwstatus);
+               u16 pci_status, pci_cmd;
 
-               skge_pci_clear(hw);
+               pci_read_config_word(hw->pdev, PCI_COMMAND, &pci_cmd);
+               pci_read_config_word(hw->pdev, PCI_STATUS, &pci_status);
 
+               printk(KERN_ERR PFX "%s: PCI error cmd=%#x status=%#x\n",
+                              pci_name(hw->pdev), pci_cmd, pci_status);
+
+               /* Write the error bits back to clear them. */
+               pci_status &= PCI_STATUS_ERROR_BITS;
+               skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+               pci_write_config_word(hw->pdev, PCI_COMMAND,
+                                     pci_cmd | PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+               pci_write_config_word(hw->pdev, PCI_STATUS, pci_status);
+               skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+               /* 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",
-                              hwstatus);
+                       printk(KERN_INFO PFX "unable to clear error (so ignoring them)\n");
                        hw->intr_mask &= ~IS_HW_ERR;
                }
        }
 }
 
 /*
- * 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.
  */
@@ -2732,12 +2857,11 @@ static void skge_extirq(unsigned long data)
        int port;
 
        spin_lock(&hw->phy_lock);
-       for (port = 0; port < 2; port++) {
+       for (port = 0; port < hw->ports; port++) {
                struct net_device *dev = hw->dev[port];
+               struct skge_port *skge = netdev_priv(dev);
 
-               if (dev && netif_running(dev)) {
-                       struct skge_port *skge = netdev_priv(dev);
-
+               if (netif_running(dev)) {
                        if (hw->chip_id != CHIP_ID_GENESIS)
                                yukon_phy_intr(skge);
                        else
@@ -2746,36 +2870,39 @@ static void skge_extirq(unsigned long data)
        }
        spin_unlock(&hw->phy_lock);
 
-       local_irq_disable();
        hw->intr_mask |= IS_EXT_REG;
        skge_write32(hw, B0_IMSK, hw->intr_mask);
-       local_irq_enable();
 }
 
 static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct skge_hw *hw = dev_id;
-       u32 status = skge_read32(hw, B0_SP_ISRC);
+       u32 status;
 
-       if (status == 0 || status == ~0) /* hotplug or shared irq */
+       /* Reading this register masks IRQ */
+       status = skge_read32(hw, B0_SP_ISRC);
+       if (status == 0)
                return IRQ_NONE;
 
-       status &= hw->intr_mask;
-       if (status & IS_R1_F) {
-               hw->intr_mask &= ~IS_R1_F;
+       if (status & IS_EXT_REG) {
+               hw->intr_mask &= ~IS_EXT_REG;
+               tasklet_schedule(&hw->ext_tasklet);
+       }
+
+       if (status & (IS_R1_F|IS_XA1_F)) {
+               skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F);
+               hw->intr_mask &= ~(IS_R1_F|IS_XA1_F);
                netif_rx_schedule(hw->dev[0]);
        }
 
-       if (status & IS_R2_F) {
-               hw->intr_mask &= ~IS_R2_F;
+       if (status & (IS_R2_F|IS_XA2_F)) {
+               skge_write8(hw, Q_ADDR(Q_R2, Q_CSR), CSR_IRQ_CL_F);
+               hw->intr_mask &= ~(IS_R2_F|IS_XA2_F);
                netif_rx_schedule(hw->dev[1]);
        }
 
-       if (status & IS_XA1_F)
-               skge_tx_intr(hw->dev[0]);
-
-       if (status & IS_XA2_F)
-               skge_tx_intr(hw->dev[1]);
+       if (likely((status & hw->intr_mask) == 0))
+               return IRQ_HANDLED;
 
        if (status & IS_PA_TO_RX1) {
                struct skge_port *skge = netdev_priv(hw->dev[0]);
@@ -2804,11 +2931,6 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
        if (status & IS_HW_ERR)
                skge_error_irq(hw);
 
-       if (status & IS_EXT_REG) {
-               hw->intr_mask &= ~IS_EXT_REG;
-               tasklet_schedule(&hw->ext_tasklet);
-       }
-
        skge_write32(hw, B0_IMSK, hw->intr_mask);
 
        return IRQ_HANDLED;
@@ -2828,21 +2950,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 {
@@ -2875,8 +3005,9 @@ static const char *skge_board_name(const struct skge_hw *hw)
  */
 static int skge_reset(struct skge_hw *hw)
 {
-       u16 ctst;
-       u8 t8, mac_cfg;
+       u32 reg;
+       u16 ctst, pci_status;
+       u8 t8, mac_cfg, pmd_type, phy_type;
        int i;
 
        ctst = skge_read16(hw, B0_CTST);
@@ -2886,8 +3017,13 @@ static int skge_reset(struct skge_hw *hw)
        skge_write8(hw, B0_CTST, CS_RST_CLR);
 
        /* clear PCI errors, if any */
-       skge_pci_clear(hw);
+       skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+       skge_write8(hw, B2_TST_CTRL2, 0);
 
+       pci_read_config_word(hw->pdev, PCI_STATUS, &pci_status);
+       pci_write_config_word(hw->pdev, PCI_STATUS,
+                             pci_status | PCI_STATUS_ERROR_BITS);
+       skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
        skge_write8(hw, B0_CTST, CS_MRST_CLR);
 
        /* restore CLK_RUN bits (for Yukon-Lite) */
@@ -2895,18 +3031,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;
@@ -2914,13 +3051,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:
@@ -2948,12 +3082,32 @@ static int skge_reset(struct skge_hw *hw)
        else
                hw->ram_size = t8 * 4096;
 
+       hw->intr_mask = IS_HW_ERR | IS_EXT_REG | IS_PORT_1;
+       if (hw->ports > 1)
+               hw->intr_mask |= IS_PORT_2;
+
        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);
@@ -2994,12 +3148,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)
@@ -3028,6 +3178,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)
@@ -3077,6 +3228,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);
@@ -3103,13 +3255,15 @@ static int __devinit skge_probe(struct pci_dev *pdev,
        struct skge_hw *hw;
        int err, using_dac = 0;
 
-       if ((err = pci_enable_device(pdev))) {
+       err = pci_enable_device(pdev);
+       if (err) {
                printk(KERN_ERR PFX "%s cannot enable PCI device\n",
                       pci_name(pdev));
                goto err_out;
        }
 
-       if ((err = pci_request_regions(pdev, DRV_NAME))) {
+       err = pci_request_regions(pdev, DRV_NAME);
+       if (err) {
                printk(KERN_ERR PFX "%s cannot obtain PCI resources\n",
                       pci_name(pdev));
                goto err_out_disable_pdev;
@@ -3117,16 +3271,22 @@ static int __devinit skge_probe(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
-       if (!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK)))
+       if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) {
                using_dac = 1;
-       else if (!(err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
+               err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+       } else if (!(err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
+               using_dac = 0;
+               err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+       }
+
+       if (err) {
                printk(KERN_ERR PFX "%s no usable DMA configuration\n",
                       pci_name(pdev));
                goto err_out_free_regions;
        }
 
 #ifdef __BIG_ENDIAN
-       /* byte swap decriptors in hardware */
+       /* byte swap descriptors in hardware */
        {
                u32 reg;
 
@@ -3137,14 +3297,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);
@@ -3156,7 +3315,8 @@ static int __devinit skge_probe(struct pci_dev *pdev,
                goto err_out_free_hw;
        }
 
-       if ((err = request_irq(pdev->irq, skge_intr, SA_SHIRQ, DRV_NAME, hw))) {
+       err = request_irq(pdev->irq, skge_intr, SA_SHIRQ, DRV_NAME, hw);
+       if (err) {
                printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
                       pci_name(pdev), pdev->irq);
                goto err_out_iounmap;
@@ -3167,14 +3327,15 @@ 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);
 
        if ((dev = skge_devinit(hw, 0, using_dac)) == NULL)
                goto err_out_led_off;
 
-       if ((err = register_netdev(dev))) {
+       err = register_netdev(dev);
+       if (err) {
                printk(KERN_ERR PFX "%s: cannot register net device\n",
                       pci_name(pdev));
                goto err_out_free_netdev;
@@ -3227,6 +3388,10 @@ 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_write8(hw, B0_CTST, CS_RST_SET);
+
        tasklet_kill(&hw->ext_tasklet);
 
        free_irq(pdev->irq, hw);
@@ -3235,7 +3400,7 @@ 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);
@@ -3254,7 +3419,10 @@ static int skge_suspend(struct pci_dev *pdev, pm_message_t 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;
@@ -3284,8 +3452,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;