|
@@ -340,10 +340,12 @@ static struct {
|
|
|
|
|
|
static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val)
|
|
|
{
|
|
|
- spin_lock_bh(&tp->indirect_lock);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tp->indirect_lock, flags);
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
|
|
|
- spin_unlock_bh(&tp->indirect_lock);
|
|
|
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
}
|
|
|
|
|
|
static void tg3_write_flush_reg32(struct tg3 *tp, u32 off, u32 val)
|
|
@@ -352,24 +354,75 @@ static void tg3_write_flush_reg32(struct tg3 *tp, u32 off, u32 val)
|
|
|
readl(tp->regs + off);
|
|
|
}
|
|
|
|
|
|
-static void _tw32_flush(struct tg3 *tp, u32 off, u32 val)
|
|
|
+static u32 tg3_read_indirect_reg32(struct tg3 *tp, u32 off)
|
|
|
{
|
|
|
- if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) {
|
|
|
- spin_lock_bh(&tp->indirect_lock);
|
|
|
- pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
|
|
|
- pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
|
|
|
- spin_unlock_bh(&tp->indirect_lock);
|
|
|
- } else {
|
|
|
- void __iomem *dest = tp->regs + off;
|
|
|
- writel(val, dest);
|
|
|
- readl(dest); /* always flush PCI write */
|
|
|
+ unsigned long flags;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tp->indirect_lock, flags);
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off);
|
|
|
+ pci_read_config_dword(tp->pdev, TG3PCI_REG_DATA, &val);
|
|
|
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
+static void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ if (off == (MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW)) {
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_RCV_RET_RING_CON_IDX +
|
|
|
+ TG3_64BIT_REG_LOW, val);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (off == (MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW)) {
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_STD_RING_PROD_IDX +
|
|
|
+ TG3_64BIT_REG_LOW, val);
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tp->indirect_lock, flags);
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off + 0x5600);
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_DATA, val);
|
|
|
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
+
|
|
|
+ /* In indirect mode when disabling interrupts, we also need
|
|
|
+ * to clear the interrupt bit in the GRC local ctrl register.
|
|
|
+ */
|
|
|
+ if ((off == (MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW)) &&
|
|
|
+ (val == 0x1)) {
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_LOCAL_CTRL,
|
|
|
+ tp->grc_local_ctrl|GRC_LCLCTRL_CLEARINT);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static u32 tg3_read_indirect_mbox(struct tg3 *tp, u32 off)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tp->indirect_lock, flags);
|
|
|
+ pci_write_config_dword(tp->pdev, TG3PCI_REG_BASE_ADDR, off + 0x5600);
|
|
|
+ pci_read_config_dword(tp->pdev, TG3PCI_REG_DATA, &val);
|
|
|
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
+static void _tw32_flush(struct tg3 *tp, u32 off, u32 val)
|
|
|
+{
|
|
|
+ tp->write32(tp, off, val);
|
|
|
+ if (!(tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) &&
|
|
|
+ !(tp->tg3_flags & TG3_FLAG_5701_REG_WRITE_BUG) &&
|
|
|
+ !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
|
|
|
+ tp->read32(tp, off); /* flush */
|
|
|
}
|
|
|
|
|
|
static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val)
|
|
|
{
|
|
|
tp->write32_mbox(tp, off, val);
|
|
|
- tp->read32_mbox(tp, off);
|
|
|
+ if (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) &&
|
|
|
+ !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
|
|
|
+ tp->read32_mbox(tp, off);
|
|
|
}
|
|
|
|
|
|
static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
|
|
@@ -404,24 +457,28 @@ static u32 tg3_read32(struct tg3 *tp, u32 off)
|
|
|
|
|
|
static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
|
|
|
{
|
|
|
- spin_lock_bh(&tp->indirect_lock);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tp->indirect_lock, flags);
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
|
|
|
|
|
|
/* Always leave this as zero. */
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
|
|
|
- spin_unlock_bh(&tp->indirect_lock);
|
|
|
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
}
|
|
|
|
|
|
static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
|
|
|
{
|
|
|
- spin_lock_bh(&tp->indirect_lock);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tp->indirect_lock, flags);
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
|
|
|
pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
|
|
|
|
|
|
/* Always leave this as zero. */
|
|
|
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
|
|
|
- spin_unlock_bh(&tp->indirect_lock);
|
|
|
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
|
|
|
}
|
|
|
|
|
|
static void tg3_disable_ints(struct tg3 *tp)
|
|
@@ -9149,14 +9206,6 @@ static int __devinit tg3_is_sun_570X(struct tg3 *tp)
|
|
|
static int __devinit tg3_get_invariants(struct tg3 *tp)
|
|
|
{
|
|
|
static struct pci_device_id write_reorder_chipsets[] = {
|
|
|
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
|
|
|
- PCI_DEVICE_ID_INTEL_82801AA_8) },
|
|
|
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
|
|
|
- PCI_DEVICE_ID_INTEL_82801AB_8) },
|
|
|
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
|
|
|
- PCI_DEVICE_ID_INTEL_82801BA_11) },
|
|
|
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
|
|
|
- PCI_DEVICE_ID_INTEL_82801BA_6) },
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_AMD,
|
|
|
PCI_DEVICE_ID_AMD_FE_GATE_700C) },
|
|
|
{ },
|
|
@@ -9173,7 +9222,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
|
|
|
tp->tg3_flags2 |= TG3_FLG2_SUN_570X;
|
|
|
#endif
|
|
|
|
|
|
- /* If we have an AMD 762 or Intel ICH/ICH0/ICH2 chipset, write
|
|
|
+ /* If we have an AMD 762 chipset, write
|
|
|
* reordering to the mailbox registers done by the host
|
|
|
* controller can cause major troubles. We read back from
|
|
|
* every mailbox register write to force the writes to be
|
|
@@ -9211,6 +9260,69 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
|
|
|
if (tp->pci_chip_rev_id == CHIPREV_ID_5752_A0_HW)
|
|
|
tp->pci_chip_rev_id = CHIPREV_ID_5752_A0;
|
|
|
|
|
|
+ /* If we have 5702/03 A1 or A2 on certain ICH chipsets,
|
|
|
+ * we need to disable memory and use config. cycles
|
|
|
+ * only to access all registers. The 5702/03 chips
|
|
|
+ * can mistakenly decode the special cycles from the
|
|
|
+ * ICH chipsets as memory write cycles, causing corruption
|
|
|
+ * of register and memory space. Only certain ICH bridges
|
|
|
+ * will drive special cycles with non-zero data during the
|
|
|
+ * address phase which can fall within the 5703's address
|
|
|
+ * range. This is not an ICH bug as the PCI spec allows
|
|
|
+ * non-zero address during special cycles. However, only
|
|
|
+ * these ICH bridges are known to drive non-zero addresses
|
|
|
+ * during special cycles.
|
|
|
+ *
|
|
|
+ * Since special cycles do not cross PCI bridges, we only
|
|
|
+ * enable this workaround if the 5703 is on the secondary
|
|
|
+ * bus of these ICH bridges.
|
|
|
+ */
|
|
|
+ if ((tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) ||
|
|
|
+ (tp->pci_chip_rev_id == CHIPREV_ID_5703_A2)) {
|
|
|
+ static struct tg3_dev_id {
|
|
|
+ u32 vendor;
|
|
|
+ u32 device;
|
|
|
+ u32 rev;
|
|
|
+ } ich_chipsets[] = {
|
|
|
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_8,
|
|
|
+ PCI_ANY_ID },
|
|
|
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_8,
|
|
|
+ PCI_ANY_ID },
|
|
|
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_11,
|
|
|
+ 0xa },
|
|
|
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_6,
|
|
|
+ PCI_ANY_ID },
|
|
|
+ { },
|
|
|
+ };
|
|
|
+ struct tg3_dev_id *pci_id = &ich_chipsets[0];
|
|
|
+ struct pci_dev *bridge = NULL;
|
|
|
+
|
|
|
+ while (pci_id->vendor != 0) {
|
|
|
+ bridge = pci_get_device(pci_id->vendor, pci_id->device,
|
|
|
+ bridge);
|
|
|
+ if (!bridge) {
|
|
|
+ pci_id++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (pci_id->rev != PCI_ANY_ID) {
|
|
|
+ u8 rev;
|
|
|
+
|
|
|
+ pci_read_config_byte(bridge, PCI_REVISION_ID,
|
|
|
+ &rev);
|
|
|
+ if (rev > pci_id->rev)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (bridge->subordinate &&
|
|
|
+ (bridge->subordinate->number ==
|
|
|
+ tp->pdev->bus->number)) {
|
|
|
+
|
|
|
+ tp->tg3_flags2 |= TG3_FLG2_ICH_WORKAROUND;
|
|
|
+ pci_dev_put(bridge);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* Find msi capability. */
|
|
|
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5780)
|
|
|
tp->msi_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_MSI);
|
|
@@ -9342,6 +9454,22 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
|
|
|
tp->write32_rx_mbox = tg3_write_flush_reg32;
|
|
|
}
|
|
|
|
|
|
+ if (tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND) {
|
|
|
+ tp->read32 = tg3_read_indirect_reg32;
|
|
|
+ tp->write32 = tg3_write_indirect_reg32;
|
|
|
+ tp->read32_mbox = tg3_read_indirect_mbox;
|
|
|
+ tp->write32_mbox = tg3_write_indirect_mbox;
|
|
|
+ tp->write32_tx_mbox = tg3_write_indirect_mbox;
|
|
|
+ tp->write32_rx_mbox = tg3_write_indirect_mbox;
|
|
|
+
|
|
|
+ iounmap(tp->regs);
|
|
|
+ tp->regs = 0;
|
|
|
+
|
|
|
+ pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd);
|
|
|
+ pci_cmd &= ~PCI_COMMAND_MEMORY;
|
|
|
+ pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
|
|
|
+ }
|
|
|
+
|
|
|
/* Get eeprom hw config before calling tg3_set_power_state().
|
|
|
* In particular, the TG3_FLAG_EEPROM_WRITE_PROT flag must be
|
|
|
* determined before calling tg3_set_power_state() so that
|
|
@@ -10486,7 +10614,10 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
|
|
|
return 0;
|
|
|
|
|
|
err_out_iounmap:
|
|
|
- iounmap(tp->regs);
|
|
|
+ if (tp->regs) {
|
|
|
+ iounmap(tp->regs);
|
|
|
+ tp->regs = 0;
|
|
|
+ }
|
|
|
|
|
|
err_out_free_dev:
|
|
|
free_netdev(dev);
|
|
@@ -10508,7 +10639,10 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
|
|
|
struct tg3 *tp = netdev_priv(dev);
|
|
|
|
|
|
unregister_netdev(dev);
|
|
|
- iounmap(tp->regs);
|
|
|
+ if (tp->regs) {
|
|
|
+ iounmap(tp->regs);
|
|
|
+ tp->regs = 0;
|
|
|
+ }
|
|
|
free_netdev(dev);
|
|
|
pci_release_regions(pdev);
|
|
|
pci_disable_device(pdev);
|