sky2: Flow control frames recorded as dropped packets
[safe/jmp/linux-2.6] / drivers / net / sky2.c
index db92508..7443622 100644 (file)
@@ -50,7 +50,7 @@
 #include "sky2.h"
 
 #define DRV_NAME               "sky2"
-#define DRV_VERSION            "1.22"
+#define DRV_VERSION            "1.26"
 #define PFX                    DRV_NAME " "
 
 /*
 #define RX_MAX_PENDING         (RX_LE_SIZE/6 - 2)
 #define RX_DEF_PENDING         RX_MAX_PENDING
 
-#define TX_RING_SIZE           512
-#define TX_DEF_PENDING         (TX_RING_SIZE - 1)
-#define TX_MIN_PENDING         64
-#define MAX_SKB_TX_LE          (4 + (sizeof(dma_addr_t)/sizeof(u32))*MAX_SKB_FRAGS)
+/* This is the worst case number of transmit list elements for a single skb:
+   VLAN:GSO + CKSUM + Data + skb_frags * DMA */
+#define MAX_SKB_TX_LE  (2 + (sizeof(dma_addr_t)/sizeof(u32))*(MAX_SKB_FRAGS+1))
+#define TX_MIN_PENDING         (MAX_SKB_TX_LE+1)
+#define TX_MAX_PENDING         4096
+#define TX_DEF_PENDING         127
 
 #define STATUS_RING_SIZE       2048    /* 2 ports * (TX + 2*RX) */
 #define STATUS_LE_BYTES                (STATUS_RING_SIZE*sizeof(struct sky2_status_le))
@@ -100,6 +102,7 @@ MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
 static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */
+       { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E01) }, /* SK-9E21M */
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) },    /* DGE-560T */
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4001) },    /* DGE-550SX */
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B02) },    /* DGE-560SX */
@@ -137,6 +140,7 @@ static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
        { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */
        { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */
        { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */
+       { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4381) }, /* 88E8059 */
        { 0 }
 };
 
@@ -254,6 +258,9 @@ static void sky2_power_on(struct sky2_hw *hw)
 
                sky2_read32(hw, B2_GP_IO);
        }
+
+       /* Turn on "driver loaded" LED */
+       sky2_write16(hw, B0_CTST, Y2_LED_STAT_ON);
 }
 
 static void sky2_power_aux(struct sky2_hw *hw)
@@ -267,11 +274,15 @@ static void sky2_power_aux(struct sky2_hw *hw)
                            Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
                            Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
 
-       /* switch power to VAUX */
-       if (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL)
+       /* switch power to VAUX if supported and PME from D3cold */
+       if ( (sky2_read32(hw, B0_CTST) & Y2_VAUX_AVAIL) &&
+            pci_pme_capable(hw->pdev, PCI_D3cold))
                sky2_write8(hw, B0_POWER_CTRL,
                            (PC_VAUX_ENA | PC_VCC_ENA |
                             PC_VAUX_ON | PC_VCC_OFF));
+
+       /* turn off "driver loaded LED" */
+       sky2_write16(hw, B0_CTST, Y2_LED_STAT_OFF);
 }
 
 static void sky2_gmac_reset(struct sky2_hw *hw, unsigned port)
@@ -321,7 +332,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
        struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
        u16 ctrl, ct1000, adv, pg, ledctrl, ledover, reg;
 
-       if (sky2->autoneg == AUTONEG_ENABLE &&
+       if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
            !(hw->flags & SKY2_HW_NEWER_PHY)) {
                u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
 
@@ -363,8 +374,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                        ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
 
                        /* downshift on PHY 88E1112 and 88E1149 is changed */
-                       if (sky2->autoneg == AUTONEG_ENABLE
-                           && (hw->flags & SKY2_HW_NEWER_PHY)) {
+                       if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
+                            (hw->flags & SKY2_HW_NEWER_PHY)) {
                                /* set downshift counter to 3x and enable downshift */
                                ctrl &= ~PHY_M_PC_DSC_MSK;
                                ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA;
@@ -408,7 +419,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
        adv = PHY_AN_CSMA;
        reg = 0;
 
-       if (sky2->autoneg == AUTONEG_ENABLE) {
+       if (sky2->flags & SKY2_FLAG_AUTO_SPEED) {
                if (sky2_is_copper(hw)) {
                        if (sky2->advertising & ADVERTISED_1000baseT_Full)
                                ct1000 |= PHY_M_1000C_AFD;
@@ -423,14 +434,11 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                        if (sky2->advertising & ADVERTISED_10baseT_Half)
                                adv |= PHY_M_AN_10_HD;
 
-                       adv |= copper_fc_adv[sky2->flow_mode];
                } else {        /* special defines for FIBER (88E1040S only) */
                        if (sky2->advertising & ADVERTISED_1000baseT_Full)
                                adv |= PHY_M_AN_1000X_AFD;
                        if (sky2->advertising & ADVERTISED_1000baseT_Half)
                                adv |= PHY_M_AN_1000X_AHD;
-
-                       adv |= fiber_fc_adv[sky2->flow_mode];
                }
 
                /* Restart Auto-negotiation */
@@ -439,8 +447,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                /* forced speed/duplex settings */
                ct1000 = PHY_M_1000C_MSE;
 
-               /* Disable auto update for duplex flow control and speed */
-               reg |= GM_GPCR_AU_ALL_DIS;
+               /* Disable auto update for duplex flow control and duplex */
+               reg |= GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_SPD_DIS;
 
                switch (sky2->speed) {
                case SPEED_1000:
@@ -458,8 +466,15 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                        ctrl |= PHY_CT_DUP_MD;
                } else if (sky2->speed < SPEED_1000)
                        sky2->flow_mode = FC_NONE;
+       }
 
-
+       if (sky2->flags & SKY2_FLAG_AUTO_PAUSE) {
+               if (sky2_is_copper(hw))
+                       adv |= copper_fc_adv[sky2->flow_mode];
+               else
+                       adv |= fiber_fc_adv[sky2->flow_mode];
+       } else {
+               reg |= GM_GPCR_AU_FCT_DIS;
                reg |= gm_fc_disable[sky2->flow_mode];
 
                /* Forward pause packets to GMAC? */
@@ -589,12 +604,23 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
                /* apply workaround for integrated resistors calibration */
                gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17);
                gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60);
+       } else if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
+               /* apply fixes in PHY AFE */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
+
+               /* apply RDAC termination workaround */
+               gm_phy_write(hw, port, 24, 0x2800);
+               gm_phy_write(hw, port, 23, 0x2001);
+
+               /* set page register back to 0 */
+               gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
        } else if (hw->chip_id != CHIP_ID_YUKON_EX &&
                   hw->chip_id < CHIP_ID_YUKON_SUPR) {
                /* no effect on Yukon-XL */
                gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
 
-               if (sky2->autoneg == AUTONEG_DISABLE || sky2->speed == SPEED_100) {
+               if (!(sky2->flags & SKY2_FLAG_AUTO_SPEED) ||
+                   sky2->speed == SPEED_100) {
                        /* turn on 100 Mbps LED (LED_LINK100) */
                        ledover |= PHY_M_LED_MO_100(MO_LED_ON);
                }
@@ -605,7 +631,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
        }
 
        /* Enable phy interrupt on auto-negotiation complete (or link up) */
-       if (sky2->autoneg == AUTONEG_ENABLE)
+       if (sky2->flags & SKY2_FLAG_AUTO_SPEED)
                gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
        else
                gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
@@ -661,7 +687,9 @@ static void sky2_phy_power_down(struct sky2_hw *hw, unsigned port)
 
        /* setup General Purpose Control Register */
        gma_write16(hw, port, GM_GP_CTRL,
-                   GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 | GM_GPCR_AU_ALL_DIS);
+                   GM_GPCR_FL_PASS | GM_GPCR_SPEED_100 |
+                   GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS |
+                   GM_GPCR_AU_SPD_DIS);
 
        if (hw->chip_id != CHIP_ID_YUKON_EC) {
                if (hw->chip_id == CHIP_ID_YUKON_EC_U) {
@@ -749,7 +777,7 @@ static void sky2_wol_init(struct sky2_port *sky2)
        if (sky2->wol & WAKE_MAGIC)
                ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT;
        else
-               ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;;
+               ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;
 
        ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT;
        sky2_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl);
@@ -770,8 +798,7 @@ static void sky2_set_tx_stfwd(struct sky2_hw *hw, unsigned port)
 
        if ( (hw->chip_id == CHIP_ID_YUKON_EX &&
              hw->chip_rev != CHIP_REV_YU_EX_A0) ||
-            hw->chip_id == CHIP_ID_YUKON_FE_P ||
-            hw->chip_id == CHIP_ID_YUKON_SUPR) {
+            hw->chip_id >= CHIP_ID_YUKON_FE_P) {
                /* Yukon-Extreme B0 and further Extreme devices */
                /* enable Store & Forward mode for TX */
 
@@ -909,8 +936,14 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
 
        /* On chips without ram buffer, pause is controled by MAC level */
        if (!(hw->flags & SKY2_HW_RAM_BUFFER)) {
-               sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8);
-               sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8);
+               /* Pause threshold is scaled by 8 in bytes */
+               if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+                   hw->chip_rev == CHIP_REV_YU_FE2_A0)
+                       reg = 1568 / 8;
+               else
+                       reg = 1024 / 8;
+               sky2_write16(hw, SK_REG(port, RX_GMF_UP_THR), reg);
+               sky2_write16(hw, SK_REG(port, RX_GMF_LP_THR), 768 / 8);
 
                sky2_set_tx_stfwd(hw, port);
        }
@@ -977,23 +1010,23 @@ static void sky2_qset(struct sky2_hw *hw, u16 q)
  * hardware and driver list elements
  */
 static void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr,
-                                     u64 addr, u32 last)
+                              dma_addr_t addr, u32 last)
 {
        sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
        sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_CLR);
-       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), addr >> 32);
-       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), (u32) addr);
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), upper_32_bits(addr));
+       sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), lower_32_bits(addr));
        sky2_write16(hw, Y2_QADDR(qaddr, PREF_UNIT_LAST_IDX), last);
        sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_OP_ON);
 
        sky2_read32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL));
 }
 
-static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2)
+static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2, u16 *slot)
 {
-       struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod;
+       struct sky2_tx_le *le = sky2->tx_le + *slot;
 
-       sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE);
+       *slot = RING_NEXT(*slot, sky2->tx_ring_size);
        le->ctrl = 0;
        return le;
 }
@@ -1006,15 +1039,10 @@ static void tx_init(struct sky2_port *sky2)
        sky2->tx_tcpsum = 0;
        sky2->tx_last_mss = 0;
 
