Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[pandora-kernel.git] / arch / x86_64 / kernel / pci-calgary.c
index b3296cc..3d65b1d 100644 (file)
 #include <asm/pci-direct.h>
 #include <asm/system.h>
 #include <asm/dma.h>
+#include <asm/rio.h>
+
+#ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT
+int use_calgary __read_mostly = 1;
+#else
+int use_calgary __read_mostly = 0;
+#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
 
 #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
 #define PCI_VENDOR_DEVICE_ID_CALGARY \
@@ -52,7 +59,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
@@ -83,6 +91,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 */
@@ -112,14 +122,35 @@ static const unsigned long phb_offsets[] = {
        0xB000 /* PHB3 */
 };
 
+/* PHB debug registers */
+
+static const unsigned long phb_debug_offsets[] = {
+       0x4000  /* PHB 0 DEBUG */,
+       0x5000  /* PHB 1 DEBUG */,
+       0x6000  /* PHB 2 DEBUG */,
+       0x7000  /* PHB 3 DEBUG */
+};
+
+/*
+ * STUFF register for each debug PHB,
+ * byte 1 = start bus number, byte 2 = end bus number
+ */
+
+#define PHB_DEBUG_STUFF_OFFSET 0x0020
+
 unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED;
 static int translate_empty_slots __read_mostly = 0;
 static int calgary_detected __read_mostly = 0;
 
+static struct rio_table_hdr    *rio_table_hdr __initdata;
+static struct scal_detail      *scal_devs[MAX_NUMNODES] __initdata;
+static struct rio_detail       *rio_devs[MAX_NUMNODES * 4] __initdata;
+
 struct calgary_bus_info {
        void *tce_space;
        unsigned char translation_disabled;
        signed char phbid;
+       void __iomem *bbar;
 };
 
 static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, };
@@ -472,6 +503,11 @@ static struct dma_mapping_ops calgary_dma_ops = {
        .unmap_sg = calgary_unmap_sg,
 };
 
+static inline void __iomem * busno_to_bbar(unsigned char num)
+{
+       return bus_info[num].bbar;
+}
+
 static inline int busno_to_phbid(unsigned char num)
 {
        return bus_info[num].phbid;
@@ -617,14 +653,9 @@ static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev)
 static void __init calgary_reserve_regions(struct pci_dev *dev)
 {
        unsigned int npages;
-       void __iomem *bbar;
-       unsigned char busnum;
        u64 start;
        struct iommu_table *tbl = dev->sysdata;
 
-       bbar = tbl->bbar;
-       busnum = dev->bus->number;
-
        /* reserve bad_dma_address in case it's a legal address */
        iommu_range_reserve(tbl, bad_dma_address, 1);
 
@@ -732,6 +763,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 int phb_shift = ~0; /* silence gcc */
+       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;
@@ -756,6 +819,13 @@ static void __init calgary_enable_translation(struct pci_dev *dev)
        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;
@@ -786,33 +856,6 @@ static void __init calgary_disable_translation(struct pci_dev *dev)
        del_timer_sync(&tbl->watchdog_timer);
 }
 
-static inline unsigned int __init locate_register_space(struct pci_dev *dev)
-{
-       int rionodeid;
-       u32 address;
-
-       /*
-        * 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)
-        * ChassisBase is always zero for x366/x260/x460
-        * RioNodeId is 2 for first Calgary, 3 for second Calgary
-        */
-       address = START_ADDRESS -
-               (0x800000 * (ONE_BASED_CHASSIS_NUM + dev->bus->number / 14)) +
-               (0x100000) * (rionodeid - CHASSIS_BASE);
-       return address;
-}
-
 static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
 {
        pci_dev_get(dev);
@@ -822,23 +865,15 @@ static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
 
 static int __init calgary_init_one(struct pci_dev *dev)
 {
-       u32 address;
        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);
-       if (!bbar) {
-               ret = -ENODATA;
-               goto done;
-       }
-
+       bbar = busno_to_bbar(dev->bus->number);
        ret = calgary_setup_tar(dev, bbar);
        if (ret)
-               goto iounmap;
+               goto done;
 
        pci_dev_get(dev);
        dev->bus->self = dev;
@@ -846,17 +881,66 @@ static int __init calgary_init_one(struct pci_dev *dev)
 
        return 0;
 
-iounmap:
-       iounmap(bbar);
 done:
        return ret;
 }
 
