]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/tg3.c
[TG3]: Add indirect register method for 5703 behind ICH
[net-next-2.6.git] / drivers / net / tg3.c
index 8411e0f4cb69ee3797c267df4c192e55a2ff30ce..3a7cfb81bf8930804bbbe1c5ee204ba9cc94694f 100644 (file)
@@ -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);