-       le = get_tx_le(sky2);
+       le = get_tx_le(sky2, &sky2->tx_prod);
        le->addr = 0;
        le->opcode = OP_ADDR64 | HW_OWNER;
-}
-
-static inline struct tx_ring_info *tx_le_re(struct sky2_port *sky2,
-                                           struct sky2_tx_le *le)
-{
-       return sky2->tx_ring + (le - sky2->tx_le);
+       sky2->tx_last_upper = 0;
 }
 
 /* Update chip's next pointer */
@@ -1050,7 +1078,7 @@ static void sky2_rx_add(struct sky2_port *sky2,  u8 op,
        }
 
        le = sky2_next_rx(sky2);
-       le->addr = cpu_to_le32((u32) map);
+       le->addr = cpu_to_le32(lower_32_bits(map));
        le->length = cpu_to_le16(len);
        le->opcode = op | HW_OWNER;
 }
@@ -1068,21 +1096,46 @@ static void sky2_rx_submit(struct sky2_port *sky2,
 }
 
 
-static void sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
+static int sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
                            unsigned size)
 {
        struct sk_buff *skb = re->skb;
        int i;
 
        re->data_addr = pci_map_single(pdev, skb->data, size, PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(pdev, re->data_addr))
+               goto mapping_error;
+
        pci_unmap_len_set(re, data_size, size);
 
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
-               re->frag_addr[i] = pci_map_page(pdev,
-                                               skb_shinfo(skb)->frags[i].page,
-                                               skb_shinfo(skb)->frags[i].page_offset,
-                                               skb_shinfo(skb)->frags[i].size,
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+               re->frag_addr[i] = pci_map_page(pdev, frag->page,
+                                               frag->page_offset,
+                                               frag->size,
                                                PCI_DMA_FROMDEVICE);
+
+               if (pci_dma_mapping_error(pdev, re->frag_addr[i]))
+                       goto map_page_error;
+       }
+       return 0;
+
+map_page_error:
+       while (--i >= 0) {
+               pci_unmap_page(pdev, re->frag_addr[i],
+                              skb_shinfo(skb)->frags[i].size,
+                              PCI_DMA_FROMDEVICE);
+       }
+
+       pci_unmap_single(pdev, re->data_addr, pci_unmap_len(re, data_size),
+                        PCI_DMA_FROMDEVICE);
+
+mapping_error:
+       if (net_ratelimit())
+               dev_warn(&pdev->dev, "%s: rx mapping error\n",
+                        skb->dev->name);
+       return -EIO;
 }
 
 static void sky2_rx_unmap_skb(struct pci_dev *pdev, struct rx_ring_info *re)
@@ -1113,7 +1166,8 @@ static void rx_set_checksum(struct sky2_port *sky2)
 
        sky2_write32(sky2->hw,
                     Q_ADDR(rxqaddr[sky2->port], Q_CSR),
-                    sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+                    (sky2->flags & SKY2_FLAG_RX_CHECKSUM)
+                    ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
 }
 
 /*
@@ -1195,9 +1249,6 @@ static int sky2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        }
 
        case SIOCSMIIREG:
-               if (!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-
                spin_lock_bh(&sky2->phy_lock);
                err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f,
                                   data->val_in);
@@ -1241,6 +1292,12 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp
 }
 #endif
 
+/* Amount of required worst case padding in rx buffer */
+static inline unsigned sky2_rx_pad(const struct sky2_hw *hw)
+{
+       return (hw->flags & SKY2_HW_RAM_BUFFER) ? 8 : 2;
+}
+
 /*
  * Allocate an skb for receiving. If the MTU is large enough
  * make the skb non-linear with a fragment list of pages.
@@ -1250,6 +1307,11 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
        struct sk_buff *skb;
        int i;
 
+       skb = netdev_alloc_skb(sky2->netdev,
+                              sky2->rx_data_size + sky2_rx_pad(sky2->hw));
+       if (!skb)
+               goto nomem;
+
        if (sky2->hw->flags & SKY2_HW_RAM_BUFFER) {
                unsigned char *start;
                /*
@@ -1258,18 +1320,10 @@ static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
                 * The buffer returned from netdev_alloc_skb is
                 * aligned except if slab debugging is enabled.
                 */
-               skb = netdev_alloc_skb(sky2->netdev, sky2->rx_data_size + 8);
-               if (!skb)
-                       goto nomem;
                start = PTR_ALIGN(skb->data, 8);
                skb_reserve(skb, start - skb->data);
-       } else {
-               skb = netdev_alloc_skb(sky2->netdev,
-                                      sky2->rx_data_size + NET_IP_ALIGN);
-               if (!skb)
-                       goto nomem;
+       } else
                skb_reserve(skb, NET_IP_ALIGN);
-       }
 
        for (i = 0; i < sky2->rx_nfrags; i++) {
                struct page *page = alloc_page(GFP_ATOMIC);
@@ -1317,8 +1371,8 @@ static int sky2_rx_start(struct sky2_port *sky2)
        /* These chips have no ram buffer?
         * MAC Rx RAM Read is controlled by hardware */
        if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
-           (hw->chip_rev == CHIP_REV_YU_EC_U_A1
-            || hw->chip_rev == CHIP_REV_YU_EC_U_B0))
+           (hw->chip_rev == CHIP_REV_YU_EC_U_A1 ||
+            hw->chip_rev == CHIP_REV_YU_EC_U_B0))
                sky2_write32(hw, Q_ADDR(rxq, Q_TEST), F_M_RX_RAM_DIS);
 
        sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
@@ -1354,7 +1408,12 @@ static int sky2_rx_start(struct sky2_port *sky2)
                if (!re->skb)
                        goto nomem;
 
-               sky2_rx_map_skb(hw->pdev, re, sky2->rx_data_size);
+               if (sky2_rx_map_skb(hw->pdev, re, sky2->rx_data_size)) {
+                       dev_kfree_skb(re->skb);
+                       re->skb = NULL;
+                       goto nomem;
+               }
+
                sky2_rx_submit(sky2, re);
        }
 
@@ -1373,12 +1432,92 @@ static int sky2_rx_start(struct sky2_port *sky2)
 
        /* Tell chip about available buffers */
        sky2_rx_update(sky2, rxq);
+
+       if (hw->chip_id == CHIP_ID_YUKON_EX ||
+           hw->chip_id == CHIP_ID_YUKON_SUPR) {
+               /*
+                * Disable flushing of non ASF packets;
+                * must be done after initializing the BMUs;
+                * drivers without ASF support should do this too, otherwise
+                * it may happen that they cannot run on ASF devices;
+                * remember that the MAC FIFO isn't reset during initialization.
+                */
+               sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_MACSEC_FLUSH_OFF);
+       }
+
+       if (hw->chip_id >= CHIP_ID_YUKON_SUPR) {
+               /* Enable RX Home Address & Routing Header checksum fix */
+               sky2_write16(hw, SK_REG(sky2->port, RX_GMF_FL_CTRL),
+                            RX_IPV6_SA_MOB_ENA | RX_IPV6_DA_MOB_ENA);
+
+               /* Enable TX Home Address & Routing Header checksum fix */
+               sky2_write32(hw, Q_ADDR(txqaddr[sky2->port], Q_TEST),
+                            TBMU_TEST_HOME_ADD_FIX_EN | TBMU_TEST_ROUTING_ADD_FIX_EN);
+       }
+
+
+
        return 0;
 nomem:
        sky2_rx_clean(sky2);
        return -ENOMEM;
 }
 
+static int sky2_alloc_buffers(struct sky2_port *sky2)
+{
+       struct sky2_hw *hw = sky2->hw;
+
+       /* must be power of 2 */
+       sky2->tx_le = pci_alloc_consistent(hw->pdev,
+                                          sky2->tx_ring_size *
+                                          sizeof(struct sky2_tx_le),
+                                          &sky2->tx_le_map);
+       if (!sky2->tx_le)
+               goto nomem;
+
+       sky2->tx_ring = kcalloc(sky2->tx_ring_size, sizeof(struct tx_ring_info),
+                               GFP_KERNEL);
+       if (!sky2->tx_ring)
+               goto nomem;
+
+       sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES,
+                                          &sky2->rx_le_map);
+       if (!sky2->rx_le)
+               goto nomem;
+       memset(sky2->rx_le, 0, RX_LE_BYTES);
+
+       sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct rx_ring_info),
+                               GFP_KERNEL);
+       if (!sky2->rx_ring)
+               goto nomem;
+
+       return 0;
+nomem:
+       return -ENOMEM;
+}
+
+static void sky2_free_buffers(struct sky2_port *sky2)
+{
+       struct sky2_hw *hw = sky2->hw;
+
+       if (sky2->rx_le) {
+               pci_free_consistent(hw->pdev, RX_LE_BYTES,
+                                   sky2->rx_le, sky2->rx_le_map);
+               sky2->rx_le = NULL;
+       }
+       if (sky2->tx_le) {
+               pci_free_consistent(hw->pdev,
+                                   sky2->tx_ring_size * sizeof(struct sky2_tx_le),
+                                   sky2->tx_le, sky2->tx_le_map);
+               sky2->tx_le = NULL;
+       }
+       kfree(sky2->tx_ring);
+       kfree(sky2->rx_ring);
+
+       sky2->tx_ring = NULL;
+       sky2->rx_ring = NULL;
+}
+
 /* Bring up network interface. */
 static int sky2_up(struct net_device *dev)
 {
@@ -1386,7 +1525,7 @@ static int sky2_up(struct net_device *dev)
        struct sky2_hw *hw = sky2->hw;
        unsigned port = sky2->port;
        u32 imask, ramsize;
-       int cap, err = -ENOMEM;
+       int cap, err;
        struct net_device *otherdev = hw->dev[sky2->port^1];
 
        /*
@@ -1405,32 +1544,12 @@ static int sky2_up(struct net_device *dev)
 
        netif_carrier_off(dev);
 
-       /* must be power of 2 */
-       sky2->tx_le = pci_alloc_consistent(hw->pdev,
-                                          TX_RING_SIZE *
-                                          sizeof(struct sky2_tx_le),
-                                          &sky2->tx_le_map);
-       if (!sky2->tx_le)
-               goto err_out;
-
-       sky2->tx_ring = kcalloc(TX_RING_SIZE, sizeof(struct tx_ring_info),
-                               GFP_KERNEL);
-       if (!sky2->tx_ring)
+       err = sky2_alloc_buffers(sky2);
+       if (err)
                goto err_out;
 
        tx_init(sky2);
 
-       sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES,
-                                          &sky2->rx_le_map);
-       if (!sky2->rx_le)
-               goto err_out;
-       memset(sky2->rx_le, 0, RX_LE_BYTES);
-
-       sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct rx_ring_info),
-                               GFP_KERNEL);
-       if (!sky2->rx_ring)
-               goto err_out;
-
        sky2_mac_init(hw, port);
 
        /* Register is number of 4K blocks on internal RAM buffer. */