+static int __init calgary_locate_bbars(void)
+{
+       int ret;
+       int rioidx, phb, bus;
+       void __iomem *bbar;
+       void __iomem *target;
+       unsigned long offset;
+       u8 start_bus, end_bus;
+       u32 val;
+
+       ret = -ENODATA;
+       for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) {
+               struct rio_detail *rio = rio_devs[rioidx];
+
+               if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY))
+                       continue;
+
+               /* map entire 1MB of Calgary config space */
+               bbar = ioremap_nocache(rio->BBAR, 1024 * 1024);
+               if (!bbar)
+                       goto error;
+
+               for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {
+                       offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET;
+                       target = calgary_reg(bbar, offset);
+
+                       val = be32_to_cpu(readl(target));
+                       start_bus = (u8)((val & 0x00FF0000) >> 16);
+                       end_bus = (u8)((val & 0x0000FF00) >> 8);
+                       for (bus = start_bus; bus <= end_bus; bus++) {
+                               bus_info[bus].bbar = bbar;
+                               bus_info[bus].phbid = phb;
+                       }
+               }
+       }
+
+       return 0;
+
+error:
+       /* scan bus_info and iounmap any bbars we previously ioremap'd */
+       for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++)
+               if (bus_info[bus].bbar)
+                       iounmap(bus_info[bus].bbar);
+
+       return ret;
+}
+
 static int __init calgary_init(void)
 {
-       int ret = -ENODEV;
+       int ret;
        struct pci_dev *dev = NULL;
 
+       ret = calgary_locate_bbars();
+       if (ret)
+               return ret;
+
        do {
                dev = pci_get_device(PCI_VENDOR_ID_IBM,
                                     PCI_DEVICE_ID_IBM_CALGARY,
@@ -879,7 +963,7 @@ static int __init calgary_init(void)
 
 error:
        do {
-               dev = pci_find_device_reverse(PCI_VENDOR_ID_IBM,
+               dev = pci_get_device_reverse(PCI_VENDOR_ID_IBM,
                                              PCI_DEVICE_ID_IBM_CALGARY,
                                              dev);
                if (!dev)
@@ -920,13 +1004,56 @@ static inline int __init determine_tce_table_size(u64 ram)
        return ret;
 }
 
+static int __init build_detail_arrays(void)
+{
+       unsigned long ptr;
+       int i, scal_detail_size, rio_detail_size;
+
+       if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){
+               printk(KERN_WARNING
+                       "Calgary: MAX_NUMNODES too low! Defined as %d, "
+                       "but system has %d nodes.\n",
+                       MAX_NUMNODES, rio_table_hdr->num_scal_dev);
+               return -ENODEV;
+       }
+
+       switch (rio_table_hdr->version){
+       case 2:
+               scal_detail_size = 11;
+               rio_detail_size = 13;
+               break;
+       case 3:
+               scal_detail_size = 12;
+               rio_detail_size = 15;
+               break;
+       default:
+               printk(KERN_WARNING
+                      "Calgary: Invalid Rio Grande Table Version: %d\n",
+                      rio_table_hdr->version);
+               return -EPROTO;
+       }
+
+       ptr = ((unsigned long)rio_table_hdr) + 3;
+       for (i = 0; i < rio_table_hdr->num_scal_dev;
+                   i++, ptr += scal_detail_size)
+               scal_devs[i] = (struct scal_detail *)ptr;
+
+       for (i = 0; i < rio_table_hdr->num_rio_dev;
+                   i++, ptr += rio_detail_size)
+               rio_devs[i] = (struct rio_detail *)ptr;
+
+       return 0;
+}
+
 void __init detect_calgary(void)
 {
        u32 val;
        int bus;
        void *tbl;
        int calgary_found = 0;
-       int phb = -1;
+       unsigned long ptr;
+       unsigned int offset, prev_offset;
+       int ret;
 
        /*
         * if the user specified iommu=off or iommu=soft or we found
@@ -935,25 +1062,54 @@ void __init detect_calgary(void)
        if (swiotlb || no_iommu || iommu_detected)
                return;
 
+       if (!use_calgary)
+               return;
+
        if (!early_pci_allowed())
                return;
 
+       printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n");
+
+       ptr = (unsigned long)phys_to_virt(get_bios_ebda());
+
+       rio_table_hdr = NULL;
+       prev_offset = 0;
+       offset = 0x180;
+       /*
+        * The next offset is stored in the 1st word.
+        * Only parse up until the offset increases:
+        */
+       while (offset > prev_offset) {
+               /* The block id is stored in the 2nd word */
+               if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
+                       /* set the pointer past the offset & block id */
+                       rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4);
+                       break;
+               }
+               prev_offset = offset;
+               offset = *((unsigned short *)(ptr + offset));
+       }
+       if (!rio_table_hdr) {
+               printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table "
+                      "in EBDA - bailing!\n");
+               return;
+       }
+
+       ret = build_detail_arrays();
+       if (ret) {
+               printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n", ret);
+               return;
+       }
+
        specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);
 
        for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
                int dev;
                struct calgary_bus_info *info = &bus_info[bus];
-               info->phbid = -1;
 
                if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY)
                        continue;
 
-               /*
-                * There are 4 PHBs per Calgary chip.  Set phb to which phb (0-3)
-                * it is connected to releative to the clagary chip.
-                */
-               phb = (phb + 1) % PHBS_PER_CALGARY;
-
                if (info->translation_disabled)
                        continue;
 
@@ -968,13 +1124,15 @@ void __init detect_calgary(void)
                                if (!tbl)
                                        goto cleanup;
                                info->tce_space = tbl;
-                               info->phbid = phb;
                                calgary_found = 1;
                                break;
                        }
                }
        }
 
+       printk(KERN_DEBUG "Calgary: finished detection, Calgary %s\n",
+              calgary_found ? "found" : "not found");
+
        if (calgary_found) {
                iommu_detected = 1;
                calgary_detected = 1;