Merge ../linux-2.6-watchdog-mm
[linux-drm-fsl-dcu.git] / arch / x86_64 / kernel / pci-calgary.c
index f760045d6d35f0bf082f43d1322f32fe34c04680..37a770859e7180ae38e06c22d6064fbe1a4ebeab 100644 (file)
@@ -2,8 +2,9 @@
  * Derived from arch/powerpc/kernel/iommu.c
  *
  * Copyright (C) IBM Corporation, 2006
+ * Copyright (C) 2006  Jon Mason <jdmason@kudzu.us>
  *
- * Author: Jon Mason <jdmason@us.ibm.com>
+ * Author: Jon Mason <jdmason@kudzu.us>
  * Author: Muli Ben-Yehuda <muli@il.ibm.com>
 
  * This program is free software; you can redistribute it and/or modify
@@ -51,7 +52,8 @@
 #define ONE_BASED_CHASSIS_NUM   1
 
 /* register offsets inside the host bridge space */
-#define PHB_CSR_OFFSET         0x0110
+#define CALGARY_CONFIG_REG     0x0108
+#define PHB_CSR_OFFSET         0x0110 /* Channel Status */
 #define PHB_PLSSR_OFFSET       0x0120
 #define PHB_CONFIG_RW_OFFSET   0x0160
 #define PHB_IOBASE_BAR_LOW     0x0170
@@ -82,6 +84,8 @@
 #define TAR_VALID              0x0000000000000008UL
 /* CSR (Channel/DMA Status Register) */
 #define CSR_AGENT_MASK         0xffe0ffff
+/* CCR (Calgary Configuration Register) */
+#define CCR_2SEC_TIMEOUT        0x000000000000000EUL
 
 #define MAX_NUM_OF_PHBS                8 /* how many PHBs in total? */
 #define MAX_NUM_CHASSIS                8 /* max number of chassis */
@@ -714,7 +718,7 @@ static void calgary_watchdog(unsigned long data)
 
        /* If no error, the agent ID in the CSR is not valid */
        if (val32 & CSR_AGENT_MASK) {
-               printk(KERN_EMERG "calgary_watchdog: DMA error on bus %d, "
+               printk(KERN_EMERG "calgary_watchdog: DMA error on PHB %#x, "
                                  "CSR = %#x\n", dev->bus->number, val32);
                writel(0, target);
 
@@ -731,6 +735,38 @@ static void calgary_watchdog(unsigned long data)
        }
 }
 
+static void __init calgary_increase_split_completion_timeout(void __iomem *bbar,
+       unsigned char busnum)
+{
+       u64 val64;
+       void __iomem *target;
+       unsigned long phb_shift = -1;
+       u64 mask;
+
+       switch (busno_to_phbid(busnum)) {
+       case 0: phb_shift = (63 - 19);
+               break;
+       case 1: phb_shift = (63 - 23);
+               break;
+       case 2: phb_shift = (63 - 27);
+               break;
+       case 3: phb_shift = (63 - 35);
+               break;
+       default:
+               BUG_ON(busno_to_phbid(busnum));
+       }
+
+       target = calgary_reg(bbar, CALGARY_CONFIG_REG);
+       val64 = be64_to_cpu(readq(target));
+
+       /* zero out this PHB's timer bits */
+       mask = ~(0xFUL << phb_shift);
+       val64 &= mask;
+       val64 |= (CCR_2SEC_TIMEOUT << phb_shift);
+       writeq(cpu_to_be64(val64), target);
+       readq(target); /* flush */
+}
+
 static void __init calgary_enable_translation(struct pci_dev *dev)
 {
        u32 val32;
@@ -748,13 +784,20 @@ static void __init calgary_enable_translation(struct pci_dev *dev)
        val32 = be32_to_cpu(readl(target));
        val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE;
 
-       printk(KERN_INFO "Calgary: enabling translation on PHB %d\n", busnum);
+       printk(KERN_INFO "Calgary: enabling translation on PHB %#x\n", busnum);
        printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this "
               "bus.\n");
 
        writel(cpu_to_be32(val32), target);
        readl(target); /* flush */
 