@@ -1438,7 +1557,6 @@ static int sky2_up(struct net_device *dev)
        if (ramsize > 0) {
                u32 rxspace;
 
-               hw->flags |= SKY2_HW_RAM_BUFFER;
                pr_debug(PFX "%s: ram buffer %dK\n", dev->name, ramsize);
                if (ramsize < 16)
                        rxspace = ramsize / 2;
@@ -1460,12 +1578,12 @@ static int sky2_up(struct net_device *dev)
                sky2_write32(hw, Q_ADDR(txqaddr[port], Q_TEST), F_TX_CHK_AUTO_OFF);
 
        /* Set almost empty threshold */
-       if (hw->chip_id == CHIP_ID_YUKON_EC_U
-           && hw->chip_rev == CHIP_REV_YU_EC_U_A0)
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
+           hw->chip_rev == CHIP_REV_YU_EC_U_A0)
                sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV);
 
        sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
-                          TX_RING_SIZE - 1);
+                          sky2->tx_ring_size - 1);
 
 #ifdef SKY2_VLAN_TAG_USED
        sky2_set_vlan_mode(hw, port, sky2->vlgrp != NULL);
@@ -1479,43 +1597,28 @@ static int sky2_up(struct net_device *dev)
        imask = sky2_read32(hw, B0_IMSK);
        imask |= portirq_msk[port];
        sky2_write32(hw, B0_IMSK, imask);
-
-       sky2_set_multicast(dev);
+       sky2_read32(hw, B0_IMSK);
 
        if (netif_msg_ifup(sky2))
                printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
+
        return 0;
 
 err_out:
-       if (sky2->rx_le) {
-               pci_free_consistent(hw->pdev, RX_LE_BYTES,
-                                   sky2->rx_le, sky2->rx_le_map);
-               sky2->rx_le = NULL;
-       }
-       if (sky2->tx_le) {
-               pci_free_consistent(hw->pdev,
-                                   TX_RING_SIZE * sizeof(struct sky2_tx_le),
-                                   sky2->tx_le, sky2->tx_le_map);
-               sky2->tx_le = NULL;
-       }
-       kfree(sky2->tx_ring);
-       kfree(sky2->rx_ring);
-
-       sky2->tx_ring = NULL;
-       sky2->rx_ring = NULL;
+       sky2_free_buffers(sky2);
        return err;
 }
 
 /* Modular subtraction in ring */
-static inline int tx_dist(unsigned tail, unsigned head)
+static inline int tx_inuse(const struct sky2_port *sky2)
 {
-       return (head - tail) & (TX_RING_SIZE - 1);
+       return (sky2->tx_prod - sky2->tx_cons) & (sky2->tx_ring_size - 1);
 }
 
 /* Number of list elements available for next tx */
 static inline int tx_avail(const struct sky2_port *sky2)
 {
-       return sky2->tx_pending - tx_dist(sky2->tx_cons, sky2->tx_prod);
+       return sky2->tx_pending - tx_inuse(sky2);
 }
 
 /* Estimate of number of transmit list elements required */
@@ -1523,11 +1626,13 @@ static unsigned tx_le_req(const struct sk_buff *skb)
 {
        unsigned count;
 
-       count = sizeof(dma_addr_t) / sizeof(u32);
-       count += skb_shinfo(skb)->nr_frags * count;
+       count = (skb_shinfo(skb)->nr_frags + 1)
+               * (sizeof(dma_addr_t) / sizeof(u32));
 
        if (skb_is_gso(skb))
                ++count;
+       else if (sizeof(dma_addr_t) == sizeof(u32))
+               ++count;        /* possible vlan */
 
        if (skb->ip_summed == CHECKSUM_PARTIAL)
                ++count;
@@ -1535,13 +1640,27 @@ static unsigned tx_le_req(const struct sk_buff *skb)
        return count;
 }
 
+static void sky2_tx_unmap(struct pci_dev *pdev, struct tx_ring_info *re)
+{
+       if (re->flags & TX_MAP_SINGLE)
+               pci_unmap_single(pdev, pci_unmap_addr(re, mapaddr),
+                                pci_unmap_len(re, maplen),
+                                PCI_DMA_TODEVICE);
+       else if (re->flags & TX_MAP_PAGE)
+               pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
+                              pci_unmap_len(re, maplen),
+                              PCI_DMA_TODEVICE);
+       re->flags = 0;
+}
+
 /*
  * Put one packet in ring for transmit.
  * A single packet can generate multiple list elements, and
  * the number of ring elements will probably be less than the number
  * of list elements used.
  */
-static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb,
+                                  struct net_device *dev)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
        struct sky2_hw *hw = sky2->hw;
@@ -1549,23 +1668,31 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        struct tx_ring_info *re;
        unsigned i, len;
        dma_addr_t mapping;
+       u32 upper;
+       u16 slot;
        u16 mss;
        u8 ctrl;
 
        if (unlikely(tx_avail(sky2) < tx_le_req(skb)))
                return NETDEV_TX_BUSY;
 
-       if (unlikely(netif_msg_tx_queued(sky2)))
-               printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n",
-                      dev->name, sky2->tx_prod, skb->len);
-
        len = skb_headlen(skb);
        mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE);
 
+       if (pci_dma_mapping_error(hw->pdev, mapping))
+               goto mapping_error;
+
+       slot = sky2->tx_prod;
+       if (unlikely(netif_msg_tx_queued(sky2)))
+               printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n",
+                      dev->name, slot, skb->len);
+
        /* Send high bits if needed */
-       if (sizeof(dma_addr_t) > sizeof(u32)) {
-               le = get_tx_le(sky2);
-               le->addr = cpu_to_le32(upper_32_bits(mapping));
+       upper = upper_32_bits(mapping);
+       if (upper != sky2->tx_last_upper) {
+               le = get_tx_le(sky2, &slot);
+               le->addr = cpu_to_le32(upper);
+               sky2->tx_last_upper = upper;
                le->opcode = OP_ADDR64 | HW_OWNER;
        }
 
@@ -1577,7 +1704,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
                        mss += ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb);
 
                if (mss != sky2->tx_last_mss) {
-                       le = get_tx_le(sky2);
+                       le = get_tx_le(sky2, &slot);
                        le->addr = cpu_to_le32(mss);
 
                        if (hw->flags & SKY2_HW_NEW_LE)
@@ -1593,7 +1720,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */
        if (sky2->vlgrp && vlan_tx_tag_present(skb)) {
                if (!le) {
-                       le = get_tx_le(sky2);
+                       le = get_tx_le(sky2, &slot);
                        le->addr = 0;
                        le->opcode = OP_VLAN|HW_OWNER;
                } else
@@ -1622,7 +1749,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
                        if (tcpsum != sky2->tx_tcpsum) {
                                sky2->tx_tcpsum = tcpsum;
 
-                               le = get_tx_le(sky2);
+                               le = get_tx_le(sky2, &slot);
                                le->addr = cpu_to_le32(tcpsum);
                                le->length = 0; /* initial checksum value */
                                le->ctrl = 1;   /* one packet */
@@ -1631,16 +1758,17 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
                }
        }
 
-       le = get_tx_le(sky2);
-       le->addr = cpu_to_le32((u32) mapping);
+       re = sky2->tx_ring + slot;
+       re->flags = TX_MAP_SINGLE;
+       pci_unmap_addr_set(re, mapaddr, mapping);
+       pci_unmap_len_set(re, maplen, len);
+
+       le = get_tx_le(sky2, &slot);
+       le->addr = cpu_to_le32(lower_32_bits(mapping));
        le->length = cpu_to_le16(len);
        le->ctrl = ctrl;
        le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);
 
-       re = tx_le_re(sky2, le);
-       re->skb = skb;
-       pci_unmap_addr_set(re, mapaddr, mapping);
-       pci_unmap_len_set(re, maplen, len);
 
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
@@ -1648,98 +1776,122 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
                mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset,
                                       frag->size, PCI_DMA_TODEVICE);
 
-               if (sizeof(dma_addr_t) > sizeof(u32)) {
-                       le = get_tx_le(sky2);
-                       le->addr = cpu_to_le32(upper_32_bits(mapping));
-                       le->ctrl = 0;
+               if (pci_dma_mapping_error(hw->pdev, mapping))
+                       goto mapping_unwind;
+
+               upper = upper_32_bits(mapping);
+               if (upper != sky2->tx_last_upper) {
+                       le = get_tx_le(sky2, &slot);
+                       le->addr = cpu_to_le32(upper);
+                       sky2->tx_last_upper = upper;
                        le->opcode = OP_ADDR64 | HW_OWNER;
                }
 
-               le = get_tx_le(sky2);
-               le->addr = cpu_to_le32((u32) mapping);
+               re = sky2->tx_ring + slot;
+               re->flags = TX_MAP_PAGE;
+               pci_unmap_addr_set(re, mapaddr, mapping);
+               pci_unmap_len_set(re, maplen, frag->size);
+
+               le = get_tx_le(sky2, &slot);
+               le->addr = cpu_to_le32(lower_32_bits(mapping));
                le->length = cpu_to_le16(frag->size);
                le->ctrl = ctrl;
                le->opcode = OP_BUFFER | HW_OWNER;
-
-               re = tx_le_re(sky2, le);
-               re->skb = skb;
-               pci_unmap_addr_set(re, mapaddr, mapping);
-               pci_unmap_len_set(re, maplen, frag->size);
        }
 
+       re->skb = skb;
        le->ctrl |= EOP;
 
+       sky2->tx_prod = slot;
+
        if (tx_avail(sky2) <= MAX_SKB_TX_LE)
                netif_stop_queue(dev);
 
        sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod);
 
