Need to avoid race in updating IRQ mask. This can probably be replaced
smarter use of the interrupt control registers (if/when chipset
docs are available).
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
goto err_out;
/* Enable interrupts from phy/mac for port */
goto err_out;
/* Enable interrupts from phy/mac for port */
+ spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
netif_stop_queue(dev);
/* Disable port IRQ */
netif_stop_queue(dev);
/* Disable port IRQ */
+ spin_lock_irq(&hw->hw_lock);
hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
sky2_write32(hw, B0_IMSK, hw->intr_mask);
hw->intr_mask &= ~((sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
out:
up(&sky2->phy_sema);
out:
up(&sky2->phy_sema);
+ spin_lock_irq(&hw->hw_lock);
hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
}
if (likely(work_done < to_do)) {
}
if (likely(work_done < to_do)) {
- netif_rx_complete(dev0);
+ spin_lock_irq(&hw->hw_lock);
+ __netif_rx_complete(dev0);
+
hw->intr_mask |= Y2_IS_STAT_BMU;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
hw->intr_mask |= Y2_IS_STAT_BMU;
sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ spin_unlock_irq(&hw->hw_lock);
+
return 0;
} else {
*budget -= work_done;
return 0;
} else {
*budget -= work_done;
hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
sky2_write32(hw, B0_IMSK, hw->intr_mask);
hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
sky2_write32(hw, B0_IMSK, hw->intr_mask);
schedule_work(&sky2->phy_task);
}
schedule_work(&sky2->phy_task);
}
if (status == 0 || status == ~0)
return IRQ_NONE;
if (status == 0 || status == ~0)
return IRQ_NONE;
+ spin_lock(&hw->hw_lock);
if (status & Y2_IS_HW_ERR)
sky2_hw_intr(hw);
if (status & Y2_IS_HW_ERR)
sky2_hw_intr(hw);
sky2_write32(hw, B0_Y2_SP_ICR, 2);
sky2_write32(hw, B0_Y2_SP_ICR, 2);
- sky2_read32(hw, B0_IMSK);
+ spin_unlock(&hw->hw_lock);
goto err_out_free_hw;
}
hw->pm_cap = pm_cap;
goto err_out_free_hw;
}
hw->pm_cap = pm_cap;
+ spin_lock_init(&hw->hw_lock);
#ifdef __BIG_ENDIAN
/* byte swap descriptors in hardware */
#ifdef __BIG_ENDIAN
/* byte swap descriptors in hardware */
struct sky2_hw {
void __iomem *regs;
struct pci_dev *pdev;
struct sky2_hw {
void __iomem *regs;
struct pci_dev *pdev;
struct net_device *dev[2];
struct net_device *dev[2];
+ spinlock_t hw_lock;
+ u32 intr_mask;