+       /*
+        * Give split completion a longer timeout on bus 1 for aic94xx
+        * http://bugzilla.kernel.org/show_bug.cgi?id=7180
+        */
+       if (busnum == 1)
+               calgary_increase_split_completion_timeout(bbar, busnum);
+
        init_timer(&tbl->watchdog_timer);
        tbl->watchdog_timer.function = &calgary_watchdog;
        tbl->watchdog_timer.data = (unsigned long)dev;
@@ -778,7 +821,7 @@ static void __init calgary_disable_translation(struct pci_dev *dev)
        val32 = be32_to_cpu(readl(target));
        val32 &= ~(PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE);
 
-       printk(KERN_INFO "Calgary: disabling translation on PHB %d!\n", busnum);
+       printk(KERN_INFO "Calgary: disabling translation on PHB %#x!\n", busnum);
        writel(cpu_to_be32(val32), target);
        readl(target); /* flush */
 
@@ -790,7 +833,16 @@ static inline unsigned int __init locate_register_space(struct pci_dev *dev)
        int rionodeid;
        u32 address;
 
-       rionodeid = (dev->bus->number % 15 > 4) ? 3 : 2;
+       /*
+        * Each Calgary has four busses. The first four busses (first Calgary)
+        * have RIO node ID 2, then the next four (second Calgary) have RIO
+        * node ID 3, the next four (third Calgary) have node ID 2 again, etc.
+        * We use a gross hack - relying on the dev->bus->number ordering,
+        * modulo 14 - to decide which Calgary a given bus is on. Busses 0, 1,
+        * 2 and 4 are on the first Calgary (id 2), 6, 8, a and c are on the
+        * second (id 3), and then it repeats modulo 14.
+        */
+       rionodeid = (dev->bus->number % 14 > 4) ? 3 : 2;
        /*
         * register space address calculation as follows:
         * FE0MB-8MB*OneBasedChassisNumber+1MB*(RioNodeId-ChassisBase)
@@ -798,7 +850,7 @@ static inline unsigned int __init locate_register_space(struct pci_dev *dev)
         * RioNodeId is 2 for first Calgary, 3 for second Calgary
         */
        address = START_ADDRESS -
-               (0x800000 * (ONE_BASED_CHASSIS_NUM + dev->bus->number / 15)) +
+               (0x800000 * (ONE_BASED_CHASSIS_NUM + dev->bus->number / 14)) +
                (0x100000) * (rionodeid - CHASSIS_BASE);
        return address;
 }
@@ -816,6 +868,8 @@ static int __init calgary_init_one(struct pci_dev *dev)
        void __iomem *bbar;
        int ret;
 
+       BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM);
+
        address = locate_register_space(dev);
        /* map entire 1MB of Calgary config space */
        bbar = ioremap_nocache(address, 1024 * 1024);
@@ -842,10 +896,10 @@ done:
 
 static int __init calgary_init(void)
 {
-       int i, ret = -ENODEV;
+       int ret = -ENODEV;
        struct pci_dev *dev = NULL;
 
-       for (i = 0; i < MAX_PHB_BUS_NUM; i++) {
+       do {
                dev = pci_get_device(PCI_VENDOR_ID_IBM,
                                     PCI_DEVICE_ID_IBM_CALGARY,
                                     dev);
@@ -861,12 +915,12 @@ static int __init calgary_init(void)
                ret = calgary_init_one(dev);
                if (ret)
                        goto error;
-       }
+       } while (1);
 
        return ret;
 
 error:
-       for (i--; i >= 0; i--) {
+       do {
                dev = pci_find_device_reverse(PCI_VENDOR_ID_IBM,
                                              PCI_DEVICE_ID_IBM_CALGARY,
                                              dev);
@@ -882,7 +936,7 @@ error:
                calgary_disable_translation(dev);
                calgary_free_bus(dev);
                pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */
-       }
+       } while (1);
 
        return ret;
 }
@@ -1052,7 +1106,7 @@ static int __init calgary_parse_options(char *p)
 
                        if (bridge < MAX_PHB_BUS_NUM) {
                                printk(KERN_INFO "Calgary: disabling "
-                                      "translation for PHB 0x%x\n", bridge);
+                                      "translation for PHB %#x\n", bridge);
                                bus_info[bridge].translation_disabled = 1;
                        }
                }