-       dev->trans_start = jiffies;
+       return NETDEV_TX_OK;
+
+mapping_unwind:
+       for (i = sky2->tx_prod; i != slot; i = RING_NEXT(i, sky2->tx_ring_size)) {
+               re = sky2->tx_ring + i;
+
+               sky2_tx_unmap(hw->pdev, re);
+       }
+
+mapping_error:
+       if (net_ratelimit())
+               dev_warn(&hw->pdev->dev, "%s: tx mapping error\n", dev->name);
+       dev_kfree_skb(skb);
        return NETDEV_TX_OK;
 }
 
 /*
  * Free ring elements from starting at tx_cons until "done"
  *
- * NB: the hardware will tell us about partial completion of multi-part
+ * NB:
+ *  1. The hardware will tell us about partial completion of multi-part
  *     buffers so make sure not to free skb to early.
+ *  2. This may run in parallel start_xmit because the it only
+ *     looks at the tail of the queue of FIFO (tx_cons), not
+ *     the head (tx_prod)
  */
 static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
 {
        struct net_device *dev = sky2->netdev;
-       struct pci_dev *pdev = sky2->hw->pdev;
        unsigned idx;
 
-       BUG_ON(done >= TX_RING_SIZE);
+       BUG_ON(done >= sky2->tx_ring_size);
 
        for (idx = sky2->tx_cons; idx != done;
-            idx = RING_NEXT(idx, TX_RING_SIZE)) {
-               struct sky2_tx_le *le = sky2->tx_le + idx;
+            idx = RING_NEXT(idx, sky2->tx_ring_size)) {
                struct tx_ring_info *re = sky2->tx_ring + idx;
+               struct sk_buff *skb = re->skb;
 
-               switch(le->opcode & ~HW_OWNER) {
-               case OP_LARGESEND:
-               case OP_PACKET:
-                       pci_unmap_single(pdev,
-                                        pci_unmap_addr(re, mapaddr),
-                                        pci_unmap_len(re, maplen),
-                                        PCI_DMA_TODEVICE);
-                       break;
-               case OP_BUFFER:
-                       pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
-                                      pci_unmap_len(re, maplen),
-                                      PCI_DMA_TODEVICE);
-                       break;
-               }
+               sky2_tx_unmap(sky2->hw->pdev, re);
 
-               if (le->ctrl & EOP) {
+               if (skb) {
                        if (unlikely(netif_msg_tx_done(sky2)))
                                printk(KERN_DEBUG "%s: tx done %u\n",
                                       dev->name, idx);
 
                        dev->stats.tx_packets++;
-                       dev->stats.tx_bytes += re->skb->len;
+                       dev->stats.tx_bytes += skb->len;
+
+                       re->skb = NULL;
+                       dev_kfree_skb_any(skb);
 
-                       dev_kfree_skb_any(re->skb);
-                       sky2->tx_next = RING_NEXT(idx, TX_RING_SIZE);
+                       sky2->tx_next = RING_NEXT(idx, sky2->tx_ring_size);
                }
        }
 
        sky2->tx_cons = idx;
        smp_mb();
 
-       if (tx_avail(sky2) > MAX_SKB_TX_LE + 4)
+       /* Wake unless it's detached, and called e.g. from sky2_down() */
+       if (tx_avail(sky2) > MAX_SKB_TX_LE + 4 && netif_device_present(dev))
                netif_wake_queue(dev);
 }
 
-/* Cleanup all untransmitted buffers, assume transmitter not running */
-static void sky2_tx_clean(struct net_device *dev)
+static void sky2_tx_reset(struct sky2_hw *hw, unsigned port)
 {
-       struct sky2_port *sky2 = netdev_priv(dev);
+       /* Disable Force Sync bit and Enable Alloc bit */
+       sky2_write8(hw, SK_REG(port, TXA_CTRL),
+                   TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
 
-       netif_tx_lock_bh(dev);
-       sky2_tx_complete(sky2, sky2->tx_prod);
-       netif_tx_unlock_bh(dev);
+       /* Stop Interval Timer and Limit Counter of Tx Arbiter */
+       sky2_write32(hw, SK_REG(port, TXA_ITI_INI), 0L);
+       sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L);
+
+       /* Reset the PCI FIFO of the async Tx queue */
+       sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR),
+                    BMU_RST_SET | BMU_FIFO_RST);
+
+       /* Reset the Tx prefetch units */
+       sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL),
+                    PREF_UNIT_RST_SET);
+
+       sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+       sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
 }
 
 /* Network shutdown */
@@ -1758,14 +1910,8 @@ static int sky2_down(struct net_device *dev)
        if (netif_msg_ifdown(sky2))
                printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
 
-       /* Disable port IRQ */
-       imask = sky2_read32(hw, B0_IMSK);
-       imask &= ~portirq_msk[port];
-       sky2_write32(hw, B0_IMSK, imask);
-
-       synchronize_irq(hw->pdev->irq);
-
-       sky2_gmac_reset(hw, port);
+       /* Force flow control off */
+       sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
 
        /* Stop transmitter */
        sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP);
@@ -1778,61 +1924,44 @@ static int sky2_down(struct net_device *dev)
        ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA);
        gma_write16(hw, port, GM_GP_CTRL, ctrl);
 
-       /* Make sure no packets are pending */
-       napi_synchronize(&hw->napi);
-
        sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
 
        /* Workaround shared GMAC reset */
-       if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0
-             && port == 0 && hw->dev[1] && netif_running(hw->dev[1])))
+       if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 &&
+             port == 0 && hw->dev[1] && netif_running(hw->dev[1])))
                sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
 
-       /* Disable Force Sync bit and Enable Alloc bit */
-       sky2_write8(hw, SK_REG(port, TXA_CTRL),
-                   TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
-
-       /* Stop Interval Timer and Limit Counter of Tx Arbiter */
-       sky2_write32(hw, SK_REG(port, TXA_ITI_INI), 0L);
-       sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L);
+       sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
 
-       /* Reset the PCI FIFO of the async Tx queue */
-       sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR),
-                    BMU_RST_SET | BMU_FIFO_RST);
-
-       /* Reset the Tx prefetch units */
-       sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL),
-                    PREF_UNIT_RST_SET);
-
-       sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+       /* Force any delayed status interrrupt and NAPI */
+       sky2_write32(hw, STAT_LEV_TIMER_CNT, 0);
+       sky2_write32(hw, STAT_TX_TIMER_CNT, 0);
+       sky2_write32(hw, STAT_ISR_TIMER_CNT, 0);
+       sky2_read8(hw, STAT_ISR_TIMER_CTRL);
 
        sky2_rx_stop(sky2);
 
-       sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
-       sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
-
-       sky2_phy_power_down(hw, port);
+       /* Disable port IRQ */
+       imask = sky2_read32(hw, B0_IMSK);
+       imask &= ~portirq_msk[port];
+       sky2_write32(hw, B0_IMSK, imask);
+       sky2_read32(hw, B0_IMSK);
 
-       /* turn off LED's */
-       sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
+       synchronize_irq(hw->pdev->irq);
+       napi_synchronize(&hw->napi);
 
-       sky2_tx_clean(dev);
-       sky2_rx_clean(sky2);
+       spin_lock_bh(&sky2->phy_lock);
+       sky2_phy_power_down(hw, port);
+       spin_unlock_bh(&sky2->phy_lock);
 
-       pci_free_consistent(hw->pdev, RX_LE_BYTES,
-                           sky2->rx_le, sky2->rx_le_map);
-       kfree(sky2->rx_ring);
+       sky2_tx_reset(hw, port);
 
-       pci_free_consistent(hw->pdev,
-                           TX_RING_SIZE * sizeof(struct sky2_tx_le),
-                           sky2->tx_le, sky2->tx_le_map);
-       kfree(sky2->tx_ring);
+       /* Free any pending frames stuck in HW queue */
+       sky2_tx_complete(sky2, sky2->tx_prod);
 
-       sky2->tx_le = NULL;
-       sky2->rx_le = NULL;
+       sky2_rx_clean(sky2);
 
-       sky2->rx_ring = NULL;
-       sky2->tx_ring = NULL;
+       sky2_free_buffers(sky2);
 
        return 0;
 }
@@ -1908,7 +2037,7 @@ static void sky2_link_down(struct sky2_port *sky2)
 
        netif_carrier_off(sky2->netdev);
 
-       /* Turn on link LED */
+       /* Turn off link LED */
        sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
 
        if (netif_msg_link(sky2))
@@ -1976,8 +2105,8 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
                        sky2->flow_status = FC_TX;
        }
 
-       if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000
-           && !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX))
+       if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 &&
+           !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX))
                sky2->flow_status = FC_NONE;
 
        if (sky2->flow_status & FC_TX)
@@ -2006,7 +2135,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
                printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n",
                       sky2->netdev->name, istatus, phystat);
 
-       if (sky2->autoneg == AUTONEG_ENABLE && (istatus & PHY_M_IS_AN_COMPL)) {
+       if (istatus & PHY_M_IS_AN_COMPL) {
                if (sky2_autoneg_done(sky2, phystat) == 0)
                        sky2_link_up(sky2);
                goto out;
@@ -2029,6 +2158,27 @@ out:
        spin_unlock(&sky2->phy_lock);
 }
 
+/* Special quick link interrupt (Yukon-2 Optima only) */
+static void sky2_qlink_intr(struct sky2_hw *hw)
+{
+       struct sky2_port *sky2 = netdev_priv(hw->dev[0]);
+       u32 imask;
+       u16 phy;
+
+       /* disable irq */
+       imask = sky2_read32(hw, B0_IMSK);
+       imask &= ~Y2_IS_PHY_QLNK;
+       sky2_write32(hw, B0_IMSK, imask);
+
+       /* reset PHY Link Detect */
+       phy = sky2_pci_read16(hw, PSM_CONFIG_REG4);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+       sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1);
+       sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+       sky2_link_up(sky2);
+}
+
 /* Transmit timeout is only called if we are running, carrier is up
  * and tx queue is full (stopped).
  */
