[PATCH] powerpc: pci_64 fixes & cleanups
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Tue, 15 Nov 2005 05:05:33 +0000 (16:05 +1100)
committerPaul Mackerras <paulus@samba.org>
Wed, 16 Nov 2005 02:29:46 +0000 (13:29 +1100)
I discovered that in some cases (PowerMac for example) we wouldn't
properly map the PCI IO space on recent kernels. In addition, the code
for initializing PCI host bridges was scattered all over the place with
some duplication between platforms.

This patch fixes the problem and does a small cleanup by creating a
pcibios_alloc_controller() in pci_64.c that is similar to the one in
pci_32.c (just takes an additional device node argument) that takes care
of all the grunt allocation and initialisation work. It should work for
both boot time and dynamically allocated PHBs.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/kernel/pci_64.c
arch/powerpc/kernel/rtas_pci.c
arch/powerpc/platforms/iseries/pci.c
arch/powerpc/platforms/maple/pci.c
arch/powerpc/platforms/powermac/pci.c
include/asm-powerpc/ppc-pci.h
include/asm-ppc64/pci-bridge.h

index d7de3ec0bf83749eb24064c71fc52f8a0ea7537d..5a5b246850817984436aa8ee4e957fecaf017f3f 100644 (file)
@@ -187,7 +187,7 @@ static DEFINE_SPINLOCK(hose_spinlock);
 /*
  * pci_controller(phb) initialized common variables.
  */
-void __devinit pci_setup_pci_controller(struct pci_controller *hose)
+static void __devinit pci_setup_pci_controller(struct pci_controller *hose)
 {
        memset(hose, 0, sizeof(struct pci_controller));
 
@@ -197,6 +197,65 @@ void __devinit pci_setup_pci_controller(struct pci_controller *hose)
        spin_unlock(&hose_spinlock);
 }
 
