#include <linux/tcp.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/workqueue.h>
 #include <linux/if_vlan.h>
 #include <linux/mii.h>
 
 
        sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
 
-       spin_lock_bh(&hw->phy_lock);
+       down(&sky2->phy_sema);
        sky2_phy_init(hw, port);
-       spin_unlock_bh(&hw->phy_lock);
+       up(&sky2->phy_sema);
 
        /* MIB clear */
        reg = gma_read16(hw, port, GM_PHY_ADDR);
                /* fallthru */
        case SIOCGMIIREG: {
                u16 val = 0;
-               spin_lock_bh(&hw->phy_lock);
+
+               down(&sky2->phy_sema);
                err = __gm_phy_read(hw, sky2->port, data->reg_num & 0x1f, &val);
-               spin_unlock_bh(&hw->phy_lock);
+               up(&sky2->phy_sema);
+
                data->val_out = val;
                break;
        }
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
 
-               spin_lock_bh(&hw->phy_lock);
+               down(&sky2->phy_sema);
                err = gm_phy_write(hw, sky2->port, data->reg_num & 0x1f,
                                   data->val_in);
-               spin_unlock_bh(&hw->phy_lock);
+               up(&sky2->phy_sema);
                break;
        }
        return err;
        sky2_write32(hw, B0_IMSK, hw->intr_mask);
        local_irq_enable();
 
+       flush_scheduled_work();
 
        sky2_phy_reset(hw, port);
 
 }
 
 /*
- * Interrupt from PHY are handled in tasklet (soft irq)
+ * Interrupt from PHY are handled outside of interrupt context
  * because accessing phy registers requires spin wait which might
  * cause excess interrupt latency.
  */
-static void sky2_phy_task(unsigned long data)
+static void sky2_phy_task(void *arg)
 {
-       struct sky2_port *sky2 = (struct sky2_port *)data;
+       struct sky2_port *sky2 = arg;
        struct sky2_hw *hw = sky2->hw;
        u16 istatus, phystat;
 
-       spin_lock(&hw->phy_lock);
+       down(&sky2->phy_sema);
        istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT);
        phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT);
 
                        sky2_link_down(sky2);
        }
 out:
-       spin_unlock(&hw->phy_lock);
+       up(&sky2->phy_sema);
 
        local_irq_disable();
        hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2;
 
        hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
        sky2_write32(hw, B0_IMSK, hw->intr_mask);
-       tasklet_schedule(&sky2->phy_task);
+       schedule_work(&sky2->phy_task);
 }
 
 static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
 
        sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK);
 
-       spin_lock_bh(&hw->phy_lock);
        for (i = 0; i < hw->ports; i++)
                sky2_phy_reset(hw, i);
-       spin_unlock_bh(&hw->phy_lock);
 
        memset(hw->st_le, 0, STATUS_LE_BYTES);
        hw->st_idx = 0;
 
        netif_stop_queue(dev);
 
-       spin_lock_irq(&hw->phy_lock);
+       down(&sky2->phy_sema);
        sky2_phy_reset(hw, sky2->port);
        sky2_phy_init(hw, sky2->port);
-       spin_unlock_irq(&hw->phy_lock);
+       up(&sky2->phy_sema);
 
        return 0;
 }
 /* Can have one global because blinking is controlled by
  * ethtool and that is always under RTNL mutex
  */
-static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on)
+static void sky2_led(struct sky2_hw *hw, unsigned port, int on)
 {
        u16 pg;
 
-       spin_lock_bh(&hw->phy_lock);
        switch (hw->chip_id) {
        case CHIP_ID_YUKON_XL:
                pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
                             PHY_M_LED_MO_RX(MO_LED_OFF));
 
        }
-       spin_unlock_bh(&hw->phy_lock);
 }
 
 /* blink LED's for finding board */
        unsigned port = sky2->port;
        u16 ledctrl, ledover = 0;
        long ms;
+       int interrupted;
        int onoff = 1;
 
        if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ))
                ms = data * 1000;
 
        /* save initial values */
-       spin_lock_bh(&hw->phy_lock);
+       down(&sky2->phy_sema);
        if (hw->chip_id == CHIP_ID_YUKON_XL) {
                u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
                gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
                ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL);
                ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER);
        }
-       spin_unlock_bh(&hw->phy_lock);
 
-       while (ms > 0) {
+       interrupted = 0;
+       while (!interrupted && ms > 0) {
                sky2_led(hw, port, onoff);
                onoff = !onoff;
 
-               if (msleep_interruptible(250))
-                       break;  /* interrupted */
+               up(&sky2->phy_sema);
+               interrupted = msleep_interruptible(250);
+               down(&sky2->phy_sema);
+
                ms -= 250;
        }
 
        /* resume regularly scheduled programming */
-       spin_lock_bh(&hw->phy_lock);
        if (hw->chip_id == CHIP_ID_YUKON_XL) {
                u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
                gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
                gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
                gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
        }
-       spin_unlock_bh(&hw->phy_lock);
+       up(&sky2->phy_sema);
 
        return 0;
 }
        sky2->speed = -1;
        sky2->advertising = sky2_supported_modes(hw);
        sky2->rx_csum = 1;
-       tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long)sky2);
+       INIT_WORK(&sky2->phy_task, sky2_phy_task, sky2);
+       init_MUTEX(&sky2->phy_sema);
        sky2->tx_pending = TX_DEF_PENDING;
        sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING;
 
 
        memset(hw, 0, sizeof(*hw));
        hw->pdev = pdev;
-       spin_lock_init(&hw->phy_lock);
 
        hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
        if (!hw->regs) {