@@ -2124,9 +2274,8 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2,
 {
        struct sk_buff *skb;
 
-       skb = netdev_alloc_skb(sky2->netdev, length + 2);
+       skb = netdev_alloc_skb_ip_align(sky2->netdev, length);
        if (likely(skb)) {
-               skb_reserve(skb, 2);
                pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr,
                                            length, PCI_DMA_FROMDEVICE);
                skb_copy_from_linear_data(re->skb, skb->data, length);
@@ -2178,26 +2327,32 @@ static struct sk_buff *receive_new(struct sky2_port *sky2,
                                   struct rx_ring_info *re,
                                   unsigned int length)
 {
-       struct sk_buff *skb, *nskb;
+       struct sk_buff *skb;
+       struct rx_ring_info nre;
        unsigned hdr_space = sky2->rx_data_size;
 
-       /* Don't be tricky about reusing pages (yet) */
-       nskb = sky2_rx_alloc(sky2);
-       if (unlikely(!nskb))
-               return NULL;
+       nre.skb = sky2_rx_alloc(sky2);
+       if (unlikely(!nre.skb))
+               goto nobuf;
+
+       if (sky2_rx_map_skb(sky2->hw->pdev, &nre, hdr_space))
+               goto nomap;
 
        skb = re->skb;
        sky2_rx_unmap_skb(sky2->hw->pdev, re);
-
        prefetch(skb->data);
-       re->skb = nskb;
-       sky2_rx_map_skb(sky2->hw->pdev, re, hdr_space);
+       *re = nre;
 
        if (skb_shinfo(skb)->nr_frags)
                skb_put_frags(skb, hdr_space, length);
        else
                skb_put(skb, length);
        return skb;
+
+nomap:
+       dev_kfree_skb(nre.skb);
+nobuf:
+       return NULL;
 }
 
 /*
@@ -2249,6 +2404,9 @@ okay:
                skb = receive_copy(sky2, re, length);
        else
                skb = receive_new(sky2, re, length);
+
+       dev->stats.rx_dropped += (skb == NULL);
+
 resubmit:
        sky2_rx_submit(sky2, re);
 
@@ -2284,15 +2442,45 @@ error:
        goto resubmit;
 }
 
-/* Transmit complete */
-static inline void sky2_tx_done(struct net_device *dev, u16 last)
+/* Transmit complete */
+static inline void sky2_tx_done(struct net_device *dev, u16 last)
+{
+       struct sky2_port *sky2 = netdev_priv(dev);
+
+       if (netif_running(dev))
+               sky2_tx_complete(sky2, last);
+}
+
+static inline void sky2_skb_rx(const struct sky2_port *sky2,
+                              u32 status, struct sk_buff *skb)
+{
+#ifdef SKY2_VLAN_TAG_USED
+       u16 vlan_tag = be16_to_cpu(sky2->rx_tag);
+       if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
+               if (skb->ip_summed == CHECKSUM_NONE)
+                       vlan_hwaccel_receive_skb(skb, sky2->vlgrp, vlan_tag);
+               else
+                       vlan_gro_receive(&sky2->hw->napi, sky2->vlgrp,
+                                        vlan_tag, skb);
+               return;
+       }
+#endif
+       if (skb->ip_summed == CHECKSUM_NONE)
+               netif_receive_skb(skb);
+       else
+               napi_gro_receive(&sky2->hw->napi, skb);
+}
+
+static inline void sky2_rx_done(struct sky2_hw *hw, unsigned port,
+                               unsigned packets, unsigned bytes)
 {
-       struct sky2_port *sky2 = netdev_priv(dev);
+       if (packets) {
+               struct net_device *dev = hw->dev[port];
 
-       if (netif_running(dev)) {
-               netif_tx_lock(dev);
-               sky2_tx_complete(sky2, last);
-               netif_tx_unlock(dev);
+               dev->stats.rx_packets += packets;
+               dev->stats.rx_bytes += bytes;
+               dev->last_rx = jiffies;
+               sky2_rx_update(netdev_priv(dev), rxqaddr[port]);
        }
 }
 
@@ -2300,7 +2488,8 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last)
 static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
 {
        int work_done = 0;
-       unsigned rx[2] = { 0, 0 };
+       unsigned int total_bytes[2] = { 0 };
+       unsigned int total_packets[2] = { 0 };
 
        rmb();
        do {
@@ -2327,16 +2516,16 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
                le->opcode = 0;
                switch (opcode & ~HW_OWNER) {
                case OP_RXSTAT:
-                       ++rx[port];
+                       total_packets[port]++;
+                       total_bytes[port] += length;
+
                        skb = sky2_receive(dev, length, status);
-                       if (unlikely(!skb)) {
-                               dev->stats.rx_dropped++;
+                       if (!skb)
                                break;
-                       }
 
                        /* This chip reports checksum status differently */
                        if (hw->flags & SKY2_HW_NEW_LE) {
-                               if (sky2->rx_csum &&
+                               if ((sky2->flags & SKY2_FLAG_RX_CHECKSUM) &&
                                    (le->css & (CSS_ISIPV4 | CSS_ISIPV6)) &&
                                    (le->css & CSS_TCPUDPCSOK))
                                        skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -2345,18 +2534,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
                        }
 
                        skb->protocol = eth_type_trans(skb, dev);
-                       dev->stats.rx_packets++;
-                       dev->stats.rx_bytes += skb->len;
-                       dev->last_rx = jiffies;
 
-#ifdef SKY2_VLAN_TAG_USED
-                       if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
-                               vlan_hwaccel_receive_skb(skb,
-                                                        sky2->vlgrp,
-                                                        be16_to_cpu(sky2->rx_tag));
-                       } else
-#endif
-                               netif_receive_skb(skb);
+                       sky2_skb_rx(sky2, status, skb);
 
                        /* Stop after net poll weight */
                        if (++work_done >= to_do)
@@ -2373,7 +2552,7 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
                        /* fall through */
 #endif
                case OP_RXCHKS:
-                       if (!sky2->rx_csum)
+                       if (!(sky2->flags & SKY2_FLAG_RX_CHECKSUM))
                                break;
 
                        /* If this happens then driver assuming wrong format */
@@ -2393,12 +2572,13 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
                        if (likely(status >> 16 == (status & 0xffff))) {
                                skb = sky2->rx_ring[sky2->rx_next].skb;
                                skb->ip_summed = CHECKSUM_COMPLETE;
-                               skb->csum = status & 0xffff;
+                               skb->csum = le16_to_cpu(status);
                        } else {
                                printk(KERN_NOTICE PFX "%s: hardware receive "
                                       "checksum problem (status = %#x)\n",
                                       dev->name, status);
-                               sky2->rx_csum = 0;
+                               sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
+
                                sky2_write32(sky2->hw,
                                             Q_ADDR(rxqaddr[port], Q_CSR),
                                             BMU_DIS_RX_CHKSUM);
@@ -2407,7 +2587,6 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
 
                case OP_TXINDEXLE:
                        /* TX index reports status for both ports */
-                       BUILD_BUG_ON(TX_RING_SIZE > 0x1000);
                        sky2_tx_done(hw->dev[0], status & 0xfff);
                        if (hw->dev[1])
                                sky2_tx_done(hw->dev[1],
@@ -2426,11 +2605,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do, u16 idx)
        sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
 
 exit_loop:
-       if (rx[0])
-               sky2_rx_update(netdev_priv(hw->dev[0]), Q_R1);
-
-       if (rx[1])
-               sky2_rx_update(netdev_priv(hw->dev[1]), Q_R2);
+       sky2_rx_done(hw, 0, total_packets[0], total_bytes[0]);
+       sky2_rx_done(hw, 1, total_packets[1], total_bytes[1]);
 
        return work_done;
 }
@@ -2554,19 +2730,15 @@ static void sky2_mac_intr(struct sky2_hw *hw, unsigned port)
 }
 
 /* This should never happen it is a bug. */
-static void sky2_le_error(struct sky2_hw *hw, unsigned port,
-                         u16 q, unsigned ring_size)
+static void sky2_le_error(struct sky2_hw *hw, unsigned port, u16 q)
 {
        struct net_device *dev = hw->dev[port];
-       struct sky2_port *sky2 = netdev_priv(dev);
-       unsigned idx;
-       const u64 *le = (q == Q_R1 || q == Q_R2)
-               ? (u64 *) sky2->rx_le : (u64 *) sky2->tx_le;
+       u16 idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
 
-       idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
-       printk(KERN_ERR PFX "%s: descriptor error q=%#x get=%u [%llx] put=%u\n",
-              dev->name, (unsigned) q, idx, (unsigned long long) le[idx],
-              (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)));
+       dev_err(&hw->pdev->dev, PFX
+               "%s: descriptor error q=%#x get=%u put=%u\n",
+               dev->name, (unsigned) q, (unsigned) idx,
+               (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX)));
 
        sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK);
 }
@@ -2652,16 +2824,16 @@ static void sky2_err_intr(struct sky2_hw *hw, u32 status)
                sky2_mac_intr(hw, 1);
 
        if (status & Y2_IS_CHK_RX1)
-               sky2_le_error(hw, 0, Q_R1, RX_LE_SIZE);
+               sky2_le_error(hw, 0, Q_R1);
 
        if (status & Y2_IS_CHK_RX2)
-               sky2_le_error(hw, 1, Q_R2, RX_LE_SIZE);
+               sky2_le_error(hw, 1, Q_R2);
 
        if (status & Y2_IS_CHK_TXA1)
-               sky2_le_error(hw, 0, Q_XA1, TX_RING_SIZE);
+               sky2_le_error(hw, 0, Q_XA1);
 
        if (status & Y2_IS_CHK_TXA2)
-               sky2_le_error(hw, 1, Q_XA2, TX_RING_SIZE);
+               sky2_le_error(hw, 1, Q_XA2);
 }
 
 static int sky2_poll(struct napi_struct *napi, int work_limit)
@@ -2680,6 +2852,9 @@ static int sky2_poll(struct napi_struct *napi, int work_limit)
        if (status & Y2_IS_IRQ_PHY2)
                sky2_phy_intr(hw, 1);
 
+       if (status & Y2_IS_PHY_QLNK)
+               sky2_qlink_intr(hw);
+
        while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
                work_done += sky2_status_intr(hw, work_limit - work_done, idx);
 
@@ -2729,6 +2904,7 @@ static u32 sky2_mhz(const struct sky2_hw *hw)
        case CHIP_ID_YUKON_EX:
        case CHIP_ID_YUKON_SUPR:
        case CHIP_ID_YUKON_UL_2:
+       case CHIP_ID_YUKON_OPT:
                return 125;
 
        case CHIP_ID_YUKON_FE:
@@ -2822,6 +2998,12 @@ static int __devinit sky2_init(struct sky2_hw *hw)
                        | SKY2_HW_ADV_POWER_CTL;
                break;
 
+       case CHIP_ID_YUKON_OPT:
+               hw->flags = SKY2_HW_GIGABIT
+                       | SKY2_HW_NEW_LE
+                       | SKY2_HW_ADV_POWER_CTL;
+               break;
+
        default:
                dev_err(&hw->pdev->dev, "unsupported chip type 0x%x\n",
                        hw->chip_id);
@@ -2839,6 +3021,9 @@ static int __devinit sky2_init(struct sky2_hw *hw)
                        ++hw->ports;
        }
 
+       if (sky2_read8(hw, B2_E_0))
+               hw->flags |= SKY2_HW_RAM_BUFFER;
+
        return 0;
 }
 
@@ -2897,6 +3082,54 @@ static void sky2_reset(struct sky2_hw *hw)
                        sky2_write16(hw, SK_REG(i, GMAC_CTRL),
                                     GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON
                                     | GMC_BYP_RETR_ON);