+static void add_linux_pci_domain(struct device_node *dev,
+                                struct pci_controller *phb)
+{
+       struct property *of_prop;
+       unsigned int size;
+
+       of_prop = (struct property *)
+               get_property(dev, "linux,pci-domain", &size);
+       if (of_prop != NULL)
+               return;
+       WARN_ON(of_prop && size < sizeof(int));
+       if (of_prop && size < sizeof(int))
+               of_prop = NULL;
+       size = sizeof(struct property) + sizeof(int);
+       if (of_prop == NULL) {
+               if (mem_init_done)
+                       of_prop = kmalloc(size, GFP_KERNEL);
+               else
+                       of_prop = alloc_bootmem(size);
+       }
+       memset(of_prop, 0, sizeof(struct property));
+       of_prop->name = "linux,pci-domain";
+       of_prop->length = sizeof(int);
+       of_prop->value = (unsigned char *)&of_prop[1];
+       *((int *)of_prop->value) = phb->global_number;
+       prom_add_property(dev, of_prop);
+}
+
+struct pci_controller * pcibios_alloc_controller(struct device_node *dev)
+{
+       struct pci_controller *phb;
+
+       if (mem_init_done)
+               phb = kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
+       else
+               phb = alloc_bootmem(sizeof (struct pci_controller));
+       if (phb == NULL)
+               return NULL;
+       pci_setup_pci_controller(phb);
+       phb->arch_data = dev;
+       phb->is_dynamic = mem_init_done;
+       if (dev)
+               add_linux_pci_domain(dev, phb);
+       return phb;
+}
+
+void pcibios_free_controller(struct pci_controller *phb)
+{
+       if (phb->arch_data) {
+               struct device_node *np = phb->arch_data;
+               int *domain = (int *)get_property(np,
+                                                 "linux,pci-domain", NULL);
+               if (domain)
+                       *domain = -1;
+       }
+       if (phb->is_dynamic)
+               kfree(phb);
+}
+
 static void __init pcibios_claim_one_bus(struct pci_bus *b)
 {
        struct pci_dev *dev;
@@ -907,9 +966,10 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
         *                      (size depending on dev->n_addr_cells)
         *   cells 4+5 or 5+6:  the size of the range
         */
-       rlen = 0;
-       hose->io_base_phys = 0;
        ranges = (unsigned int *) get_property(dev, "ranges", &rlen);
+       if (ranges == NULL)
+               return;
+       hose->io_base_phys = 0;
        while ((rlen -= np * sizeof(unsigned int)) >= 0) {
                res = NULL;
                pci_space = ranges[0];
@@ -1107,6 +1167,8 @@ int remap_bus_range(struct pci_bus *bus)
        
        if (get_bus_io_range(bus, &start_phys, &start_virt, &size))
                return 1;
+       if (start_phys == 0)
+               return 1;
        printk("mapping IO %lx -> %lx, size: %lx\n", start_phys, start_virt, size);
        if (__ioremap_explicit(start_phys, start_virt, size,
                               _PAGE_NO_CACHE | _PAGE_GUARDED))
index 0e5a8e116653e8a111bfde579a2f30b33b86936b..60dec2401c26dc7225c0a3b7bfefefdca3f42e46 100644 (file)
@@ -304,75 +304,18 @@ static int __devinit setup_phb(struct device_node *dev,
                               struct pci_controller *phb,
                               unsigned int addr_size_words)
 {
-       pci_setup_pci_controller(phb);
-
        if (is_python(dev))
                python_countermeasures(dev, addr_size_words);
 
        if (phb_set_bus_ranges(dev, phb))
                return 1;
 
-       phb->arch_data = dev;
        phb->ops = &rtas_pci_ops;
        phb->buid = get_phb_buid(dev);
 
        return 0;
 }
 
-static void __devinit add_linux_pci_domain(struct device_node *dev,
-                                          struct pci_controller *phb,
-                                          struct property *of_prop)
-{
-       memset(of_prop, 0, sizeof(struct property));
-       of_prop->name = "linux,pci-domain";
-       of_prop->length = sizeof(phb->global_number);
-       of_prop->value = (unsigned char *)&of_prop[1];
-       memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number));
-       prom_add_property(dev, of_prop);
-}
-
-static struct pci_controller * __init alloc_phb(struct device_node *dev,
-                                               unsigned int addr_size_words)
-{
-       struct pci_controller *phb;
-       struct property *of_prop;
-
-       phb = alloc_bootmem(sizeof(struct pci_controller));
-       if (phb == NULL)
-               return NULL;
-
-       of_prop = alloc_bootmem(sizeof(struct property) +
-                               sizeof(phb->global_number));
-       if (!of_prop)
-               return NULL;
-
-       if (setup_phb(dev, phb, addr_size_words))
-               return NULL;
-
-       add_linux_pci_domain(dev, phb, of_prop);
-
-       return phb;
-}
-
-static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words)
-{
-       struct pci_controller *phb;
-
-       phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),
-                                              GFP_KERNEL);
-       if (phb == NULL)
-               return NULL;
-
-       if (setup_phb(dev, phb, addr_size_words))
-               return NULL;
-
-       phb->is_dynamic = 1;
-
-       /* TODO: linux,pci-domain? */
-
-       return phb;
-}
-
 unsigned long __init find_and_init_phbs(void)
 {
        struct device_node *node;
@@ -397,10 +340,10 @@ unsigned long __init find_and_init_phbs(void)
                if (node->type == NULL || strcmp(node->type, "pci") != 0)
                        continue;
 
-               phb = alloc_phb(node, root_size_cells);
+               phb = pcibios_alloc_controller(node);
                if (!phb)
                        continue;
-
+               setup_phb(node, phb, root_size_cells);
                pci_process_bridge_OF_ranges(phb, node, 0);
                pci_setup_phb_io(phb, index == 0);
 #ifdef CONFIG_PPC_PSERIES
@@ -446,10 +389,10 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
        root_size_cells = prom_n_size_cells(root);
 
        primary = list_empty(&hose_list);
-       phb = alloc_phb_dynamic(dn, root_size_cells);
+       phb = pcibios_alloc_controller(dn);
        if (!phb)
                return NULL;
-
+       setup_phb(dn, phb, root_size_cells);
        pci_process_bridge_OF_ranges(phb, dn, primary);
 
        pci_setup_phb_io_dynamic(phb, primary);
@@ -505,8 +448,7 @@ int pcibios_remove_root_bus(struct pci_controller *phb)
        }
 
        list_del(&phb->list_node);
-       if (phb->is_dynamic)
-               kfree(phb);
+       pcibios_free_controller(phb);
 
        return 0;
 }
index 4b75131773a698eef0ec7ff1ab38e5b3b9331b97..dafc518fbb8334ec5db75eb388e34e1c09ca6fcf 100644 (file)
@@ -244,10 +244,9 @@ unsigned long __init find_and_init_phbs(void)
                if (ret == 0) {
                        printk("bus %d appears to exist\n", bus);
 
-                       phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), GFP_KERNEL);
+                       phb = pcibios_alloc_controller(NULL);
                        if (phb == NULL)
                                return -ENOMEM;
