diff options
author | Paul Mackerras <paulus@samba.org> | 2006-01-15 22:05:47 +1100 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-01-15 22:05:47 +1100 |
commit | e05b3b4adbaeea508bd1c195f1f2e22c8251176b (patch) | |
tree | 61b88c6524b199c6051b46d040b0c379109d40ce | |
parent | 820a8ce7931d18338e5c089725ec083518da1644 (diff) |
powerpc/32: Restore previous version of 32-bit PCI code
When I removed the powermac support from arch/ppc/kernel/pci.c,
I overlooked the fact that that file is used in 32-bit ARCH=powerpc
builds. To prevent problems in future, restore the original version
of that file as arch/powerpc/kernel/pci_32.c, and use that.
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_32.c | 1897 | ||||
-rw-r--r-- | arch/ppc/kernel/Makefile | 1 |
3 files changed, 1899 insertions, 2 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index a94699d8dc5..c287980b7e6 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -60,7 +60,8 @@ obj-$(CONFIG_MODULES) += $(module-y) pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci_iommu.o \ pci_direct_iommu.o iomap.o -obj-$(CONFIG_PCI) += $(pci64-y) +pci32-$(CONFIG_PPC32) := pci_32.o +obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y) kexec-$(CONFIG_PPC64) := machine_kexec_64.o crash.o kexec-$(CONFIG_PPC32) := machine_kexec_32.o obj-$(CONFIG_KEXEC) += machine_kexec.o $(kexec-y) diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c new file mode 100644 index 00000000000..704c846b2b0 --- /dev/null +++ b/arch/powerpc/kernel/pci_32.c @@ -0,0 +1,1897 @@ +/* + * Common pmac/prep/chrp pci routines. -- Cort + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/capability.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/bootmem.h> + +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/sections.h> +#include <asm/pci-bridge.h> +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/machdep.h> + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; +int pcibios_assign_bus_offset = 1; + +void pcibios_make_OF_bus_map(void); + +static int pci_relocate_bridge_resource(struct pci_bus *bus, int i); +static int probe_resource(struct pci_bus *parent, struct resource *pr, + struct resource *res, struct resource **conflict); +static void update_bridge_base(struct pci_bus *bus, int i); +static void pcibios_fixup_resources(struct pci_dev* dev); +static void fixup_broken_pcnet32(struct pci_dev* dev); +static int reparent_resources(struct resource *parent, struct resource *res); +static void fixup_cpc710_pci64(struct pci_dev* dev); +#ifdef CONFIG_PPC_OF +static u8* pci_to_OF_bus_map; +#endif + +/* By default, we don't re-assign bus numbers. We do this only on + * some pmacs + */ +int pci_assign_all_buses; + +struct pci_controller* hose_head; +struct pci_controller** hose_tail = &hose_head; + +static int pci_bus_count; + +static void +fixup_broken_pcnet32(struct pci_dev* dev) +{ + if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { + dev->vendor = PCI_VENDOR_ID_AMD; + pci_write_config_word(dev, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32); + +static void +fixup_cpc710_pci64(struct pci_dev* dev) +{ + /* Hide the PCI64 BARs from the kernel as their content doesn't + * fit well in the resource management + */ + dev->resource[0].start = dev->resource[0].end = 0; + dev->resource[0].flags = 0; + dev->resource[1].start = dev->resource[1].end = 0; + dev->resource[1].flags = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64); + +static void +pcibios_fixup_resources(struct pci_dev *dev) +{ + struct pci_controller* hose = (struct pci_controller *)dev->sysdata; + int i; + unsigned long offset; + + if (!hose) { + printk(KERN_ERR "No hose for PCI dev %s!\n", pci_name(dev)); + return; + } + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + struct resource *res = dev->resource + i; + if (!res->flags) + continue; + if (res->end == 0xffffffff) { + DBG("PCI:%s Resource %d [%08lx-%08lx] is unassigned\n", + pci_name(dev), i, res->start, res->end); + res->end -= res->start; + res->start = 0; + res->flags |= IORESOURCE_UNSET; + continue; + } + offset = 0; + if (res->flags & IORESOURCE_MEM) { + offset = hose->pci_mem_offset; + } else if (res->flags & IORESOURCE_IO) { + offset = (unsigned long) hose->io_base_virt + - isa_io_base; + } + if (offset != 0) { + res->start += offset; + res->end += offset; +#ifdef DEBUG + printk("Fixup res %d (%lx) of dev %s: %lx -> %lx\n", + i, res->flags, pci_name(dev), + res->start - offset, res->start); +#endif + } + } + + /* Call machine specific resource fixup */ + if (ppc_md.pcibios_fixup_resources) + ppc_md.pcibios_fixup_resources(dev); +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources); + +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + unsigned long offset = 0; + struct pci_controller *hose = dev->sysdata; + + if (hose && res->flags & IORESOURCE_IO) + offset = (unsigned long)hose->io_base_virt - isa_io_base; + else if (hose && res->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; + region->start = res->start - offset; + region->end = res->end - offset; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + unsigned long offset = 0; + struct pci_controller *hose = dev->sysdata; + + if (hose && res->flags & IORESOURCE_IO) + offset = (unsigned long)hose->io_base_virt - isa_io_base; + else if (hose && res->flags & IORESOURCE_MEM) + offset = hose->pci_mem_offset; + res->start = region->start + offset; + res->end = region->end + offset; +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void pcibios_align_resource(void *data, struct resource *res, unsigned long size, + unsigned long align) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (size > 0x100) { + printk(KERN_ERR "PCI: I/O Region %s/%d too large" + " (%ld bytes)\n", pci_name(dev), + dev->resource - res, size); + } + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} +EXPORT_SYMBOL(pcibios_align_resource); + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init +pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + int i; + struct resource *res, *pr; + + /* Depth-First Search on bus tree */ + list_for_each_entry(bus, bus_list, node) { + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL || !res->flags + || res->start > res->end) + continue; + if (bus->parent == NULL) + pr = (res->flags & IORESOURCE_IO)? + &ioport_resource: &iomem_resource; + else { + pr = pci_find_parent_resource(bus->self, res); + if (pr == res) { + /* this happens when the generic PCI + * code (wrongly) decides that this + * bridge is transparent -- paulus + */ + continue; + } + } + + DBG("PCI: bridge rsrc %lx..%lx (%lx), parent %p\n", + res->start, res->end, res->flags, pr); + if (pr) { + if (request_resource(pr, res) == 0) + continue; + /* + * Must be a conflict with an existing entry. + * Move that entry (or entries) under the + * bridge resource and try again. + */ + if (reparent_resources(pr, res) == 0) + continue; + } + printk(KERN_ERR "PCI: Cannot allocate resource region " + "%d of PCI bridge %d\n", i, bus->number); + if (pci_relocate_bridge_resource(bus, i)) + bus->resource[i] = NULL; + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +/* + * Reparent resource children of pr that conflict with res + * under res, and make res replace those children. + */ +static int __init +reparent_resources(struct resource *parent, struct resource *res) +{ + struct resource *p, **pp; + struct resource **firstpp = NULL; + + for (pp = &parent->child; (p = *pp) != NULL; pp = &p->sibling) { + if (p->end < res->start) + continue; + if (res->end < p->start) + break; + if (p->start < res->start || p->end > res->end) + return -1; /* not completely contained */ + if (firstpp == NULL) + firstpp = pp; + } + if (firstpp == NULL) + return -1; /* didn't find any conflicting entries? */ + res->parent = parent; + res->child = *firstpp; + res->sibling = *pp; + *firstpp = res; + *pp = NULL; + for (p = res->child; p != NULL; p = p->sibling) { + p->parent = res; + DBG(KERN_INFO "PCI: reparented %s [%lx..%lx] under %s\n", + p->name, p->start, p->end, res->name); + } + return 0; +} + +/* + * A bridge has been allocated a range which is outside the range + * of its parent bridge, so it needs to be moved. + */ +static int __init +pci_relocate_bridge_resource(struct pci_bus *bus, int i) +{ + struct resource *res, *pr, *conflict; + unsigned long try, size; + int j; + struct pci_bus *parent = bus->parent; + + if (parent == NULL) { + /* shouldn't ever happen */ + printk(KERN_ERR "PCI: can't move host bridge resource\n"); + return -1; + } + res = bus->resource[i]; + if (res == NULL) + return -1; + pr = NULL; + for (j = 0; j < 4; j++) { + struct resource *r = parent->resource[j]; + if (!r) + continue; + if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) + continue; + if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) { + pr = r; + break; + } + if (res->flags & IORESOURCE_PREFETCH) + pr = r; + } + if (pr == NULL) + return -1; + size = res->end - res->start; + if (pr->start > pr->end || size > pr->end - pr->start) + return -1; + try = pr->end; + for (;;) { + res->start = try - size; + res->end = try; + if (probe_resource(bus->parent, pr, res, &conflict) == 0) + break; + if (conflict->start <= pr->start + size) + return -1; + try = conflict->start - 1; + } + if (request_resource(pr, res)) { + DBG(KERN_ERR "PCI: huh? couldn't move to %lx..%lx\n", + res->start, res->end); + return -1; /* "can't happen" */ + } + update_bridge_base(bus, i); + printk(KERN_INFO "PCI: bridge %d resource %d moved to %lx..%lx\n", + bus->number, i, res->start, res->end); + return 0; +} + +static int __init +probe_resource(struct pci_bus *parent, struct resource *pr, + struct resource *res, struct resource **conflict) +{ + struct pci_bus *bus; + struct pci_dev *dev; + struct resource *r; + int i; + + for (r = pr->child; r != NULL; r = r->sibling) { + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + list_for_each_entry(bus, &parent->children, node) { + for (i = 0; i < 4; ++i) { + if ((r = bus->resource[i]) == NULL) + continue; + if (!r->flags || r->start > r->end || r == res) + continue; + if (pci_find_parent_resource(bus->self, r) != pr) + continue; + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + } + list_for_each_entry(dev, &parent->devices, bus_list) { + for (i = 0; i < 6; ++i) { + r = &dev->resource[i]; + if (!r->flags || (r->flags & IORESOURCE_UNSET)) + continue; + if (pci_find_parent_resource(dev, r) != pr) + continue; + if (r->end >= res->start && res->end >= r->start) { + *conflict = r; + return 1; + } + } + } + return 0; +} + +static void __init +update_bridge_base(struct pci_bus *bus, int i) +{ + struct resource *res = bus->resource[i]; + u8 io_base_lo, io_limit_lo; + u16 mem_base, mem_limit; + u16 cmd; + unsigned long start, end, off; + struct pci_dev *dev = bus->self; + struct pci_controller *hose = dev->sysdata; + + if (!hose) { + printk("update_bridge_base: no hose?\n"); + return; + } + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, + cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); + if (res->flags & IORESOURCE_IO) { + off = (unsigned long) hose->io_base_virt - isa_io_base; + start = res->start - off; + end = res->end - off; + io_base_lo = (start >> 8) & PCI_IO_RANGE_MASK; + io_limit_lo = (end >> 8) & PCI_IO_RANGE_MASK; + if (end > 0xffff) { + pci_write_config_word(dev, PCI_IO_BASE_UPPER16, + start >> 16); + pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16, + end >> 16); + io_base_lo |= PCI_IO_RANGE_TYPE_32; + } else + io_base_lo |= PCI_IO_RANGE_TYPE_16; + pci_write_config_byte(dev, PCI_IO_BASE, io_base_lo); + pci_write_config_byte(dev, PCI_IO_LIMIT, io_limit_lo); + + } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) + == IORESOURCE_MEM) { + off = hose->pci_mem_offset; + mem_base = ((res->start - off) >> 16) & PCI_MEMORY_RANGE_MASK; + mem_limit = ((res->end - off) >> 16) & PCI_MEMORY_RANGE_MASK; + pci_write_config_word(dev, PCI_MEMORY_BASE, mem_base); + pci_write_config_word(dev, PCI_MEMORY_LIMIT, mem_limit); + + } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH)) + == (IORESOURCE_MEM | IORESOURCE_PREFETCH)) { + off = hose->pci_mem_offset; + mem_base = ((res->start - off) >> 16) & PCI_PREF_RANGE_MASK; + mem_limit = ((res->end - off) >> 16) & PCI_PREF_RANGE_MASK; + pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, mem_base); + pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, mem_limit); + + } else { + DBG(KERN_ERR "PCI: ugh, bridge %s res %d has flags=%lx\n", + pci_name(dev), i, res->flags); + } + pci_write_config_word(dev, PCI_COMMAND, cmd); +} + +static inline void alloc_resource(struct pci_dev *dev, int idx) +{ + struct resource *pr, *r = &dev->resource[idx]; + + DBG("PCI:%s: Resource %d: %08lx-%08lx (f=%lx)\n", + pci_name(dev), idx, r->start, r->end, r->flags); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI: Cannot allocate resource region %d" + " of device %s\n", idx, pci_name(dev)); + if (pr) + DBG("PCI: parent is %p: %08lx-%08lx (f=%lx)\n", + pr, pr->start, pr->end, pr->flags); + /* We'll assign a new address later */ + r->flags |= IORESOURCE_UNSET; + r->end -= r->start; + r->start = 0; + } +} + +static void __init +pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + int idx, disabled; + u16 command; + struct resource *r; + + for_each_pci_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->flags || (r->flags & IORESOURCE_UNSET)) + continue; /* Not assigned at all */ + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) + alloc_resource(dev, idx); + } + if (pass) + continue; + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", pci_name(dev)); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, + reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } +} + +static void __init +pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + int idx; + struct resource *r; + + for_each_pci_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + + /* + * We shall assign a new address to this resource, + * either because the BIOS (sic) forgot to do so + * or because we have decided the old address was + * unusable for some reason. + */ + if ((r->flags & IORESOURCE_UNSET) && r->end && + (!ppc_md.pcibios_enable_device_hook || + !ppc_md.pcibios_enable_device_hook(dev, 1))) { + r->flags &= ~IORESOURCE_UNSET; + pci_assign_resource(dev, idx); + } + } + +#if 0 /* don't assign ROMs */ + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); +#endif + } +} + + +int +pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1<<idx))) + continue; + + r = &dev->resource[idx]; + if (r->flags & IORESOURCE_UNSET) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +static int next_controller_index; + +struct pci_controller * __init +pcibios_alloc_controller(void) +{ + struct pci_controller *hose; + + hose = (struct pci_controller *)alloc_bootmem(sizeof(*hose)); + memset(hose, 0, sizeof(struct pci_controller)); + + *hose_tail = hose; + hose_tail = &hose->next; + + hose->index = next_controller_index++; + + return hose; +} + +#ifdef CONFIG_PPC_OF +/* + * Functions below are used on OpenFirmware machines. + */ +static void +make_one_node_map(struct device_node* node, u8 pci_bus) +{ + int *bus_range; + int len; + + if (pci_bus >= pci_bus_count) + return; + bus_range = (int *) get_property(node, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s, " + "assuming it starts at 0\n", node->full_name); + pci_to_OF_bus_map[pci_bus] = 0; + } else + pci_to_OF_bus_map[pci_bus] = bus_range[0]; + + for (node=node->child; node != 0;node = node->sibling) { + struct pci_dev* dev; + unsigned int *class_code, *reg; + + class_code = (unsigned int *) get_property(node, "class-code", NULL); + if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) + continue; + reg = (unsigned int *)get_property(node, "reg", NULL); + if (!reg) + continue; + dev = pci_find_slot(pci_bus, ((reg[0] >> 8) & 0xff)); + if (!dev || !dev->subordinate) + continue; + make_one_node_map(node, dev->subordinate->number); + } +} + +void +pcibios_make_OF_bus_map(void) +{ + int i; + struct pci_controller* hose; + u8* of_prop_map; + + pci_to_OF_bus_map = (u8*)kmalloc(pci_bus_count, GFP_KERNEL); + if (!pci_to_OF_bus_map) { + printk(KERN_ERR "Can't allocate OF bus map !\n"); + return; + } + + /* We fill the bus map with invalid values, that helps + * debugging. + */ + for (i=0; i<pci_bus_count; i++) + pci_to_OF_bus_map[i] = 0xff; + + /* For each hose, we begin searching bridges */ + for(hose=hose_head; hose; hose=hose->next) { + struct device_node* node; + node = (struct device_node *)hose->arch_data; + if (!node) + continue; + make_one_node_map(node, hose->first_busno); + } + of_prop_map = get_property(find_path_device("/"), "pci-OF-bus-map", NULL); + if (of_prop_map) + memcpy(of_prop_map, pci_to_OF_bus_map, pci_bus_count); +#ifdef DEBUG + printk("PCI->OF bus map:\n"); + for (i=0; i<pci_bus_count; i++) { + if (pci_to_OF_bus_map[i] == 0xff) + continue; + printk("%d -> %d\n", i, pci_to_OF_bus_map[i]); + } +#endif +} + +typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data); + +static struct device_node* +scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void* data) +{ + struct device_node* sub_node; + + for (; node != 0;node = node->sibling) { + unsigned int *class_code; + + if (filter(node, data)) + return node; + + /* For PCI<->PCI bridges or CardBus bridges, we go down + * Note: some OFs create a parent node "multifunc-device" as + * a fake root for all functions of a multi-function device, + * we go down them as well. + */ + class_code = (unsigned int *) get_property(node, "class-code", NULL); + if ((!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && + (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) && + strcmp(node->name, "multifunc-device")) + continue; + sub_node = scan_OF_pci_childs(node->child, filter, data); + if (sub_node) + return sub_node; + } + return NULL; +} + +static int +scan_OF_pci_childs_iterator(struct device_node* node, void* data) +{ + unsigned int *reg; + u8* fdata = (u8*)data; + + reg = (unsigned int *) get_property(node, "reg", NULL); + if (reg && ((reg[0] >> 8) & 0xff) == fdata[1] + && ((reg[0] >> 16) & 0xff) == fdata[0]) + return 1; + return 0; +} + +static struct device_node* +scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) +{ + u8 filter_data[2] = {bus, dev_fn}; + + return scan_OF_pci_childs(node, scan_OF_pci_childs_iterator, filter_data); +} + +/* + * Scans the OF tree for a device node matching a PCI device + */ +struct device_node * +pci_busdev_to_OF_node(struct pci_bus *bus, int devfn) +{ + struct pci_controller *hose; + struct device_node *node; + int busnr; + + if (!have_of) + return NULL; + + /* Lookup the hose */ + busnr = bus->number; + hose = pci_bus_to_hose(busnr); + if (!hose) + return NULL; + + /* Check it has an OF node associated */ + node = (struct device_node *) hose->arch_data; + if (!node) + return NULL; + + /* Fixup bus number according to what OF think it is. */ +#ifdef CONFIG_PPC_PMAC + /* The G5 need a special case here. Basically, we don't remap all + * busses on it so we don't create the pci-OF-map. However, we do + * remap the AGP bus and so have to deal with it. A future better + * fix has to be done by making the remapping per-host and always + * filling the pci_to_OF map. --BenH + */ + if (_machine == _MACH_Pmac && busnr >= 0xf0) + busnr -= 0xf0; + else +#endif + if (pci_to_OF_bus_map) + busnr = pci_to_OF_bus_map[busnr]; + if (busnr == 0xff) + return NULL; + + /* Now, lookup childs of the hose */ + return scan_OF_childs_for_device(node->child, busnr, devfn); +} +EXPORT_SYMBOL(pci_busdev_to_OF_node); + +struct device_node* +pci_device_to_OF_node(struct pci_dev *dev) +{ + return pci_busdev_to_OF_node(dev->bus, dev->devfn); +} +EXPORT_SYMBOL(pci_device_to_OF_node); + +/* This routine is meant to be used early during boot, when the + * PCI bus numbers have not yet been assigned, and you need to + * issue PCI config cycles to an OF device. + * It could also be used to "fix" RTAS config cycles if you want + * to set pci_assign_all_buses to 1 and still use RTAS for PCI + * config cycles. + */ +struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) +{ + if (!have_of) + return NULL; + while(node) { + struct pci_controller* hose; + for (hose=hose_head;hose;hose=hose->next) + if (hose->arch_data == node) + return hose; + node=node->parent; + } + return NULL; +} + +static int +find_OF_pci_device_filter(struct device_node* node, void* data) +{ + return ((void *)node == data); +} + +/* + * Returns the PCI device matching a given OF node + */ +int +pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn) +{ + unsigned int *reg; + struct pci_controller* hose; + struct pci_dev* dev = NULL; + + if (!have_of) + return -ENODEV; + /* Make sure it's really a PCI device */ + hose = pci_find_hose_for_OF_device(node); + if (!hose || !hose->arch_data) + return -ENODEV; + if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child, + find_OF_pci_device_filter, (void *)node)) + return -ENODEV; + reg = (unsigned int *) get_property(node, "reg", NULL); + if (!reg) + return -ENODEV; + *bus = (reg[0] >> 16) & 0xff; + *devfn = ((reg[0] >> 8) & 0xff); + + /* Ok, here we need some tweak. If we have already renumbered + * all busses, we can't rely on the OF bus number any more. + * the pci_to_OF_bus_map is not enough as several PCI busses + * may match the same OF bus number. + */ + if (!pci_to_OF_bus_map) + return 0; + + for_each_pci_dev(dev) + if (pci_to_OF_bus_map[dev->bus->number] == *bus && + dev->devfn == *devfn) { + *bus = dev->bus->number; + pci_dev_put(dev); + return 0; + } + + return -ENODEV; +} +EXPORT_SYMBOL(pci_device_from_OF_node); + +void __init +pci_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary) +{ + static unsigned int static_lc_ranges[256] __initdata; + unsigned int *dt_ranges, *lc_ranges, *ranges, *prev; + unsigned int size; + int rlen = 0, orig_rlen; + int memno = 0; + struct resource *res; + int np, na = prom_n_addr_cells(dev); + np = na + 5; + + /* First we try to merge ranges to fix a problem with some pmacs + * that can have more than 3 ranges, fortunately using contiguous + * addresses -- BenH + */ + dt_ranges = (unsigned int *) get_property(dev, "ranges", &rlen); + if (!dt_ranges) + return; + /* Sanity check, though hopefully that never happens */ + if (rlen > sizeof(static_lc_ranges)) { + printk(KERN_WARNING "OF ranges property too large !\n"); + rlen = sizeof(static_lc_ranges); + } + lc_ranges = static_lc_ranges; + memcpy(lc_ranges, dt_ranges, rlen); + orig_rlen = rlen; + + /* Let's work on a copy of the "ranges" property instead of damaging + * the device-tree image in memory + */ + ranges = lc_ranges; + prev = NULL; + while ((rlen -= np * sizeof(unsigned int)) >= 0) { + if (prev) { + if (prev[0] == ranges[0] && prev[1] == ranges[1] && + (prev[2] + prev[na+4]) == ranges[2] && + (prev[na+2] + prev[na+4]) == ranges[na+2]) { + prev[na+4] += ranges[na+4]; + ranges[0] = 0; + ranges += np; + continue; + } + } + prev = ranges; + ranges += np; + } + + /* + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 2: a PCI address + * cells 3 or 3+4: a CPU physical address + * (size depending on dev->n_addr_cells) + * cells 4+5 or 5+6: the size of the range + */ + ranges = lc_ranges; + rlen = orig_rlen; + while (ranges && (rlen -= np * sizeof(unsigned int)) >= 0) { + res = NULL; + size = ranges[na+4]; + switch ((ranges[0] >> 24) & 0x3) { + case 1: /* I/O space */ + if (ranges[2] != 0) + break; + hose->io_base_phys = ranges[na+2]; + /* limit I/O space to 16MB */ + if (size > 0x01000000) + size = 0x01000000; + hose->io_base_virt = ioremap(ranges[na+2], size); + if (primary) + isa_io_base = (unsigned long) hose->io_base_virt; + res = &hose->io_resource; + res->flags = IORESOURCE_IO; + res->start = ranges[2]; + DBG("PCI: IO 0x%lx -> 0x%lx\n", + res->start, res->start + size - 1); + break; + case 2: /* memory space */ + memno = 0; + if (ranges[1] == 0 && ranges[2] == 0 + && ranges[na+4] <= (16 << 20)) { + /* 1st 16MB, i.e. ISA memory area */ + if (primary) + isa_mem_base = ranges[na+2]; + memno = 1; + } + while (memno < 3 && hose->mem_resources[memno].flags) + ++memno; + if (memno == 0) + hose->pci_mem_offset = ranges[na+2] - ranges[2]; + if (memno < 3) { + res = &hose->mem_resources[memno]; + res->flags = IORESOURCE_MEM; + if(ranges[0] & 0x40000000) + res->flags |= IORESOURCE_PREFETCH; + res->start = ranges[na+2]; + DBG("PCI: MEM[%d] 0x%lx -> 0x%lx\n", memno, + res->start, res->start + size - 1); + } + break; + } + if (res != NULL) { + res->name = dev->full_name; + res->end = res->start + size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + ranges += np; + } +} + +/* We create the "pci-OF-bus-map" property now so it appears in the + * /proc device tree + */ +void __init +pci_create_OF_bus_map(void) +{ + struct property* of_prop; + + of_prop = (struct property*) alloc_bootmem(sizeof(struct property) + 256); + if (of_prop && find_path_device("/")) { + memset(of_prop, -1, sizeof(struct property) + 256); + of_prop->name = "pci-OF-bus-map"; + of_prop->length = 256; + of_prop->value = (unsigned char *)&of_prop[1]; + prom_add_property(find_path_device("/"), of_prop); + } +} + +static ssize_t pci_show_devspec(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev; + struct device_node *np; + + pdev = to_pci_dev (dev); + np = pci_device_to_OF_node(pdev); + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); + +#else /* CONFIG_PPC_OF */ +void pcibios_make_OF_bus_map(void) +{ +} +#endif /* CONFIG_PPC_OF */ + +/* Add sysfs properties */ +void pcibios_add_platform_entries(struct pci_dev *pdev) +{ +#ifdef CONFIG_PPC_OF + device_create_file(&pdev->dev, &dev_attr_devspec); +#endif /* CONFIG_PPC_OF */ +} + + +#ifdef CONFIG_PPC_PMAC +/* + * This set of routines checks for PCI<->PCI bridges that have closed + * IO resources and have child devices. It tries to re-open an IO + * window on them. + * + * This is a _temporary_ fix to workaround a problem with Apple's OF + * closing IO windows on P2P bridges when the OF drivers of cards + * below this bridge don't claim any IO range (typically ATI or + * Adaptec). + * + * A more complete fix would be to use drivers/pci/setup-bus.c, which + * involves a working pcibios_fixup_pbus_ranges(), some more care about + * ordering when creating the host bus resources, and maybe a few more + * minor tweaks + */ + +/* Initialize bridges with base/limit values we have collected */ +static void __init +do_update_p2p_io_resource(struct pci_bus *bus, int enable_vga) +{ + struct pci_dev *bridge = bus->self; + struct pci_controller* hose = (struct pci_controller *)bridge->sysdata; + u32 l; + u16 w; + struct resource res; + + if (bus->resource[0] == NULL) + return; + res = *(bus->resource[0]); + + DBG("Remapping Bus %d, bridge: %s\n", bus->number, pci_name(bridge)); + res.start -= ((unsigned long) hose->io_base_virt - isa_io_base); + res.end -= ((unsigned long) hose->io_base_virt - isa_io_base); + DBG(" IO window: %08lx-%08lx\n", res.start, res.end); + + /* Set up the top and bottom of the PCI I/O segment for this bus. */ + pci_read_config_dword(bridge, PCI_IO_BASE, &l); + l &= 0xffff000f; + l |= (res.start >> 8) & 0x00f0; + l |= res.end & 0xf000; + pci_write_config_dword(bridge, PCI_IO_BASE, l); + + if ((l & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + l = (res.start >> 16) | (res.end & |