+
+       }
+
+       if (hw->chip_id == CHIP_ID_YUKON_SUPR && hw->chip_rev > CHIP_REV_YU_SU_B0) {
+               /* enable MACSec clock gating */
+               sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS);
+       }
+
+       if (hw->chip_id == CHIP_ID_YUKON_OPT) {
+               u16 reg;
+               u32 msk;
+
+               if (hw->chip_rev == 0) {
+                       /* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */
+                       sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7));
+
+                       /* set PHY Link Detect Timer to 1.1 second (11x 100ms) */
+                       reg = 10;
+               } else {
+                       /* set PHY Link Detect Timer to 0.4 second (4x 100ms) */
+                       reg = 3;
+               }
+
+               reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
+
+               /* reset PHY Link Detect */
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+               sky2_pci_write16(hw, PSM_CONFIG_REG4,
+                                reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT);
+               sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
+
+
+               /* enable PHY Quick Link */
+               msk = sky2_read32(hw, B0_IMSK);
+               msk |= Y2_IS_PHY_QLNK;
+               sky2_write32(hw, B0_IMSK, msk);
+
+               /* check if PSMv2 was running before */
+               reg = sky2_pci_read16(hw, PSM_CONFIG_REG3);
+               if (reg & PCI_EXP_LNKCTL_ASPMC) {
+                       int cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+                       /* restore the PCIe Link Control register */
+                       sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg);
+               }
+               sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+               /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+               sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
        }
 
        /* Clear I2C IRQ noise */
@@ -2906,8 +3139,6 @@ static void sky2_reset(struct sky2_hw *hw)
        sky2_write8(hw, B2_TI_CTRL, TIM_STOP);
        sky2_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ);
 
-       sky2_write8(hw, B0_Y2LED, LED_STAT_ON);
-
        /* Turn off descriptor polling */
        sky2_write32(hw, B28_DPT_CTRL, DPT_STOP);
 
@@ -2975,18 +3206,48 @@ static void sky2_reset(struct sky2_hw *hw)
        sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
 }
 
+/* Take device down (offline).
+ * Equivalent to doing dev_stop() but this does not
+ * inform upper layers of the transistion.
+ */
+static void sky2_detach(struct net_device *dev)
+{
+       if (netif_running(dev)) {
+               netif_tx_lock(dev);
+               netif_device_detach(dev);       /* stop txq */
+               netif_tx_unlock(dev);
+               sky2_down(dev);
+       }
+}
+
+/* Bring device back after doing sky2_detach */
+static int sky2_reattach(struct net_device *dev)
+{
+       int err = 0;
+
+       if (netif_running(dev)) {
+               err = sky2_up(dev);
+               if (err) {
+                       printk(KERN_INFO PFX "%s: could not restart %d\n",
+                              dev->name, err);
+                       dev_close(dev);
+               } else {
+                       netif_device_attach(dev);
+                       sky2_set_multicast(dev);
+               }
+       }
+
+       return err;
+}
+
 static void sky2_restart(struct work_struct *work)
 {
        struct sky2_hw *hw = container_of(work, struct sky2_hw, restart_work);
-       struct net_device *dev;
-       int i, err;
+       int i;
 
        rtnl_lock();
-       for (i = 0; i < hw->ports; i++) {
-               dev = hw->dev[i];
-               if (netif_running(dev))
-                       sky2_down(dev);
-       }
+       for (i = 0; i < hw->ports; i++)
+               sky2_detach(hw->dev[i]);
 
        napi_disable(&hw->napi);
        sky2_write32(hw, B0_IMSK, 0);
@@ -2994,17 +3255,8 @@ static void sky2_restart(struct work_struct *work)
        sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
        napi_enable(&hw->napi);
 
-       for (i = 0; i < hw->ports; i++) {
-               dev = hw->dev[i];
-               if (netif_running(dev)) {
-                       err = sky2_up(dev);
-                       if (err) {
-                               printk(KERN_INFO PFX "%s: could not restart %d\n",
-                                      dev->name, err);
-                               dev_close(dev);
-                       }
-               }
-       }
+       for (i = 0; i < hw->ports; i++)
+               sky2_reattach(hw->dev[i]);
 
        rtnl_unlock();
 }
@@ -3014,6 +3266,27 @@ static inline u8 sky2_wol_supported(const struct sky2_hw *hw)
        return sky2_is_copper(hw) ? (WAKE_PHY | WAKE_MAGIC) : 0;
 }
 
+static void sky2_hw_set_wol(struct sky2_hw *hw)
+{
+       int wol = 0;
+       int i;
+
+       for (i = 0; i < hw->ports; i++) {
+               struct net_device *dev = hw->dev[i];
+               struct sky2_port *sky2 = netdev_priv(dev);
+
+               if (sky2->wol)
+                       wol = 1;
+       }
+
+       if (hw->chip_id == CHIP_ID_YUKON_EC_U ||
+           hw->chip_id == CHIP_ID_YUKON_EX ||
+           hw->chip_id == CHIP_ID_YUKON_FE_P)
+               sky2_write32(hw, B0_CTST, wol ? Y2_HW_WOL_ON : Y2_HW_WOL_OFF);
+
+       device_set_wakeup_enable(&hw->pdev->dev, wol);
+}
+
 static void sky2_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
        const struct sky2_port *sky2 = netdev_priv(dev);
@@ -3027,19 +3300,13 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
        struct sky2_port *sky2 = netdev_priv(dev);
        struct sky2_hw *hw = sky2->hw;
 
-       if ((wol->wolopts & ~sky2_wol_supported(sky2->hw))
-           || !device_can_wakeup(&hw->pdev->dev))
+       if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) ||
+           !device_can_wakeup(&hw->pdev->dev))
                return -EOPNOTSUPP;
 
        sky2->wol = wol->wolopts;
 
-       if (hw->chip_id == CHIP_ID_YUKON_EC_U ||
-           hw->chip_id == CHIP_ID_YUKON_EX ||
-           hw->chip_id == CHIP_ID_YUKON_FE_P)
-               sky2_write32(hw, B0_CTST, sky2->wol
-                            ? Y2_HW_WOL_ON : Y2_HW_WOL_OFF);
-
-       device_set_wakeup_enable(&hw->pdev->dev, sky2->wol);
+       sky2_hw_set_wol(hw);
 
        if (!netif_running(dev))
                sky2_wol_init(sky2);
@@ -3083,7 +3350,8 @@ static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
        }
 
        ecmd->advertising = sky2->advertising;
-       ecmd->autoneg = sky2->autoneg;
+       ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_SPEED)
+               ? AUTONEG_ENABLE : AUTONEG_DISABLE;
        ecmd->duplex = sky2->duplex;
        return 0;
 }
@@ -3095,6 +3363,7 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
        u32 supported = sky2_supported_modes(hw);
 
        if (ecmd->autoneg == AUTONEG_ENABLE) {
+               sky2->flags |= SKY2_FLAG_AUTO_SPEED;
                ecmd->advertising = supported;
                sky2->duplex = -1;
                sky2->speed = -1;
@@ -3136,9 +3405,9 @@ static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 
                sky2->speed = ecmd->speed;
                sky2->duplex = ecmd->duplex;
+               sky2->flags &= ~SKY2_FLAG_AUTO_SPEED;
        }
 
-       sky2->autoneg = ecmd->autoneg;
        sky2->advertising = ecmd->advertising;
 
        if (netif_running(dev)) {
@@ -3208,14 +3477,17 @@ static u32 sky2_get_rx_csum(struct net_device *dev)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
 
-       return sky2->rx_csum;
+       return !!(sky2->flags & SKY2_FLAG_RX_CHECKSUM);
 }
 
 static int sky2_set_rx_csum(struct net_device *dev, u32 data)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
 
-       sky2->rx_csum = data;
+       if (data)
+               sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
+       else
+               sky2->flags &= ~SKY2_FLAG_RX_CHECKSUM;
 
        sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
                     data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
@@ -3233,7 +3505,7 @@ static int sky2_nway_reset(struct net_device *dev)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
 
-       if (!netif_running(dev) || sky2->autoneg != AUTONEG_ENABLE)
+       if (!netif_running(dev) || !(sky2->flags & SKY2_FLAG_AUTO_SPEED))
                return -EINVAL;
 
        sky2_phy_reinit(sky2);
@@ -3473,7 +3745,8 @@ static void sky2_get_pauseparam(struct net_device *dev,
                ecmd->tx_pause = ecmd->rx_pause = 1;
        }
 