-                       pci_setup_pci_controller(phb);
 
                        phb->pci_mem_offset = phb->local_number = bus;
                        phb->first_busno = bus;
index 895aeb3f75d0d73b6ddf94b370eaec93be0be604..f40451da037c00bd5bc6bc066c2dee90fd1212b0 100644 (file)
@@ -326,26 +326,12 @@ static int __init add_bridge(struct device_node *dev)
                dev->full_name);
        }
 
-       hose = alloc_bootmem(sizeof(struct pci_controller));
+       hose = pcibios_alloc_controller(dev);
        if (hose == NULL)
                return -ENOMEM;
-       pci_setup_pci_controller(hose);
-
-       hose->arch_data = dev;
        hose->first_busno = bus_range ? bus_range[0] : 0;
        hose->last_busno = bus_range ? bus_range[1] : 0xff;
 
-       of_prop = alloc_bootmem(sizeof(struct property) +
-                               sizeof(hose->global_number));
-       if (of_prop) {
-               memset(of_prop, 0, sizeof(struct property));
-               of_prop->name = "linux,pci-domain";
-               of_prop->length = sizeof(hose->global_number);
-               of_prop->value = (unsigned char *)&of_prop[1];
-               memcpy(of_prop->value, &hose->global_number, sizeof(hose->global_number));
-               prom_add_property(dev, of_prop);
-       }
-
        disp_name = NULL;
        if (device_is_compatible(dev, "u3-agp")) {
                setup_u3_agp(hose);
index dfd41b9781a9a9df37dbeac71d3c8d18e5510404..443be526cde7ec90a3b0b2e2f18f965613cd3581 100644 (file)
@@ -640,15 +640,16 @@ static void __init setup_u3_ht(struct pci_controller* hose)
         * the reg address cell, we shall fix that by killing struct
         * reg_property and using some accessor functions instead
         */
-       hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000);
+       hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000,
+                                                          0x02000000);
 
        /*
-        * /ht node doesn't expose a "ranges" property, so we "remove" regions that
-        * have been allocated to AGP. So far, this version of the code doesn't assign
-        * any of the 0xfxxxxxxx "fine" memory regions to /ht.
-        * We need to fix that sooner or later by either parsing all child "ranges"
-        * properties or figuring out the U3 address space decoding logic and
-        * then read its configuration register (if any).
+        * /ht node doesn't expose a "ranges" property, so we "remove"
+        * regions that have been allocated to AGP. So far, this version of
+        * the code doesn't assign any of the 0xfxxxxxxx "fine" memory regions
+        * to /ht. We need to fix that sooner or later by either parsing all
+        * child "ranges" properties or figuring out the U3 address space
+        * decoding logic and then read its configuration register (if any).
         */
        hose->io_base_phys = 0xf4000000;
        hose->pci_io_size = 0x00400000;
@@ -671,10 +672,10 @@ static void __init setup_u3_ht(struct pci_controller* hose)
                return;
        }
 
-       /* We "remove" the AGP resources from the resources allocated to HT, that
-        * is we create "holes". However, that code does assumptions that so far
-        * happen to be true (cross fingers...), typically that resources in the
-        * AGP node are properly ordered
+       /* We "remove" the AGP resources from the resources allocated to HT,
+        * that is we create "holes". However, that code does assumptions
+        * that so far happen to be true (cross fingers...), typically that
+        * resources in the AGP node are properly ordered
         */
        cur = 0;
        for (i=0; i<3; i++) {
@@ -684,23 +685,30 @@ static void __init setup_u3_ht(struct pci_controller* hose)
                /* We don't care about "fine" resources */
                if (res->start >= 0xf0000000)
                        continue;
-               /* Check if it's just a matter of "shrinking" us in one direction */
+               /* Check if it's just a matter of "shrinking" us in one
+                * direction
+                */
                if (hose->mem_resources[cur].start == res->start) {
                        DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n",
-                           cur, hose->mem_resources[cur].start, res->end + 1);
+                           cur, hose->mem_resources[cur].start,
+                           res->end + 1);
                        hose->mem_resources[cur].start = res->end + 1;
                        continue;
                }
                if (hose->mem_resources[cur].end == res->end) {
                        DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n",
-                           cur, hose->mem_resources[cur].end, res->start - 1);
+                           cur, hose->mem_resources[cur].end,
+                           res->start - 1);
                        hose->mem_resources[cur].end = res->start - 1;
                        continue;
                }
                /* No, it's not the case, we need a hole */
                if (cur == 2) {
-                       /* not enough resources for a hole, we drop part of the range */
-                       printk(KERN_WARNING "Running out of resources for /ht host !\n");
+                       /* not enough resources for a hole, we drop part
+                        * of the range
+                        */
+                       printk(KERN_WARNING "Running out of resources"
+                              " for /ht host !\n");
                        hose->mem_resources[cur].end = res->start - 1;
                        continue;
                }
