diff -uNr linux-2.3.paul/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile --- linux-2.3.paul/arch/ppc/kernel/Makefile Wed Mar 8 00:19:16 2000 +++ linux/arch/ppc/kernel/Makefile Sun Mar 26 16:50:18 2000 @@ -38,7 +38,7 @@ endif ifdef CONFIG_PCI -O_OBJS += pci.o pci-dma.o +O_OBJS += pci.o pci-dma.o pci_debug.o endif ifdef CONFIG_KGDB diff -uNr linux-2.3.paul/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c --- linux-2.3.paul/arch/ppc/kernel/pci.c Wed Mar 8 00:19:16 2000 +++ linux/arch/ppc/kernel/pci.c Sun Mar 26 23:09:08 2000 @@ -22,6 +22,15 @@ #include "pci.h" +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + + static void __init pcibios_claim_resources(struct list_head *); unsigned long isa_io_base = 0; @@ -67,13 +76,272 @@ generic_pcibios_write_dword }; + + +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 +#define PCI_NO_SORT 0x100 +#define PCI_BIOS_SORT 0x200 +#define PCI_NO_CHECKS 0x400 +#define PCI_PEER_FIXUP 0x800 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 + +unsigned int pci_probe; + + +void pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check; + int reg; + + new = res->start | (res->flags & PCI_REGION_FLAG_MASK); + if (resource < 6) { + reg = PCI_BASE_ADDRESS_0 + 4*resource; + } else if (resource == PCI_ROM_RESOURCE) { + res->flags |= PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resource, + new, check); + } +} + +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + /* We need to avoid collisions with `mirrored' VGA ports + and other strange ISA hardware, so we always want the + addresses kilobyte aligned. */ + if (size > 0x100) { + printk(KERN_ERR "PCI: I/O Region %s/%d too large" + " (%ld bytes)\n", dev->slot_name, + dev->resource - res, size); + } + + start = (start + 1024 - 1) & ~(1024 - 1); + res->start = start; + } +} + + +/* + * 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 list_head *ln; + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if ((dev = bus->self)) { + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) + printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + pci_for_each_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->start) /* Address not assigned at all */ + continue; + if (r->end == 0xffffffff) { /* Unassigned */ + DBG("PCI: Resource %08lx-%08lx was unassigned\n", r->start, r->end); + r->end -= r->start; + r->start = 0; + continue; + } + + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", + r->start, r->end, r->flags, disabled, pass); + 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, dev->slot_name); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & PCI_ROM_ADDRESS_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", dev->slot_name); + r->flags &= ~PCI_ROM_ADDRESS_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; + int idx; + struct resource *r; + + pci_for_each_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]; + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + continue; + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end) + pci_assign_resource(dev, idx); + } + + if (pci_probe & PCI_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); + } +/* pcibios_enable_resources(dev); */ + } +} + + +int pcibios_enable_resources(struct pci_dev *dev) +{ + 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++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + + + void __init pcibios_init(void) { +#if 0 printk("PCI: Probing PCI hardware\n"); pci_scan_bus(0, &generic_pci_ops, NULL); if (ppc_md.pcibios_fixup) ppc_md.pcibios_fixup(); pcibios_claim_resources(&pci_root_buses); +#else + printk("PCI: Probing PCI hardware (semiautomatic)\n"); + pci_scan_bus(0, &generic_pci_ops, NULL); + if (ppc_md.pcibios_fixup) + ppc_md.pcibios_fixup(); + + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); + pcibios_assign_resources(); +#endif +debug_scan_pci(); } void __init @@ -131,31 +399,12 @@ return str; } -/* the next two are stolen from the alpha port... */ -void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) -{ - unsigned long where, size; - u32 reg; - - where = PCI_BASE_ADDRESS_0 + (resource * 4); - size = res->end - res->start; - pci_read_config_dword(dev, where, ®); - reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); - pci_write_config_dword(dev, where, reg); -} - +/* the next one is stolen from the alpha port... */ void __init pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); /* XXX FIXME - update OF device tree node interrupt property */ -} - -void __init -pcibios_align_resource(void *data, struct resource *res, unsigned long size) -{ } int pcibios_enable_device(struct pci_dev *dev) diff -uNr linux-2.3.paul/arch/ppc/kernel/pci_debug.c linux/arch/ppc/kernel/pci_debug.c --- linux-2.3.paul/arch/ppc/kernel/pci_debug.c Thu Jan 1 01:00:00 1970 +++ linux/arch/ppc/kernel/pci_debug.c Sun Mar 26 16:50:18 2000 @@ -0,0 +1,349 @@ + +#include +#include + +static struct pci_dev *__pci_devices[100]; +static int num_pci_devices = 0; + +static struct pci_bus *__pci_buses[100]; +static int num_pci_buses = 0; + +static struct resource *__resources[1000]; +static int num_resources = 0; + +static void __add_pointer(const void *ptr, const void **table, + int *num_entries, int max_entries) +{ + int i; + + if (ptr == NULL) + return; + + if (*num_entries == max_entries) { + printk("add_pointer: %p is full (%d entries)\n", table, *num_entries); + return; + } + for (i = 0; i < *num_entries; i++) + if ((unsigned long)table[i] == (unsigned long)ptr) + return; + table[(*num_entries)++] = ptr; +} + +static void __sort_table(const void **table, int num_entries) +{ + int i, j; + + for (i = 0; i < num_entries-1; i++) + for (j = i+1; j < num_entries; j++) + if ((unsigned long)table[j] < (unsigned long)table[i]) { + const void *t = table[i]; + table[i] = table[j]; + table[j] = t; + } +} + +static void add_pci_device(const struct pci_dev *dev) +{ + __add_pointer(dev, (const void **)__pci_devices, &num_pci_devices, + sizeof(__pci_devices)/sizeof(void *)); +} + +static void add_pci_bus(const struct pci_bus *bus) +{ + __add_pointer(bus, (const void **)__pci_buses, &num_pci_buses, + sizeof(__pci_buses)/sizeof(void *)); +} + +static void add_resource(const struct resource *res) +{ + __add_pointer(res, (const void **)__resources, &num_resources, + sizeof(__resources)/sizeof(void *)); +} + +static void sort_pci_devices(void) +{ + __sort_table((const void **)__pci_devices, num_pci_devices); +} + +static void sort_pci_buses(void) +{ + __sort_table((const void **)__pci_buses, num_pci_buses); +} + +static void sort_resources(void) +{ + __sort_table((const void **)__resources, num_resources); +} + +static void dump_resource_terse(int n, const struct resource *res) +{ + if (!res || !res->flags) + return; + printk(" %2d: %p [", n, res); + if (res->flags & IORESOURCE_IO) + printk(" I/O"); + if (res->flags & IORESOURCE_MEM) + printk(" MEM"); + if (res->flags & IORESOURCE_IRQ) + printk(" IRQ"); + if (res->flags & IORESOURCE_DMA) + printk(" DMA"); + if (res->flags & (IORESOURCE_IO|IORESOURCE_MEM)) + printk(" %p-%p", (void *)res->start, (void *)res->end); + printk(" ]\n"); +} + +void dump_resource(const struct resource *res) +{ + printk("Resource %p %s\n", res, res->name); + if (!res->flags) + return; + printk(" range = %p-%p\n", (void *)res->start, (void *)res->end); + printk(" flags = 0x%08lx [", res->flags); + if (res->flags & IORESOURCE_IO) + printk(" IO"); + if (res->flags & IORESOURCE_MEM) + printk(" MEM"); + if (res->flags & IORESOURCE_IRQ) + printk(" IRQ"); + if (res->flags & IORESOURCE_DMA) + printk(" DMA"); + + if (res->flags & IORESOURCE_PREFETCH) + printk(" PREFETCH"); + if (res->flags & IORESOURCE_READONLY) + printk(" READONLY"); + if (res->flags & IORESOURCE_CACHEABLE) + printk(" CACHEABLE"); + if (res->flags & IORESOURCE_RANGELENGTH) + printk(" RANGELENGTH"); + if (res->flags & IORESOURCE_SHADOWABLE) + printk(" SHADOWABLE"); + + if (res->flags & IORESOURCE_UNSET) + printk(" UNSET"); + if (res->flags & IORESOURCE_AUTO) + printk(" AUTO"); + if (res->flags & IORESOURCE_BUSY) + printk(" BUSY"); + + if (res->flags & IORESOURCE_IRQ) { + if (res->flags & IORESOURCE_IRQ_HIGHEDGE) + printk(" IRQ_HIGHEDGE"); + if (res->flags & IORESOURCE_IRQ_LOWEDGE) + printk(" IRQ_LOWEDGE"); + if (res->flags & IORESOURCE_IRQ_HIGHLEVEL) + printk(" IRQ_HIGHLEVEL"); + if (res->flags & IORESOURCE_IRQ_LOWLEVEL) + printk(" IRQ_LOWLEVEL"); + } + + if (res->flags & IORESOURCE_DMA) { + switch (res->flags & IORESOURCE_DMA_TYPE_MASK) { + case IORESOURCE_DMA_8BIT: + printk(" DMA_8BIT"); + break; + case IORESOURCE_DMA_8AND16BIT: + printk(" DMA_8AND16BIT"); + break; + case IORESOURCE_DMA_16BIT: + printk(" DMA_16BIT"); + break; + } + if (res->flags & IORESOURCE_DMA_MASTER) + printk(" DMA_MASTER"); + if (res->flags & IORESOURCE_DMA_BYTE) + printk(" DMA_BYTE"); + if (res->flags & IORESOURCE_DMA_WORD) + printk(" DMA_WORD"); + switch (res->flags & IORESOURCE_DMA_SPEED_MASK) { + case IORESOURCE_DMA_COMPATIBLE: + printk(" DMA_COMPATIBLE"); + break; + case IORESOURCE_DMA_TYPEA: + printk(" DMA_TYPEA"); + break; + case IORESOURCE_DMA_TYPEB: + printk(" DMA_TYPEB"); + break; + case IORESOURCE_DMA_TYPEF: + printk(" DMA_TYPEF"); + break; + } + } + + if (res->flags & IORESOURCE_MEM) { + if (res->flags & IORESOURCE_MEM_WRITEABLE) + printk(" MEM_WRITEABLE"); + if (res->flags & IORESOURCE_MEM_CACHEABLE) + printk(" MEM_CACHEABLE"); + if (res->flags & IORESOURCE_MEM_RANGELENGTH) + printk(" MEM_RANGELENGTH"); + switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { + case IORESOURCE_MEM_8BIT: + printk(" MEM_8BIT"); + break; + case IORESOURCE_MEM_16BIT: + printk(" MEM_16BIT"); + break; + case IORESOURCE_MEM_8AND16BIT: + printk(" MEM_8AND16BIT"); + break; + } + if (res->flags & IORESOURCE_MEM_SHADOWABLE) + printk(" MEM_SHADOWABLE"); + if (res->flags & IORESOURCE_MEM_EXPANSIONROM) + printk(" MEM_EXPANSIONROM"); + } + printk(" ]\n"); + printk(" parent = %p sibling = %p child = %p\n", res->parent, + res->sibling, res->child); +} + +void dump_pci_device(const struct pci_dev *dev) +{ + int i; + + printk("PCI device %p %s %s\n", dev, dev->slot_name, dev->name); + printk(" global_list.prev = %p global_list.next = %p\n", + dev->global_list.prev, dev->global_list.next); + printk(" bus_list.prev = %p bus_list.next = %p\n", dev->bus_list.prev, + dev->bus_list.next); + printk(" bus = %p subordinate = %p\n", dev->bus, dev->subordinate); + printk(" sysdata = %p procent = %p devfn = 0x%08x\n", dev->sysdata, + dev->procent, dev->devfn); + printk(" vendor/device = %04x:%04x subsystem = 0x%04x:%04x\n", + dev->vendor, dev->device, dev->subsystem_vendor, + dev->subsystem_device); + printk(" class = 0x%08x hdr_type = 0x%02x rom_base_reg = 0x%02x\n", + dev->class, dev->hdr_type, dev->rom_base_reg); + printk(" driver = %p driver_data = %p\n", dev->driver, dev->driver_data); + printk(" dma_mask = %p\n", (void *)dev->dma_mask); + printk(" compatible[] = "); + for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) + printk(" %04x:%04x", dev->vendor_compatible[i], + dev->device_compatible[i]); + printk("\n"); + printk(" irq = %d\n", dev->irq); + printk(" resource[] =\n"); + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + dump_resource_terse(i, &dev->resource[i]); + printk(" dma_resource[] =\n"); + for (i = 0; i < DEVICE_COUNT_DMA; i++) + dump_resource_terse(i, &dev->dma_resource[i]); + printk(" irq_resource[] =\n"); + for (i = 0; i < DEVICE_COUNT_IRQ; i++) + dump_resource_terse(i, &dev->irq_resource[i]); + printk(" active = %d ro = %d regs = 0x%04x\n", dev->active, dev->ro, + dev->regs); + printk(" prepare = %p activate = %p deactivate = %p\n", dev->prepare, + dev->activate, dev->deactivate); + printk("\n"); +} + +void dump_pci_bus(const struct pci_bus *bus) +{ + int i; + + printk("PCI bus %p %s\n", bus, bus->name); + printk(" node.prev = %p node.next = %p\n", bus->node.prev, + bus->node.next); + printk(" parent = %p\n", bus->parent); + printk(" children.prev = %p children.next = %p\n", bus->children.prev, + bus->children.next); + printk(" devices.prev = %p devices.next = %p\n", bus->devices.prev, + bus->devices.next); + printk(" self = %p\n", bus->self); + printk(" resource[] = [\n"); + for (i = 0; i < 4; i++) + dump_resource_terse(i, bus->resource[i]); + printk(" ]\n"); + printk(" ops = %p sysdata = %p procdir = %p\n", bus->ops, bus->sysdata, + bus->procdir); + printk(" number = %d primary = %d secondary = %d subordinate = %d\n", + bus->number, bus->primary, bus->secondary, bus->subordinate); + printk(" vendor/device = %04x:%04x\n", bus->vendor, bus->device); + printk(" serial = %d pnpver = %d productver = %d checksum = %d\n", + bus->serial, bus->pnpver, bus->productver, bus->checksum); + printk("\n"); +} + + +static void scan_pci_buses(void) +{ + struct pci_bus *bus; + int i; + + for (bus = pci_bus_b(pci_root_buses.next); + bus != pci_bus_b(&pci_root_buses); + bus = pci_bus_b(bus->node.next)) { + add_pci_bus(bus); + for (i = 0; i < 4; i++) + add_resource(bus->resource[i]); + } +} + +static void scan_pci_devices(void) +{ + struct pci_dev *dev; + int i; + + pci_for_each_dev(dev) { + add_pci_device(dev); + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) + add_resource(&dev->resource[i]); + for (i = 0; i < DEVICE_COUNT_DMA; i++) + add_resource(&dev->dma_resource[i]); + for (i = 0; i < DEVICE_COUNT_IRQ; i++) + add_resource(&dev->irq_resource[i]); + } +} + +void dump_pci_buses(void) +{ + int i; + + printk(">>>>>>>>>>>>>>>>>>>> PCI Buses >>>>>>>>>>>>>>>>>>>>\n"); + for (i = 0; i < num_pci_buses; i++) + dump_pci_bus(__pci_buses[i]); + printk("<<<<<<<<<<<<<<<<<<<< PCI Buses <<<<<<<<<<<<<<<<<<<<\n"); +} + + +void dump_pci_devices(void) +{ + int i; + + printk(">>>>>>>>>>>>>>>>>>>> PCI Devices >>>>>>>>>>>>>>>>>>>>\n"); + for (i = 0; i < num_pci_devices; i++) + dump_pci_device(__pci_devices[i]); + printk("<<<<<<<<<<<<<<<<<<<< PCI Devices <<<<<<<<<<<<<<<<<<<<\n"); +} + + +void dump_resources(void) +{ + int i; + + printk(">>>>>>>>>>>>>>>>>>>> Resources >>>>>>>>>>>>>>>>>>>>\n"); + for (i = 0; i < num_resources; i++) + dump_resource(__resources[i]); + printk("<<<<<<<<<<<<<<<<<<<< Resources <<<<<<<<<<<<<<<<<<<<\n"); +} + +void debug_scan_pci(void) +{ + scan_pci_buses(); + scan_pci_devices(); + sort_pci_buses(); + sort_pci_devices(); + sort_resources(); +} + +void debug_dump_pci(void) +{ + dump_pci_buses(); + dump_pci_devices(); + dump_resources(); +} + diff -uNr linux-2.3.paul/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c --- linux-2.3.paul/arch/ppc/kernel/pmac_pci.c Fri Feb 11 00:19:51 2000 +++ linux/arch/ppc/kernel/pmac_pci.c Sun Mar 26 23:15:04 2000 @@ -27,6 +27,15 @@ #include "pci.h" +#undef DEBUG +#define DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + struct bridge_data **bridges, *bridge_list; static int max_bus; @@ -41,6 +50,10 @@ static int uninorth_default = -1; static void add_bridges(struct device_node *dev); +struct pci_bus *fix_chaos(struct bridge_data *); +void fix_planb(struct pci_dev *); + +extern struct pci_ops generic_pci_ops; /* * Magic constants for enabling cache coherency in the bandit/PSX bridge. @@ -54,6 +67,10 @@ #define BANDIT_MAGIC 0x50 #define BANDIT_COHERENT 0x40 +#define CONTROL_DEVID 3 +#define PLANB_DEVID 4 +#define PLANB_BASE 0xf1000000 + __pmac void *pci_io_base(unsigned int bus) { @@ -65,6 +82,16 @@ } __pmac +int pci_io_length(unsigned int bus) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return 0; + return bp->io_length; +} + +__pmac int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, unsigned char *devfn_ptr) { @@ -613,6 +640,7 @@ bp->cfg_data = 0; /* is 0x10000 enough for io space ? */ bp->io_base = (void *)ioremap(addr->address, 0x10000); + bp->io_length = 0x10000; } else if (strcmp(dev->name, "pci") == 0) { /* XXX assume this is a mpc106 (grackle) */ bp->cfg_addr = (volatile unsigned int *) @@ -620,6 +648,7 @@ bp->cfg_data = (volatile unsigned char *) ioremap(0xfee00000, 0x1000); bp->io_base = (void *) ioremap(0xfe000000, 0x20000); + bp->io_length = 0x20000; #if 0 /* Disabled for now, HW problems */ grackle_set_stg(bp, 1); #endif @@ -629,7 +658,8 @@ ioremap(addr->address + 0x800000, 0x1000); bp->cfg_data = (volatile unsigned char *) ioremap(addr->address + 0xc00000, 0x1000); - bp->io_base = (void *) ioremap(addr->address, 0x10000); + bp->io_base = (void *) ioremap(addr->address, 0x800000); + bp->io_length = 0x800000; } if (isa_io_base == 0) isa_io_base = (unsigned long) bp->io_base; @@ -664,8 +694,11 @@ if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn) continue; /* this is the node, see if it has interrupts */ - if (node->n_intrs > 0) + if (node->n_intrs > 0) { dev->irq = node->intrs[0].line; + DBG("PCI: Setting IRQ %d on device %s.\n", + dev->irq, dev->slot_name); + } break; } } @@ -674,35 +707,243 @@ pmac_pcibios_fixup(void) { struct pci_dev *dev; + struct bridge_data *bp; + struct resource *newres; + char *name; + int i, size; /* - * FIXME: This is broken: We should not assign IRQ's to IRQless - * devices (look at PCI_INTERRUPT_PIN) and we also should - * honor the existence of multi-function devices where - * different functions have different interrupt pins. [mj] + * The generic PCI code scans only bus 0 for devices and P2P + * bridges. We fix this here based on the array of host + * bridges. + * + * We also provide new IO port resources for each bus decribing + * the available IO space. */ - pci_for_each_dev(dev) - { - /* - * Open Firmware often doesn't initialize the, - * PCI_INTERRUPT_LINE config register properly, so we - * should find the device node and se if it has an - * AAPL,interrupts property. - */ + /* Count the host bridges */ + for (bp = bridge_list, i = 0; bp != NULL; bp = bp->next, i++); + + size = i * (sizeof(*newres) + 21); + if ((newres = kmalloc(size, GFP_KERNEL)) == 0) { + printk(KERN_ERR "PCI: Out of memory in pmac_pcibios_fixup!\n"); + return; + } + memset(newres, 0, size); + name = (char *)(newres + i); + + for (bp = bridge_list; bp != NULL; bp = bp->next) { + struct pci_bus *bus = NULL; + int b = bp->bus_number; + + if (strcmp(bp->node->name, "chaos") == 0) continue; +/* bus = fix_chaos(bp); */ + else if (b == 0) { + /* hack to get at struct pci_bus for bus 0 */ + pci_for_each_dev(dev) { + if (dev->bus->number != 0) continue; + bus = dev->bus; + break; + } + } else + bus = pci_scan_bus(b, &generic_pci_ops, NULL); + + if (bus == NULL) continue; /* something went wrong */ + + sprintf(name, "%s PCI IO", bp->node->name); + newres->name = name; + newres->start = (unsigned long)pci_io_base(b); + newres->end = newres->start + pci_io_length(b); + newres->flags = IORESOURCE_IO; + bus->resource[0] = newres; + newres ++; + name += 21; + } + + pci_for_each_dev(dev) { struct bridge_data *bp = bridges[dev->bus->number]; unsigned char pin; + + DBG("PCI: Fixing device %s (%04x:%04x)\n", + dev->slot_name, + dev->vendor, dev->device); + + /* SPECIAL DEVICES + * --------------- + * control was fixed in fix_chaos(). + */ + if (dev->vendor == APPLE_VENDID && + dev->device == CONTROL_DEVID) { + continue; + } + /* planb needs special care. + */ + if (dev->vendor == APPLE_VENDID && + dev->device == PLANB_DEVID) { + fix_planb (dev); + continue; + } + /* INTERRUPT FIXING + * ---------------- + * Open Firmware doesn't initialize the PCI_INTERRUPT_LINE + * config register, so we need to find the device node and + * see if it has an AAPL,interrupts property. + * + * Note that INTA# - INTD# are OR'ed together per slot, + * so no need to worry about multifunction cards. + */ - if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || - !pin) - continue; /* No interrupt generated -> no fixup */ - /* We iterate all instances of uninorth for now */ - if (uninorth_count && dev->bus->number == 0) { - int i; - for (i=0;ichild, dev); - } else - fix_intr(bp->node->child, dev); + /* Is there an interrupt? */ + if ( !(pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || + !pin) ) { + /* We iterate all instances of uninorth for now */ + if (uninorth_count && dev->bus->number == 0) { + int i; + for (i=0;ichild, dev); + } else + fix_intr(bp->node->child, dev); + } + /* + * Since the PMac host bridges do translate IO accesses, + * we correct the kernel PCI device database here with an + * offset we add to IO region's base address. The exact + * offset is provided by the bridge controling the + * device's bus. + */ + if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL) goto nextfix; + + for(i=0; i<6; i++) { + struct resource *res = &dev->resource[i]; + unsigned long base; + unsigned long a = res->start; + + if (res->flags & PCI_BASE_ADDRESS_SPACE_IO) { + base = (unsigned long) + pci_io_base(dev->bus->number); + /* unassigned region? */ + if (a == 0) continue; + /* strange value? */ + if (a > 0x800000) continue; + if (a < base) a += base; + if (a > base) { + res->start = a; + res->end += base; + DBG("PCI: Correcting IO address %d on " + "device %s, now %08lx.\n", + i, dev->slot_name, a); + } + } + } +nextfix: + /* + * Open Firmware does not enable I/O and memory space + * response on PCI devices. We try to fix this, but we need + * to be sure that OF didn't forget to assign an address + * to the device. [mj] + * + * FIXME: How can we know? We should use OF properties.... + * Or maybe the new 2.3 resource code? + */ + pcibios_enable_device(dev); + } +} + +/* + * The chaos hostbridge, controlling the separate video bus + * on the 7x00/8x00 PowerMacs, doesn't like being probed for + * attached devices. Therefore, we rely on OF to discover those. + */ + +struct pci_bus * __init +fix_chaos (struct bridge_data *bp) +{ + struct device_node *nd; + struct pci_dev temp; + struct pci_bus *b; + + b = pci_alloc_primary_bus(bp->bus_number); + b->sysdata = NULL; + b->ops = &generic_pci_ops; + b->subordinate = bp->max_bus; + /* + * Walk OF's list of devices on this bus. + */ + for (nd = bp->node->child; nd; nd = nd->sibling) { + /* + * We need at least one address entry to get the PCI + * device / function values. + */ + if (nd->n_addrs == 0) { + printk(KERN_ERR "PCI: %s: not a PCI device!\n", + nd->name); + continue; + } + temp.devfn = (nd->addrs[0].space >> 8) & 0xff; + temp.bus = b; + pci_scan_slot(&temp); + } + return b; +} + +void __init +fix_planb (struct pci_dev *pcidev) +{ + /* There is a bug with the way OF assigns addresses + * to the devices behind the chaos bridge. + * control needs only 0x1000 of space, yet decodes only + * the upper 16 bits. It therefore occupies a full 64K. + * OF assigns the planb controller memory within this space; + * so we need to change that here in order to access planb. + * Note that the new address (0xf1000000) is within chaos' + * address space, so it should never get assigned to other + * devices by OF. + * planb also gets its interrupt set. + */ + struct device_node *planb_device; + struct resource *res; + unsigned char bus, devfn, confreg; + unsigned int reg; + u32 sz; + + DBG("PCI: fixing PlanB...\n"); + planb_device = find_devices("planb"); + if (planb_device == 0) { + printk(KERN_WARNING "PCI: Error fixing planb: no OF device.\n"); + return; + } + if (planb_device->n_addrs != 1) { + printk(KERN_WARNING "PCI: Error fixing planb: expected 1 " + "address, got %d.\n", planb_device->n_addrs); + return; + } + if (planb_device->n_intrs == 0) { + printk(KERN_WARNING "PCI: Error fixing planb: no IRQ.\n"); + return; } + bus = (planb_device->addrs[0].space >> 16) & 0xff; + devfn = (planb_device->addrs[0].space >> 8) & 0xff; + if ((bus != pcidev->bus->number) || (devfn != pcidev->devfn)) { + printk(KERN_WARNING "PCI: Error fixing planb: OF and PCI " + "device don't match!\n"); + return; + } + pcidev->irq = planb_device->intrs[0].line; + confreg = planb_device->addrs[0].space & 0xff; + reg = (confreg - PCI_BASE_ADDRESS_0) >> 2; + /* Set the new base address */ + pcibios_write_config_dword(bus, devfn, confreg, ~0); + pcibios_read_config_dword(bus, devfn, confreg, &sz); + pcibios_write_config_dword(bus, devfn, confreg, PLANB_BASE); + res = &pcidev->resource[reg]; + res->start = PLANB_BASE & PCI_BASE_ADDRESS_MEM_MASK; + sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK); + res->end = res->start + (unsigned long) sz; + /* + * Everything else should be set up right from the generic scan + * code. Now enable PlanB... + */ + pcibios_enable_device(pcidev); + return; } void __init diff -uNr linux-2.3.paul/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c --- linux-2.3.paul/arch/ppc/kernel/pmac_setup.c Wed Mar 8 00:19:16 2000 +++ linux/arch/ppc/kernel/pmac_setup.c Sun Mar 26 09:31:15 2000 @@ -684,7 +684,8 @@ ppc_ide_md.fix_driveid = pmac_ide_fix_driveid; ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; - ppc_ide_md.io_base = _IO_BASE; /* actually too early for this :-( */ +/* ppc_ide_md.io_base = _IO_BASE; actually too early for this :-( */ + ppc_ide_md.io_base = 0; #endif #ifdef CONFIG_BOOTX_TEXT ppc_md.progress = pmac_progress; diff -uNr linux-2.3.paul/drivers/pci/pci.c linux/drivers/pci/pci.c --- linux-2.3.paul/drivers/pci/pci.c Sat Mar 25 06:50:06 2000 +++ linux/drivers/pci/pci.c Sun Mar 26 09:16:57 2000 @@ -23,6 +23,7 @@ #include /* isa_dma_bridge_buggy */ #undef DEBUG +#define DEBUG #ifdef DEBUG #define DBG(x...) printk(x) diff -uNr linux-2.3.paul/drivers/pci/proc.c linux/drivers/pci/proc.c --- linux-2.3.paul/drivers/pci/proc.c Sun Feb 27 09:26:19 2000 +++ linux/drivers/pci/proc.c Sun Mar 26 16:50:18 2000 @@ -385,6 +385,7 @@ int nprinted, len, begin = 0; struct pci_dev *dev; +debug_dump_pci(); len = sprintf(buf, "PCI devices found:\n"); *eof = 1; diff -uNr linux-2.3.paul/drivers/pci/quirks.c linux/drivers/pci/quirks.c --- linux-2.3.paul/drivers/pci/quirks.c Fri Mar 3 00:19:19 2000 +++ linux/drivers/pci/quirks.c Sun Mar 26 16:50:18 2000 @@ -17,7 +17,7 @@ #include #include -#undef DEBUG +#define DEBUG /* Deal with broken BIOS'es that neglect to enable passive release, which can cause problems in combination with the 82441FX/PPro MTRRs */ diff -uNr linux-2.3.paul/drivers/pci/setup-bus.c linux/drivers/pci/setup-bus.c --- linux-2.3.paul/drivers/pci/setup-bus.c Fri Jan 21 23:46:55 2000 +++ linux/drivers/pci/setup-bus.c Sun Mar 26 16:50:18 2000 @@ -17,7 +17,7 @@ #include -#define DEBUG_CONFIG 0 +#define DEBUG_CONFIG 1 #if DEBUG_CONFIG # define DBGC(args) printk args #else diff -uNr linux-2.3.paul/drivers/pci/setup-irq.c linux/drivers/pci/setup-irq.c --- linux-2.3.paul/drivers/pci/setup-irq.c Fri Jan 21 23:46:55 2000 +++ linux/drivers/pci/setup-irq.c Sun Mar 26 16:50:18 2000 @@ -18,7 +18,7 @@ #include -#define DEBUG_CONFIG 0 +#define DEBUG_CONFIG 1 #if DEBUG_CONFIG # define DBGC(args) printk args #else diff -uNr linux-2.3.paul/drivers/pci/setup-res.c linux/drivers/pci/setup-res.c --- linux-2.3.paul/drivers/pci/setup-res.c Fri Jan 21 23:46:55 2000 +++ linux/drivers/pci/setup-res.c Sun Mar 26 16:50:18 2000 @@ -19,7 +19,7 @@ #include -#define DEBUG_CONFIG 0 +#define DEBUG_CONFIG 1 #if DEBUG_CONFIG # define DBGC(args) printk args #else diff -uNr linux-2.3.paul/include/asm-ppc/io.h linux/include/asm-ppc/io.h --- linux-2.3.paul/include/asm-ppc/io.h Sat Mar 25 08:27:05 2000 +++ linux/include/asm-ppc/io.h Sun Mar 26 09:55:27 2000 @@ -32,7 +32,9 @@ extern unsigned long isa_io_base; extern unsigned long isa_mem_base; extern unsigned long pci_dram_offset; -#define _IO_BASE isa_io_base +/* We're correcting io base addresses in pci fixup code */ +/* #define _IO_BASE isa_io_base */ +#define _IO_BASE 0 #define _ISA_MEM_BASE isa_mem_base #define PCI_DRAM_OFFSET pci_dram_offset #endif /* CONFIG_APUS */ diff -uNr linux-2.3.paul/include/asm-ppc/pci-bridge.h linux/include/asm-ppc/pci-bridge.h --- linux-2.3.paul/include/asm-ppc/pci-bridge.h Mon Oct 25 03:20:01 1999 +++ linux/include/asm-ppc/pci-bridge.h Sun Mar 26 09:31:15 2000 @@ -21,6 +21,7 @@ volatile unsigned int *cfg_addr; volatile unsigned char *cfg_data; void *io_base; + int io_length; int bus_number; int max_bus; struct bridge_data *next; diff -uNr linux-2.3.paul/include/asm-ppc/pci.h linux/include/asm-ppc/pci.h --- linux-2.3.paul/include/asm-ppc/pci.h Sat Mar 25 08:27:05 2000 +++ linux/include/asm-ppc/pci.h Sun Mar 26 17:13:42 2000 @@ -8,7 +8,7 @@ #define pcibios_assign_all_busses() 0 #define PCIBIOS_MIN_IO 0x1000 -#define PCIBIOS_MIN_MEM 0x10000000 +#define PCIBIOS_MIN_MEM 0xc0000000 extern inline void pcibios_set_master(struct pci_dev *dev) {