-       ecmd->autoneg = sky2->autoneg;
+       ecmd->autoneg = (sky2->flags & SKY2_FLAG_AUTO_PAUSE)
+               ? AUTONEG_ENABLE : AUTONEG_DISABLE;
 }
 
 static int sky2_set_pauseparam(struct net_device *dev,
@@ -3481,7 +3754,11 @@ static int sky2_set_pauseparam(struct net_device *dev,
 {
        struct sky2_port *sky2 = netdev_priv(dev);
 
-       sky2->autoneg = ecmd->autoneg;
+       if (ecmd->autoneg == AUTONEG_ENABLE)
+               sky2->flags |= SKY2_FLAG_AUTO_PAUSE;
+       else
+               sky2->flags &= ~SKY2_FLAG_AUTO_PAUSE;
+
        sky2->flow_mode = sky2_flow(ecmd->rx_pause, ecmd->tx_pause);
 
        if (netif_running(dev))
@@ -3537,7 +3814,7 @@ static int sky2_set_coalesce(struct net_device *dev,
            ecmd->rx_coalesce_usecs_irq > tmax)
                return -EINVAL;
 
-       if (ecmd->tx_max_coalesced_frames >= TX_RING_SIZE-1)
+       if (ecmd->tx_max_coalesced_frames >= sky2->tx_ring_size-1)
                return -EINVAL;
        if (ecmd->rx_max_coalesced_frames > RX_MAX_PENDING)
                return -EINVAL;
@@ -3581,7 +3858,7 @@ static void sky2_get_ringparam(struct net_device *dev,
        ering->rx_max_pending = RX_MAX_PENDING;
        ering->rx_mini_max_pending = 0;
        ering->rx_jumbo_max_pending = 0;
-       ering->tx_max_pending = TX_RING_SIZE - 1;
+       ering->tx_max_pending = TX_MAX_PENDING;
 
        ering->rx_pending = sky2->rx_pending;
        ering->rx_mini_pending = 0;
@@ -3593,27 +3870,20 @@ static int sky2_set_ringparam(struct net_device *dev,
                              struct ethtool_ringparam *ering)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
-       int err = 0;
 
        if (ering->rx_pending > RX_MAX_PENDING ||
            ering->rx_pending < 8 ||
-           ering->tx_pending < MAX_SKB_TX_LE ||
-           ering->tx_pending > TX_RING_SIZE - 1)
+           ering->tx_pending < TX_MIN_PENDING ||
+           ering->tx_pending > TX_MAX_PENDING)
                return -EINVAL;
 
-       if (netif_running(dev))
-               sky2_down(dev);
+       sky2_detach(dev);
 
        sky2->rx_pending = ering->rx_pending;
        sky2->tx_pending = ering->tx_pending;
+       sky2->tx_ring_size = roundup_pow_of_two(sky2->tx_pending+1);
 
-       if (netif_running(dev)) {
-               err = sky2_up(dev);
-               if (err)
-                       dev_close(dev);
-       }
-
-       return err;
+       return sky2_reattach(dev);
 }
 
 static int sky2_get_regs_len(struct net_device *dev)
@@ -3621,6 +3891,50 @@ static int sky2_get_regs_len(struct net_device *dev)
        return 0x4000;
 }
 
+static int sky2_reg_access_ok(struct sky2_hw *hw, unsigned int b)
+{
+       /* This complicated switch statement is to make sure and
+        * only access regions that are unreserved.
+        * Some blocks are only valid on dual port cards.
+        */
+       switch (b) {
+       /* second port */
+       case 5:         /* Tx Arbiter 2 */
+       case 9:         /* RX2 */
+       case 14 ... 15: /* TX2 */
+       case 17: case 19: /* Ram Buffer 2 */
+       case 22 ... 23: /* Tx Ram Buffer 2 */
+       case 25:        /* Rx MAC Fifo 1 */
+       case 27:        /* Tx MAC Fifo 2 */
+       case 31:        /* GPHY 2 */
+       case 40 ... 47: /* Pattern Ram 2 */
+       case 52: case 54: /* TCP Segmentation 2 */
+       case 112 ... 116: /* GMAC 2 */
+               return hw->ports > 1;
+
+       case 0:         /* Control */
+       case 2:         /* Mac address */
+       case 4:         /* Tx Arbiter 1 */
+       case 7:         /* PCI express reg */
+       case 8:         /* RX1 */
+       case 12 ... 13: /* TX1 */
+       case 16: case 18:/* Rx Ram Buffer 1 */
+       case 20 ... 21: /* Tx Ram Buffer 1 */
+       case 24:        /* Rx MAC Fifo 1 */
+       case 26:        /* Tx MAC Fifo 1 */
+       case 28 ... 29: /* Descriptor and status unit */
+       case 30:        /* GPHY 1*/
+       case 32 ... 39: /* Pattern Ram 1 */
+       case 48: case 50: /* TCP Segmentation 1 */
+       case 56 ... 60: /* PCI space */
+       case 80 ... 84: /* GMAC 1 */
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
 /*
  * Returns copy of control register region
  * Note: ethtool_get_regs always provides full size (16k) buffer
@@ -3635,55 +3949,13 @@ static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs,
        regs->version = 1;
 
        for (b = 0; b < 128; b++) {
-               /* This complicated switch statement is to make sure and
-                * only access regions that are unreserved.
-                * Some blocks are only valid on dual port cards.
-                * and block 3 has some special diagnostic registers that
-                * are poison.
-                */
-               switch (b) {
-               case 3:
-                       /* skip diagnostic ram region */
+               /* skip poisonous diagnostic ram region in block 3 */
+               if (b == 3)
                        memcpy_fromio(p + 0x10, io + 0x10, 128 - 0x10);
-                       break;
-
-               /* dual port cards only */
-               case 5:         /* Tx Arbiter 2 */
-               case 9:         /* RX2 */
-               case 14 ... 15: /* TX2 */
-               case 17: case 19: /* Ram Buffer 2 */
-               case 22 ... 23: /* Tx Ram Buffer 2 */
-               case 25:        /* Rx MAC Fifo 1 */
-               case 27:        /* Tx MAC Fifo 2 */
-               case 31:        /* GPHY 2 */
-               case 40 ... 47: /* Pattern Ram 2 */
-               case 52: case 54: /* TCP Segmentation 2 */
-               case 112 ... 116: /* GMAC 2 */
-                       if (sky2->hw->ports == 1)
-                               goto reserved;
-                       /* fall through */
-               case 0:         /* Control */
-               case 2:         /* Mac address */
-               case 4:         /* Tx Arbiter 1 */
-               case 7:         /* PCI express reg */
-               case 8:         /* RX1 */
-               case 12 ... 13: /* TX1 */
-               case 16: case 18:/* Rx Ram Buffer 1 */
-               case 20 ... 21: /* Tx Ram Buffer 1 */
-               case 24:        /* Rx MAC Fifo 1 */
-               case 26:        /* Tx MAC Fifo 1 */
-               case 28 ... 29: /* Descriptor and status unit */
-               case 30:        /* GPHY 1*/
-               case 32 ... 39: /* Pattern Ram 1 */
-               case 48: case 50: /* TCP Segmentation 1 */
-               case 56 ... 60: /* PCI space */
-               case 80 ... 84: /* GMAC 1 */
+               else if (sky2_reg_access_ok(sky2->hw, b))
                        memcpy_fromio(p, io, 128);
-                       break;
-               default:
-reserved:
+               else
                        memset(p, 0, 128);
-               }
 
                p += 128;
                io += 128;
@@ -3857,6 +4129,86 @@ static const struct ethtool_ops sky2_ethtool_ops = {
 
 static struct dentry *sky2_debug;
 
+
+/*
+ * Read and parse the first part of Vital Product Data
+ */
+#define VPD_SIZE       128
+#define VPD_MAGIC      0x82
+
+static const struct vpd_tag {
+       char tag[2];
+       char *label;
+} vpd_tags[] = {
+       { "PN", "Part Number" },
+       { "EC", "Engineering Level" },
+       { "MN", "Manufacturer" },
+       { "SN", "Serial Number" },
+       { "YA", "Asset Tag" },
+       { "VL", "First Error Log Message" },
+       { "VF", "Second Error Log Message" },
+       { "VB", "Boot Agent ROM Configuration" },
+       { "VE", "EFI UNDI Configuration" },
+};
+
+static void sky2_show_vpd(struct seq_file *seq, struct sky2_hw *hw)
+{
+       size_t vpd_size;
+       loff_t offs;
+       u8 len;
+       unsigned char *buf;
+       u16 reg2;
+
+       reg2 = sky2_pci_read16(hw, PCI_DEV_REG2);
+       vpd_size = 1 << ( ((reg2 & PCI_VPD_ROM_SZ) >> 14) + 8);
+
+       seq_printf(seq, "%s Product Data\n", pci_name(hw->pdev));
+       buf = kmalloc(vpd_size, GFP_KERNEL);
+       if (!buf) {
+               seq_puts(seq, "no memory!\n");
+               return;
+       }
+
+       if (pci_read_vpd(hw->pdev, 0, vpd_size, buf) < 0) {
+               seq_puts(seq, "VPD read failed\n");
+               goto out;
+       }
+
+       if (buf[0] != VPD_MAGIC) {
+               seq_printf(seq, "VPD tag mismatch: %#x\n", buf[0]);
+               goto out;
+       }
+       len = buf[1];
+       if (len == 0 || len > vpd_size - 4) {
+               seq_printf(seq, "Invalid id length: %d\n", len);
+               goto out;
+       }
+
+       seq_printf(seq, "%.*s\n", len, buf + 3);
+       offs = len + 3;
+
+       while (offs < vpd_size - 4) {
+               int i;
+
+               if (!memcmp("RW", buf + offs, 2))       /* end marker */
+                       break;
+               len = buf[offs + 2];
+               if (offs + len + 3 >= vpd_size)
+                       break;
+
+               for (i = 0; i < ARRAY_SIZE(vpd_tags); i++) {
+                       if (!memcmp(vpd_tags[i].tag, buf + offs, 2)) {
+                               seq_printf(seq, " %s: %.*s\n",
+                                          vpd_tags[i].label, len, buf + offs + 3);
+                               break;
+                       }
+               }
+               offs += len + 3;
+       }
+out:
+       kfree(buf);
+}
+
 static int sky2_debug_show(struct seq_file *seq, void *v)
 {
        struct net_device *dev = seq->private;
@@ -3866,14 +4218,18 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
        unsigned idx, last;
        int sop;
 
-       if (!netif_running(dev))
-               return -ENETDOWN;
+       sky2_show_vpd(seq, hw);
 
-       seq_printf(seq, "IRQ src=%x mask=%x control=%x\n",
+       seq_printf(seq, "\nIRQ src=%x mask=%x control=%x\n",
                   sky2_read32(hw, B0_ISRC),
                   sky2_read32(hw, B0_IMSK),
                   sky2_read32(hw, B0_Y2_SP_ICR));
 
+       if (!netif_running(dev)) {
+               seq_printf(seq, "network not running\n");
+               return 0;
+       }
+
        napi_disable(&hw->napi);
        last = sky2_read16(hw, STAT_PUT_IDX);
 
@@ -3897,8 +4253,8 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
 
        /* Dump contents of tx ring */
        sop = 1;
-       for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < TX_RING_SIZE;
-            idx = RING_NEXT(idx, TX_RING_SIZE)) {
+       for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < sky2->tx_ring_size;
+            idx = RING_NEXT(idx, sky2->tx_ring_size)) {
                const struct sky2_tx_le *le = sky2->tx_le + idx;
                u32 a = le32_to_cpu(le->addr);
 
@@ -3941,7 +4297,7 @@ static int sky2_debug_show(struct seq_file *seq, void *v)
 
        seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n",
                   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)),
-                  last = sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
+                  sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
                   sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX)));
 
        sky2_read32(hw, B0_Y2_SP_LISR);
@@ -4095,17 +4451,21 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
        sky2->msg_enable = netif_msg_init(debug, default_msg);
 
        /* Auto speed and flow control */
-       sky2->autoneg = AUTONEG_ENABLE;
+       sky2->flags = SKY2_FLAG_AUTO_SPEED | SKY2_FLAG_AUTO_PAUSE;
+       if (hw->chip_id != CHIP_ID_YUKON_XL)
+               sky2->flags |= SKY2_FLAG_RX_CHECKSUM;
+
        sky2->flow_mode = FC_BOTH;
 
        sky2->duplex = -1;
        sky2->speed = -1;
        sky2->advertising = sky2_supported_modes(hw);
-       sky2->rx_csum = (hw->chip_id != CHIP_ID_YUKON_XL);
        sky2->wol = wol;
 
        spin_lock_init(&sky2->phy_lock);
+
        sky2->tx_pending = TX_DEF_PENDING;
+       sky2->tx_ring_size = roundup_pow_of_two(TX_DEF_PENDING+1);
        sky2->rx_pending = RX_DEF_PENDING;
 
        hw->dev[port] = dev;
@@ -4197,69 +4557,6 @@ static int __devinit sky2_test_msi(struct sky2_hw *hw)
        return err;
 }
 