@@ -714,17 +722,6 @@ static void __init setup_u3_ht(struct pci_controller* hose)
                hose->mem_resources[cur-1].end = res->start - 1;
        }
 }
-
-/* XXX this needs to be converged between ppc32 and ppc64... */
-static struct pci_controller * __init pcibios_alloc_controller(void)
-{
-       struct pci_controller *hose;
-
-       hose = alloc_bootmem(sizeof(struct pci_controller));
-       if (hose)
-               pci_setup_pci_controller(hose);
-       return hose;
-}
 #endif
 
 /*
@@ -756,11 +753,16 @@ static int __init add_bridge(struct device_node *dev)
 #endif
        bus_range = (int *) get_property(dev, "bus-range", &len);
        if (bus_range == NULL || len < 2 * sizeof(int)) {
-               printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
-                              dev->full_name);
+               printk(KERN_WARNING "Can't get bus-range for %s, assume"
+                      " bus 0\n", dev->full_name);
        }
 
+       /* XXX Different prototypes, to be merged */
+#ifdef CONFIG_PPC64
+       hose = pcibios_alloc_controller(dev);
+#else
        hose = pcibios_alloc_controller();
+#endif
        if (!hose)
                return -ENOMEM;
        hose->arch_data = dev;
@@ -768,7 +770,7 @@ static int __init add_bridge(struct device_node *dev)
        hose->last_busno = bus_range ? bus_range[1] : 0xff;
 
        disp_name = NULL;
-#ifdef CONFIG_POWER4
+#ifdef CONFIG_PPC64
        if (device_is_compatible(dev, "u3-agp")) {
                setup_u3_agp(hose);
                disp_name = "U3-AGP";
index 9896fade98a74999cd9c298d029cfac5090f66fd..2e36e5a7f4f351b1dc8b4de02411ad6445857bb1 100644 (file)
@@ -14,7 +14,6 @@
 
 extern unsigned long isa_io_base;
 
-extern void pci_setup_pci_controller(struct pci_controller *hose);
 extern void pci_setup_phb_io(struct pci_controller *hose, int primary);
 extern void pci_setup_phb_io_dynamic(struct pci_controller *hose, int primary);
 
index efbdaece0cf022fdd10bb2228368448240836af6..cf04327a597af1107f98f836476aa5c9b725a86e 100644 (file)
@@ -61,12 +61,14 @@ struct pci_dn {
        int     busno;                  /* for pci devices */
        int     bussubno;               /* for pci devices */
        int     devfn;                  /* for pci devices */
+
+#ifdef CONFIG_PPC_PSERIES
        int     eeh_mode;               /* See eeh.h for possible EEH_MODEs */
        int     eeh_config_addr;
        int     eeh_check_count;        /* # times driver ignored error */
        int     eeh_freeze_count;       /* # times this device froze up. */
        int     eeh_is_bridge;          /* device is pci-to-pci bridge */
-
+#endif
        int     pci_ext_config_space;   /* for pci devices */
        struct  pci_controller *phb;    /* for pci devices */
        struct  iommu_table *iommu_table;       /* for phb's or bridges */
@@ -74,9 +76,9 @@ struct pci_dn {
        struct  device_node *node;      /* back-pointer to the device_node */
 #ifdef CONFIG_PPC_ISERIES
        struct  list_head Device_List;
-       int             Irq;            /* Assigned IRQ */
-       int             Flags;          /* Possible flags(disable/bist)*/
-       u8              LogicalSlot;    /* Hv Slot Index for Tces */
+       int     Irq;                    /* Assigned IRQ */
+       int     Flags;                  /* Possible flags(disable/bist)*/
+       u8      LogicalSlot;            /* Hv Slot Index for Tces */
 #endif
        u32     config_space[16];       /* saved PCI config space */
 };
@@ -136,6 +138,10 @@ static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus)
        return PCI_DN(busdn)->phb;
 }
 
+extern struct pci_controller *
+pcibios_alloc_controller(struct device_node *dev);
+extern void pcibios_free_controller(struct pci_controller *phb);
+
 /* Return values for ppc_md.pci_probe_mode function */
 #define PCI_PROBE_NONE         -1      /* Don't look at this bus at all */
 #define PCI_PROBE_NORMAL       0       /* Do normal PCI probing */