-/*
- * Read and parse the first part of Vital Product Data
- */
-#define VPD_SIZE       128
-#define VPD_MAGIC      0x82
-
-static void __devinit sky2_vpd_info(struct sky2_hw *hw)
-{
-       int cap = pci_find_capability(hw->pdev, PCI_CAP_ID_VPD);
-       const u8 *p;
-       u8 *vpd_buf = NULL;
-       u16 len;
-       static struct vpd_tag {
-               char tag[2];
-               char *label;
-       } vpd_tags[] = {
-               { "PN", "Part Number" },
-               { "EC", "Engineering Level" },
-               { "MN", "Manufacturer" },
-       };
-
-       if (!cap)
-               goto out;
-
-       vpd_buf = kmalloc(VPD_SIZE, GFP_KERNEL);
-       if (!vpd_buf)
-               goto out;
-
-       if (sky2_vpd_read(hw, cap, vpd_buf, 0, VPD_SIZE))
-               goto out;
-
-       if (vpd_buf[0] != VPD_MAGIC)
-               goto out;
-       len = vpd_buf[1];
-       if (len == 0 || len > VPD_SIZE - 4)
-               goto out;
-       p = vpd_buf + 3;
-       dev_info(&hw->pdev->dev, "%.*s\n", len, p);
-       p += len;
-
-       while (p < vpd_buf + VPD_SIZE - 4) {
-               int i;
-
-               if (!memcmp("RW", p, 2))        /* end marker */
-                       break;
-
-               len = p[2];
-               if (len > (p - vpd_buf) - 4)
-                       break;
-
-               for (i = 0; i < ARRAY_SIZE(vpd_tags); i++) {
-                       if (!memcmp(vpd_tags[i].tag, p, 2)) {
-                               printk(KERN_DEBUG " %s: %.*s\n",
-                                      vpd_tags[i].label, len, p + 3);
-                               break;
-                       }
-               }
-               p += len + 3;
-       }
-out:
-       kfree(vpd_buf);
-}
-
 /* This driver supports yukon2 chipset only */
 static const char *sky2_name(u8 chipid, char *buf, int sz)
 {
@@ -4272,9 +4569,11 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
                "FE+",          /* 0xb8 */
                "Supreme",      /* 0xb9 */
                "UL 2",         /* 0xba */
+               "Unknown",      /* 0xbb */
+               "Optima",       /* 0xbc */
        };
 
-       if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_UL_2)
+       if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OPT)
                strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
        else
                snprintf(buf, sz, "(chip %#x)", chipid);
@@ -4287,6 +4586,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
        struct net_device *dev;
        struct sky2_hw *hw;
        int err, using_dac = 0, wol_default;
+       u32 reg;
        char buf1[16];
 
        err = pci_enable_device(pdev);
@@ -4295,6 +4595,22 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
                goto err_out;
        }
 
+       /* Get configuration information
+        * Note: only regular PCI config access once to test for HW issues
+        *       other PCI access through shared memory for speed and to
+        *       avoid MMCONFIG problems.
+        */
+       err = pci_read_config_dword(pdev, PCI_DEV_REG2, &reg);
+       if (err) {
+               dev_err(&pdev->dev, "PCI read config failed\n");
+               goto err_out;
+       }
+
+       if (~reg == 0) {
+               dev_err(&pdev->dev, "PCI configuration read error\n");
+               goto err_out;
+       }
+
        err = pci_request_regions(pdev, DRV_NAME);
        if (err) {
                dev_err(&pdev->dev, "cannot obtain PCI resources\n");
@@ -4304,32 +4620,48 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
        pci_set_master(pdev);
 
        if (sizeof(dma_addr_t) > sizeof(u32) &&
-           !(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) {
+           !(err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))) {
                using_dac = 1;
-               err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
                if (err < 0) {
                        dev_err(&pdev->dev, "unable to obtain 64 bit DMA "
                                "for consistent allocations\n");
                        goto err_out_free_regions;
                }
        } else {
-               err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+               err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
                        dev_err(&pdev->dev, "no usable DMA configuration\n");
                        goto err_out_free_regions;
                }
        }
 
+
+#ifdef __BIG_ENDIAN
+       /* The sk98lin vendor driver uses hardware byte swapping but
+        * this driver uses software swapping.
+        */
+       reg &= ~PCI_REV_DESC;
+       err = pci_write_config_dword(pdev,PCI_DEV_REG2, reg);
+       if (err) {
+               dev_err(&pdev->dev, "PCI write config failed\n");
+               goto err_out_free_regions;
+       }
+#endif
+
        wol_default = device_may_wakeup(&pdev->dev) ? WAKE_MAGIC : 0;
 
        err = -ENOMEM;
-       hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+
+       hw = kzalloc(sizeof(*hw) + strlen(DRV_NAME "@pci:")
+                    + strlen(pci_name(pdev)) + 1, GFP_KERNEL);
        if (!hw) {
                dev_err(&pdev->dev, "cannot allocate hardware struct\n");
                goto err_out_free_regions;
        }
 
        hw->pdev = pdev;
+       sprintf(hw->irq_name, DRV_NAME "@pci:%s", pci_name(pdev));
 
        hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
        if (!hw->regs) {
@@ -4337,18 +4669,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
                goto err_out_free_hw;
        }
 
-#ifdef __BIG_ENDIAN
-       /* The sk98lin vendor driver uses hardware byte swapping but
-        * this driver uses software swapping.
-        */
-       {
-               u32 reg;
-               reg = sky2_pci_read32(hw, PCI_DEV_REG2);
-               reg &= ~PCI_REV_DESC;
-               sky2_pci_write32(hw, PCI_DEV_REG2, reg);
-       }
-#endif
-
        /* ring for status responses */
        hw->st_le = pci_alloc_consistent(pdev, STATUS_LE_BYTES, &hw->st_dma);
        if (!hw->st_le)
@@ -4363,8 +4683,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
 
        sky2_reset(hw);
 
-       sky2_vpd_info(hw);
-
        dev = sky2_init_netdev(hw, 0, using_dac, wol_default);
        if (!dev) {
                err = -ENOMEM;
@@ -4385,11 +4703,13 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
                goto err_out_free_netdev;
        }
 
+       netif_carrier_off(dev);
+
        netif_napi_add(dev, &hw->napi, sky2_poll, NAPI_WEIGHT);
 
        err = request_irq(pdev->irq, sky2_intr,
                          (hw->flags & SKY2_HW_USE_MSI) ? 0 : IRQF_SHARED,
-                         dev->name, hw);
+                         hw->irq_name, hw);
        if (err) {
                dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
                goto err_out_unregister;
@@ -4402,22 +4722,25 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
        if (hw->ports > 1) {
                struct net_device *dev1;
 
+               err = -ENOMEM;
                dev1 = sky2_init_netdev(hw, 1, using_dac, wol_default);
-               if (!dev1)
-                       dev_warn(&pdev->dev, "allocation for second device failed\n");
-               else if ((err = register_netdev(dev1))) {
+               if (dev1 && (err = register_netdev(dev1)) == 0)
+                       sky2_show_addr(dev1);
+               else {
                        dev_warn(&pdev->dev,
                                 "register of second port failed (%d)\n", err);
                        hw->dev[1] = NULL;
-                       free_netdev(dev1);
-               } else
-                       sky2_show_addr(dev1);
+                       hw->ports = 1;
+                       if (dev1)
+                               free_netdev(dev1);
+               }
        }
 
        setup_timer(&hw->watchdog_timer, sky2_watchdog, (unsigned long) hw);
        INIT_WORK(&hw->restart_work, sky2_restart);
 
        pci_set_drvdata(pdev, hw);
+       pdev->d3_delay = 150;
 
        return 0;
 
@@ -4461,7 +4784,6 @@ static void __devexit sky2_remove(struct pci_dev *pdev)
 
        sky2_power_aux(hw);
 
-       sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
        sky2_write8(hw, B0_CTST, CS_RST_SET);
        sky2_read8(hw, B0_CTST);
 
@@ -4493,13 +4815,12 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state)
        del_timer_sync(&hw->watchdog_timer);
        cancel_work_sync(&hw->restart_work);
 
+       rtnl_lock();
        for (i = 0; i < hw->ports; i++) {
                struct net_device *dev = hw->dev[i];
                struct sky2_port *sky2 = netdev_priv(dev);
 
-               netif_device_detach(dev);
-               if (netif_running(dev))
-                       sky2_down(dev);
+               sky2_detach(dev);
 
                if (sky2->wol)
                        sky2_wol_init(sky2);
@@ -4510,6 +4831,7 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state)
        sky2_write32(hw, B0_IMSK, 0);
        napi_disable(&hw->napi);
        sky2_power_aux(hw);
+       rtnl_unlock();
 
        pci_save_state(pdev);
        pci_enable_wake(pdev, pci_choose_state(pdev, state), wol);
@@ -4546,25 +4868,18 @@ static int sky2_resume(struct pci_dev *pdev)
        sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
        napi_enable(&hw->napi);
 
+       rtnl_lock();
        for (i = 0; i < hw->ports; i++) {
-               struct net_device *dev = hw->dev[i];
-
-               netif_device_attach(dev);
-               if (netif_running(dev)) {
-                       err = sky2_up(dev);
-                       if (err) {
-                               printk(KERN_ERR PFX "%s: could not up: %d\n",
-                                      dev->name, err);
-                               rtnl_lock();
-                               dev_close(dev);
-                               rtnl_unlock();
-                               goto out;
-                       }
-               }
+               err = sky2_reattach(hw->dev[i]);
+               if (err)
+                       goto out;
        }
+       rtnl_unlock();
 
        return 0;
 out:
+       rtnl_unlock();
+
        dev_err(&pdev->dev, "resume failed (%d)\n", err);
        pci_disable_device(pdev);
        return err;
@@ -4579,6 +4894,7 @@ static void sky2_shutdown(struct pci_dev *pdev)
        if (!hw)
                return;
 
+       rtnl_lock();
        del_timer_sync(&hw->watchdog_timer);
 
        for (i = 0; i < hw->ports; i++) {
@@ -4593,6 +4909,7 @@ static void sky2_shutdown(struct pci_dev *pdev)
 
        if (wol)
                sky2_power_aux(hw);
+       rtnl_unlock();
 
        pci_enable_wake(pdev, PCI_D3hot, wol);
        pci_enable_wake(pdev, PCI_D3cold, wol);