diff -Nru a/Makefile b/Makefile --- a/Makefile Tue May 7 13:31:16 2002 +++ b/Makefile Tue May 7 13:31:16 2002 @@ -130,6 +130,7 @@ DRIVERS- := DRIVERS-$(CONFIG_ACPI) += drivers/acpi/acpi.o +DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o DRIVERS-y += drivers/base/base.o \ drivers/char/char.o \ @@ -157,7 +158,6 @@ endif DRIVERS-$(CONFIG_SOUND) += sound/sound.o -DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o DRIVERS-$(CONFIG_NET_PCMCIA) += drivers/net/pcmcia/pcmcia_net.o diff -Nru a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c --- a/arch/alpha/kernel/pci.c Tue May 7 13:31:16 2002 +++ b/arch/alpha/kernel/pci.c Tue May 7 13:31:16 2002 @@ -192,13 +192,15 @@ #undef MB #undef GB -void __init +static void __init pcibios_init(void) { if (!alpha_mv.init_pci) return; alpha_mv.init_pci(); } + +subsys_initcall(pcibios_init); char * __init pcibios_setup(char *str) diff -Nru a/arch/i386/Makefile b/arch/i386/Makefile --- a/arch/i386/Makefile Tue May 7 13:31:16 2002 +++ b/arch/i386/Makefile Tue May 7 13:31:16 2002 @@ -98,6 +98,11 @@ DRIVERS += arch/i386/math-emu/math.o endif +ifdef CONFIG_PCI +SUBDIRS += arch/i386/pci +DRIVERS += arch/i386/pci/pci.o +endif + arch/i386/kernel: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile Tue May 7 13:31:16 2002 +++ b/arch/i386/kernel/Makefile Tue May 7 13:31:16 2002 @@ -18,18 +18,8 @@ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ - pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \ + i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \ bootflag.o - - -ifdef CONFIG_PCI -obj-y += pci-i386.o -ifdef CONFIG_VISWS -obj-y += pci-visws.o -else -obj-y += pci-pc.o pci-irq.o -endif -endif obj-$(CONFIG_MCA) += mca.o obj-$(CONFIG_MTRR) += mtrr.o diff -Nru a/arch/i386/kernel/pci-dma.c b/arch/i386/kernel/pci-dma.c --- a/arch/i386/kernel/pci-dma.c Tue May 7 13:31:16 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,37 +0,0 @@ -/* - * Dynamic DMA mapping support. - * - * On i386 there is no hardware dynamic DMA address translation, - * so consistent alloc/free are merely page allocation/freeing. - * The rest of the dynamic DMA mapping interface is implemented - * in asm/pci.h. - */ - -#include -#include -#include -#include -#include - -void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) -{ - void *ret; - int gfp = GFP_ATOMIC; - - if (hwdev == NULL || ((u32)hwdev->dma_mask != 0xffffffff)) - gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); - - if (ret != NULL) { - memset(ret, 0, size); - *dma_handle = virt_to_phys(ret); - } - return ret; -} - -void pci_free_consistent(struct pci_dev *hwdev, size_t size, - void *vaddr, dma_addr_t dma_handle) -{ - free_pages((unsigned long)vaddr, get_order(size)); -} diff -Nru a/arch/i386/kernel/pci-i386.c b/arch/i386/kernel/pci-i386.c --- a/arch/i386/kernel/pci-i386.c Tue May 7 13:31:16 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,384 +0,0 @@ -/* - * Low-Level PCI Access for i386 machines - * - * Copyright 1993, 1994 Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * Drew@Colorado.EDU - * +1 (303) 786-7975 - * - * Drew's work was sponsored by: - * iX Multiuser Multitasking Magazine - * Hannover, Germany - * hm@ix.de - * - * Copyright 1997--2000 Martin Mares - * - * For more information, please consult the following manuals (look at - * http://www.pcisig.com/ for how to get them): - * - * PCI BIOS Specification - * PCI Local Bus Specification - * PCI to PCI Bridge Specification - * PCI System Design Guide - * - * - * CHANGELOG : - * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION - * Revision 2.0 present on 's ASUS mainboard. - * - * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic - * Potter, potter@cao-vlsi.ibp.fr - * - * Jan 10, 1995 : Modified to store the information about configured pci - * devices into a list, which can be accessed via /proc/pci by - * Curtis Varner, cvarner@cs.ucr.edu - * - * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter. - * Alpha version. Intel & UMC chipset support only. - * - * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code - * moved to drivers/pci/pci.c. - * - * Dec 7, 1996 : Added support for direct configuration access of boards - * with Intel compatible access schemes (tsbogend@alpha.franken.de) - * - * Feb 3, 1997 : Set internal functions to static, save/restore flags - * avoid dead locks reading broken PCI BIOS, werner@suse.de - * - * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS - * (mj@atrey.karlin.mff.cuni.cz) - * - * May 7, 1997 : Added some missing cli()'s. [mj] - * - * Jun 20, 1997 : Corrected problems in "conf1" type accesses. - * (paubert@iram.es) - * - * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts - * and cleaned it up... Martin Mares - * - * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj] - * - * May 1, 1998 : Support for peer host bridges. [mj] - * - * Jun 19, 1998 : Changed to use spinlocks, so that PCI configuration space - * can be accessed from interrupts even on SMP systems. [mj] - * - * August 1998 : Better support for peer host bridges and more paranoid - * checks for direct hardware access. Ugh, this file starts to look as - * a large gallery of common hardware bug workarounds (watch the comments) - * -- the PCI specs themselves are sane, but most implementors should be - * hit hard with \hammer scaled \magstep5. [mj] - * - * Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj] - * - * Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj] - * - * August 1999 : New resource management and configuration access stuff. [mj] - * - * Sep 19, 1999 : Use PCI IRQ routing tables for detection of peer host bridges. - * Based on ideas by Chris Frantz and David Hinds. [mj] - * - * Sep 28, 1999 : Handle unreported/unassigned IRQs. Thanks to Shuu Yamaguchi - * for a lot of patience during testing. [mj] - * - * Oct 8, 1999 : Split to pci-i386.c, pci-pc.c and pci-visws.c. [mj] - */ - -#include -#include -#include -#include -#include -#include - -#include "pci-i386.h" - -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; - new |= 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); - } -} - -/* - * 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) -{ - if (res->flags & IORESOURCE_IO) { - unsigned long start = res->start; - - if (start & 0x300) { - start = (start + 0x3ff) & ~0x3ff; - 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->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); - } - } -} - -void __init pcibios_resource_survey(void) -{ - DBG("PCI: Allocating resources\n"); - pcibios_allocate_bus_resources(&pci_root_buses); - pcibios_allocate_resources(0); - pcibios_allocate_resources(1); - pcibios_assign_resources(); -} - -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 (dev->resource[PCI_ROM_RESOURCE].start) - 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; -} - -/* - * If we set up a device for bus mastering, we need to check the latency - * timer as certain crappy BIOSes forget to set it properly. - */ -unsigned int pcibios_max_latency = 255; - -void pcibios_set_master(struct pci_dev *dev) -{ - u8 lat; - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); - if (lat < 16) - lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; - else if (lat > pcibios_max_latency) - lat = pcibios_max_latency; - else - return; - printk("PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); -} - -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long prot; - - /* I/O space cannot be accessed via normal processor loads and - * stores on this platform. - */ - if (mmap_state == pci_mmap_io) - return -EINVAL; - - /* Leave vm_pgoff as-is, the PCI space address is the physical - * address on this platform. - */ - vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO); - - prot = pgprot_val(vma->vm_page_prot); - if (boot_cpu_data.x86 > 3) - prot |= _PAGE_PCD | _PAGE_PWT; - vma->vm_page_prot = __pgprot(prot); - - /* Write-combine setting is ignored, it is changed via the mtrr - * interfaces on this platform. - */ - if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} diff -Nru a/arch/i386/kernel/pci-i386.h b/arch/i386/kernel/pci-i386.h --- a/arch/i386/kernel/pci-i386.h Tue May 7 13:31:16 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,73 +0,0 @@ -/* - * Low-Level PCI Access for i386 machines. - * - * (c) 1999 Martin Mares - */ - -#undef DEBUG - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -#define PCI_PROBE_BIOS 0x0001 -#define PCI_PROBE_CONF1 0x0002 -#define PCI_PROBE_CONF2 0x0004 -#define PCI_NO_SORT 0x0100 -#define PCI_BIOS_SORT 0x0200 -#define PCI_NO_CHECKS 0x0400 -#define PCI_USE_PIRQ_MASK 0x0800 -#define PCI_ASSIGN_ROMS 0x1000 -#define PCI_BIOS_IRQ_SCAN 0x2000 -#define PCI_ASSIGN_ALL_BUSSES 0x4000 -#define PCI_NO_ACPI_ROUTING 0x8000 - -extern unsigned int pci_probe; - -/* pci-i386.c */ - -extern unsigned int pcibios_max_latency; - -void pcibios_resource_survey(void); -int pcibios_enable_resources(struct pci_dev *); - -/* pci-pc.c */ - -extern int pcibios_last_bus; -extern struct pci_bus *pci_root_bus; -extern struct pci_ops *pci_root_ops; - -/* pci-irq.c */ - -struct irq_info { - u8 bus, devfn; /* Bus, device and function */ - struct { - u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ - u16 bitmap; /* Available IRQs */ - } __attribute__((packed)) irq[4]; - u8 slot; /* Slot number, 0=onboard */ - u8 rfu; -} __attribute__((packed)); - -struct irq_routing_table { - u32 signature; /* PIRQ_SIGNATURE should be here */ - u16 version; /* PIRQ_VERSION */ - u16 size; /* Table size in bytes */ - u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ - u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ - u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ - u32 miniport_data; /* Crap */ - u8 rfu[11]; - u8 checksum; /* Modulo 256 checksum must give zero */ - struct irq_info slots[0]; -} __attribute__((packed)); - -extern unsigned int pcibios_irq_mask; - -extern int pci_use_acpi_routing; - -void pcibios_irq_init(void); -void pcibios_fixup_irqs(void); -void pcibios_enable_irq(struct pci_dev *dev); diff -Nru a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c --- a/arch/i386/kernel/pci-irq.c Tue May 7 13:31:16 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,866 +0,0 @@ -/* - * Low-Level PCI Support for PC -- Routing of Interrupts - * - * (c) 1999--2000 Martin Mares - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pci-i386.h" - -#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) -#define PIRQ_VERSION 0x0100 - -int pci_use_acpi_routing = 0; -int broken_hp_bios_irq9; - -static struct irq_routing_table *pirq_table; - -/* - * Never use: 0, 1, 2 (timer, keyboard, and cascade) - * Avoid using: 13, 14 and 15 (FP error and IDE). - * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) - */ -unsigned int pcibios_irq_mask = 0xfff8; - -static int pirq_penalty[16] = { - 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, - 0, 0, 0, 0, 1000, 100000, 100000, 100000 -}; - -struct irq_router { - char *name; - u16 vendor, device; - int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); - int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new); -}; - -/* - * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. - */ - -static struct irq_routing_table * __init pirq_find_routing_table(void) -{ - u8 *addr; - struct irq_routing_table *rt; - int i; - u8 sum; - - for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) { - rt = (struct irq_routing_table *) addr; - if (rt->signature != PIRQ_SIGNATURE || - rt->version != PIRQ_VERSION || - rt->size % 16 || - rt->size < sizeof(struct irq_routing_table)) - continue; - sum = 0; - for(i=0; isize; i++) - sum += addr[i]; - if (!sum) { - DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); - return rt; - } - } - return NULL; -} - -/* - * If we have a IRQ routing table, use it to search for peer host - * bridges. It's a gross hack, but since there are no other known - * ways how to get a list of buses, we have to go this way. - */ - -static void __init pirq_peer_trick(void) -{ - struct irq_routing_table *rt = pirq_table; - u8 busmap[256]; - int i; - struct irq_info *e; - - memset(busmap, 0, sizeof(busmap)); - for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) { - e = &rt->slots[i]; -#ifdef DEBUG - { - int j; - DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot); - for(j=0; j<4; j++) - DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap); - DBG("\n"); - } -#endif - busmap[e->bus] = 1; - } - for(i=1; i<256; i++) - /* - * It might be a secondary bus, but in this case its parent is already - * known (ascending bus order) and therefore pci_scan_bus returns immediately. - */ - if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL)) - printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i); - pcibios_last_bus = -1; -} - -/* - * Code for querying and setting of IRQ routes on various interrupt routers. - */ - -static void eisa_set_level_irq(unsigned int irq) -{ - unsigned char mask = 1 << (irq & 7); - unsigned int port = 0x4d0 + (irq >> 3); - unsigned char val = inb(port); - - if (!(val & mask)) { - DBG(" -> edge"); - outb(val | mask, port); - } -} - -/* - * Common IRQ routing practice: nybbles in config space, - * offset by some magic constant. - */ -static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr) -{ - u8 x; - unsigned reg = offset + (nr >> 1); - - pci_read_config_byte(router, reg, &x); - return (nr & 1) ? (x >> 4) : (x & 0xf); -} - -static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val) -{ - u8 x; - unsigned reg = offset + (nr >> 1); - - pci_read_config_byte(router, reg, &x); - x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val); - pci_write_config_byte(router, reg, x); -} - -/* - * ALI pirq entries are damn ugly, and completely undocumented. - * This has been figured out from pirq tables, and it's not a pretty - * picture. - */ -static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; - - return irqmap[read_config_nybble(router, 0x48, pirq-1)]; -} - -static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; - unsigned int val = irqmap[irq]; - - if (val) { - write_config_nybble(router, 0x48, pirq-1, val); - return 1; - } - return 0; -} - -/* - * The Intel PIIX4 pirq rules are fairly simple: "pirq" is - * just a pointer to the config space. - */ -static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - u8 x; - - pci_read_config_byte(router, pirq, &x); - return (x < 16) ? x : 0; -} - -static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - pci_write_config_byte(router, pirq, irq); - return 1; -} - -/* - * The VIA pirq rules are nibble-based, like ALI, - * but without the ugly irq number munging. - */ -static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - return read_config_nybble(router, 0x55, pirq); -} - -static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - write_config_nybble(router, 0x55, pirq, irq); - return 1; -} - -/* - * ITE 8330G pirq rules are nibble-based - * FIXME: pirqmap may be { 1, 0, 3, 2 }, - * 2+3 are both mapped to irq 9 on my system - */ -static int pirq_ite_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - static unsigned char pirqmap[4] = { 1, 0, 2, 3 }; - return read_config_nybble(router,0x43, pirqmap[pirq-1]); -} - -static int pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - static unsigned char pirqmap[4] = { 1, 0, 2, 3 }; - write_config_nybble(router, 0x43, pirqmap[pirq-1], irq); - return 1; -} - -/* - * OPTI: high four bits are nibble pointer.. - * I wonder what the low bits do? - */ -static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - return read_config_nybble(router, 0xb8, pirq >> 4); -} - -static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - write_config_nybble(router, 0xb8, pirq >> 4, irq); - return 1; -} - -/* - * Cyrix: nibble offset 0x5C - */ -static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - return read_config_nybble(router, 0x5C, (pirq-1)^1); -} - -static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - write_config_nybble(router, 0x5C, (pirq-1)^1, irq); - return 1; -} - -/* - * PIRQ routing for SiS 85C503 router used in several SiS chipsets - * According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997) - * the related registers work as follows: - * - * general: one byte per re-routable IRQ, - * bit 7 IRQ mapping enabled (0) or disabled (1) - * bits [6:4] reserved - * bits [3:0] IRQ to map to - * allowed: 3-7, 9-12, 14-15 - * reserved: 0, 1, 2, 8, 13 - * - * individual registers in device config space: - * - * 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case - * - * 0x61: IDEIRQ: bits as in general case - but: - * bits [6:5] must be written 01 - * bit 4 channel-select primary (0), secondary (1) - * - * 0x62: USBIRQ: bits as in general case - but: - * bit 4 OHCI function disabled (0), enabled (1) - * - * 0x6a: ACPI/SCI IRQ - bits as in general case - * - * 0x7e: Data Acq. Module IRQ - bits as in general case - * - * Apparently there are systems implementing PCI routing table using both - * link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets - * like 0x62 as link values for USBIRQ e.g. So there is no simple - * "register = offset + pirq" relation. - * Currently we support PCI INTA..D and USBIRQ and try our best to handle - * both link mappings. - * IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS). - */ - -static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - u8 x; - int reg = pirq; - - switch(pirq) { - case 0x01: - case 0x02: - case 0x03: - case 0x04: - reg += 0x40; - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x62: - pci_read_config_byte(router, reg, &x); - if (reg != 0x62) - break; - if (!(x & 0x40)) - return 0; - break; - case 0x61: - case 0x6a: - case 0x7e: - printk(KERN_INFO "SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n"); - return 0; - default: - printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); - return 0; - } - return (x & 0x80) ? 0 : (x & 0x0f); -} - -static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - u8 x; - int reg = pirq; - - switch(pirq) { - case 0x01: - case 0x02: - case 0x03: - case 0x04: - reg += 0x40; - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x62: - x = (irq&0x0f) ? (irq&0x0f) : 0x80; - if (reg != 0x62) - break; - /* always mark OHCI enabled, as nothing else knows about this */ - x |= 0x40; - break; - case 0x61: - case 0x6a: - case 0x7e: - printk(KERN_INFO "advanced SiS pirq mapping not yet implemented\n"); - return 0; - default: - printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); - return 0; - } - pci_write_config_byte(router, reg, x); - - return 1; -} - -/* - * VLSI: nibble offset 0x74 - educated guess due to routing table and - * config space of VLSI 82C534 PCI-bridge/router (1004:0102) - * Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard - * devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6 - * for the busbridge to the docking station. - */ - -static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - if (pirq > 8) { - printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); - return 0; - } - return read_config_nybble(router, 0x74, pirq-1); -} - -static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - if (pirq > 8) { - printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); - return 0; - } - write_config_nybble(router, 0x74, pirq-1, irq); - return 1; -} - -/* - * ServerWorks: PCI interrupts mapped to system IRQ lines through Index - * and Redirect I/O registers (0x0c00 and 0x0c01). The Index register - * format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a. The Redirect - * register is a straight binary coding of desired PIC IRQ (low nibble). - * - * The 'link' value in the PIRQ table is already in the correct format - * for the Index register. There are some special index values: - * 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1, - * and 0x03 for SMBus. - */ -static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - outb_p(pirq, 0xc00); - return inb(0xc01) & 0xf; -} - -static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - outb_p(pirq, 0xc00); - outb_p(irq, 0xc01); - return 1; -} - -/* Support for AMD756 PCI IRQ Routing - * Jhon H. Caicedo - * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced) - * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced) - * The AMD756 pirq rules are nibble-based - * offset 0x56 0-3 PIRQA 4-7 PIRQB - * offset 0x57 0-3 PIRQC 4-7 PIRQD - */ -static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq) -{ - u8 irq; - irq = 0; - if (pirq <= 4) - { - irq = read_config_nybble(router, 0x56, pirq - 1); - } - printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n", - dev->vendor, dev->device, pirq, irq); - return irq; -} - -static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", - dev->vendor, dev->device, pirq, irq); - if (pirq <= 4) - { - write_config_nybble(router, 0x56, pirq - 1, irq); - } - return 1; -} - -#ifdef CONFIG_PCI_BIOS - -static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) -{ - struct pci_dev *bridge; - int pin = pci_get_interrupt_pin(dev, &bridge); - return pcibios_set_irq_routing(bridge, pin, irq); -} - -static struct irq_router pirq_bios_router = - { "BIOS", 0, 0, NULL, pirq_bios_set }; - -#endif - -static struct irq_router pirq_routers[] = { - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, pirq_piix_get, pirq_piix_set }, - { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, pirq_piix_get, pirq_piix_set }, - - { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, - - { "ITE", PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8330G_0, pirq_ite_get, pirq_ite_set }, - - { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, - { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, - { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set }, - - { "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set }, - - { "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set }, - { "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set }, - { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set }, - { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, - pirq_serverworks_get, pirq_serverworks_set }, - { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, - pirq_serverworks_get, pirq_serverworks_set }, - { "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B, - pirq_amd756_get, pirq_amd756_set }, - - { "default", 0, 0, NULL, NULL } -}; - -static struct irq_router *pirq_router; -static struct pci_dev *pirq_router_dev; - -static void __init pirq_find_router(void) -{ - struct irq_routing_table *rt = pirq_table; - struct irq_router *r; - -#ifdef CONFIG_PCI_BIOS - if (!rt->signature) { - printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n"); - pirq_router = &pirq_bios_router; - return; - } -#endif - - DBG("PCI: Attempting to find IRQ router for %04x:%04x\n", - rt->rtr_vendor, rt->rtr_device); - - /* fall back to default router if nothing else found */ - pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1]; - - pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn); - if (!pirq_router_dev) { - DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn); - return; - } - - for(r=pirq_routers; r->vendor; r++) { - /* Exact match against router table entry? Use it! */ - if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) { - pirq_router = r; - break; - } - /* Match against router device entry? Use it as a fallback */ - if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) { - pirq_router = r; - } - } - printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", - pirq_router->name, - pirq_router_dev->vendor, - pirq_router_dev->device, - pirq_router_dev->slot_name); -} - -static struct irq_info *pirq_get_info(struct pci_dev *dev) -{ - struct irq_routing_table *rt = pirq_table; - int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); - struct irq_info *info; - - for (info = rt->slots; entries--; info++) - if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn)) - return info; - return NULL; -} - -static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs) -{ -} - -#ifdef CONFIG_ACPI_PCI - -static int acpi_lookup_irq ( - struct pci_dev *dev, - u8 pin, - int assign) -{ - int result = 0; - int irq = 0; - - /* TBD: Select IRQ from possible to improve routing performance. */ - - result = acpi_prt_get_irq(dev, pin, &irq); - if (!irq) - result = -ENODEV; - if (0 != result) { - printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s\n", - 'A'+pin, dev->slot_name); - return result; - } - - dev->irq = irq; - - if (!assign) { - /* only check for the IRQ */ - printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", irq, - dev->slot_name); - return 1; - } - - /* also assign an IRQ */ - if (irq && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { - result = acpi_prt_set_irq(dev, pin, irq); - if (0 != result) { - printk(KERN_WARNING "PCI: Could not assign IRQ %d to device %s\n", irq, dev->slot_name); - return result; - } - - eisa_set_level_irq(irq); - - printk(KERN_INFO "PCI: Assigned IRQ %d for device %s\n", irq, dev->slot_name); - } - - return 1; -} - -#endif /* CONFIG_ACPI_PCI */ - -static int pcibios_lookup_irq(struct pci_dev *dev, int assign) -{ - u8 pin; - struct irq_info *info; - int i, pirq, newirq; - int irq = 0; - u32 mask; - struct irq_router *r = pirq_router; - struct pci_dev *dev2; - char *msg = NULL; - - /* Find IRQ pin */ - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - if (!pin) { - DBG(" -> no interrupt pin\n"); - return 0; - } - pin = pin - 1; - -#ifdef CONFIG_ACPI_PCI - /* Use ACPI to lookup IRQ */ - if (pci_use_acpi_routing) - return acpi_lookup_irq(dev, pin, assign); -#endif - - /* Find IRQ routing entry */ - - if (!pirq_table) - return 0; - - DBG("IRQ for %s:%d", dev->slot_name, pin); - info = pirq_get_info(dev); - if (!info) { - DBG(" -> not found in routing table\n"); - return 0; - } - pirq = info->irq[pin].link; - mask = info->irq[pin].bitmap; - if (!pirq) { - DBG(" -> not routed\n"); - return 0; - } - DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); - mask &= pcibios_irq_mask; - - /* Work around broken HP Pavilion Notebooks which assign USB to - IRQ 9 even though it is actually wired to IRQ 11 */ - - if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) { - dev->irq = 11; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11); - r->set(pirq_router_dev, dev, pirq, 11); - } - - /* - * Find the best IRQ to assign: use the one - * reported by the device if possible. - */ - newirq = dev->irq; - if (!((1 << newirq) & mask)) { - if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0; - else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, dev->slot_name); - } - if (!newirq && assign) { - for (i = 0; i < 16; i++) { - if (!(mask & (1 << i))) - continue; - if (pirq_penalty[i] < pirq_penalty[newirq] && - !request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) { - free_irq(i, dev); - newirq = i; - } - } - } - DBG(" -> newirq=%d", newirq); - - /* Check if it is hardcoded */ - if ((pirq & 0xf0) == 0xf0) { - irq = pirq & 0xf; - DBG(" -> hardcoded IRQ %d\n", irq); - msg = "Hardcoded"; - } else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \ - ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) { - DBG(" -> got IRQ %d\n", irq); - msg = "Found"; - } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { - DBG(" -> assigning IRQ %d", newirq); - if (r->set(pirq_router_dev, dev, pirq, newirq)) { - eisa_set_level_irq(newirq); - DBG(" ... OK\n"); - msg = "Assigned"; - irq = newirq; - } - } - - if (!irq) { - DBG(" ... failed\n"); - if (newirq && mask == (1 << newirq)) { - msg = "Guessed"; - irq = newirq; - } else - return 0; - } - printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name); - - /* Update IRQ for all devices with the same pirq value */ - pci_for_each_dev(dev2) { - pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin); - if (!pin) - continue; - pin--; - info = pirq_get_info(dev2); - if (!info) - continue; - if (info->irq[pin].link == pirq) { - /* We refuse to override the dev->irq information. Give a warning! */ - if ( dev2->irq && dev2->irq != irq && \ - (!(pci_probe & PCI_USE_PIRQ_MASK) || \ - ((1 << dev2->irq) & mask)) ) { - printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n", - dev2->slot_name, dev2->irq, irq); - continue; - } - dev2->irq = irq; - pirq_penalty[irq]++; - if (dev != dev2) - printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name); - } - } - return 1; -} - -void __init pcibios_irq_init(void) -{ - DBG("PCI: IRQ init\n"); - -#ifdef CONFIG_ACPI_PCI - if (!(pci_probe & PCI_NO_ACPI_ROUTING)) { - if (acpi_prts.count) { - printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); - pci_use_acpi_routing = 1; - return; - } - else - printk(KERN_WARNING "PCI: Invalid ACPI-PCI IRQ routing table\n"); - } -#endif - - pirq_table = pirq_find_routing_table(); - -#ifdef CONFIG_PCI_BIOS - if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN)) - pirq_table = pcibios_get_irq_routing_table(); -#endif - if (pirq_table) { - pirq_peer_trick(); - pirq_find_router(); - if (pirq_table->exclusive_irqs) { - int i; - for (i=0; i<16; i++) - if (!(pirq_table->exclusive_irqs & (1 << i))) - pirq_penalty[i] += 100; - } - /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */ - if (io_apic_assign_pci_irqs) - pirq_table = NULL; - } -} - -void __init pcibios_fixup_irqs(void) -{ - struct pci_dev *dev; - u8 pin; - - DBG("PCI: IRQ fixup\n"); - pci_for_each_dev(dev) { - /* - * If the BIOS has set an out of range IRQ number, just ignore it. - * Also keep track of which IRQ's are already in use. - */ - if (dev->irq >= 16) { - DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq); - dev->irq = 0; - } - /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ - if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000) - pirq_penalty[dev->irq] = 0; - pirq_penalty[dev->irq]++; - } - - pci_for_each_dev(dev) { - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); -#ifdef CONFIG_X86_IO_APIC - /* - * Recalculate IRQ numbers if we use the I/O APIC. - */ - if (io_apic_assign_pci_irqs) - { - int irq; - - if (pin) { - pin--; /* interrupt pins are numbered starting from 1 */ - irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); - /* - * Busses behind bridges are typically not listed in the MP-table. - * In this case we have to look up the IRQ based on the parent bus, - * parent slot, and pin number. The SMP code detects such bridged - * busses itself so we should get into this branch reliably. - */ - if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ - struct pci_dev * bridge = dev->bus->self; - - pin = (pin + PCI_SLOT(dev->devfn)) % 4; - irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, - PCI_SLOT(bridge->devfn), pin); - if (irq >= 0) - printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", - bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); - } - if (irq >= 0) { - printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", - dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); - dev->irq = irq; - } - } - } -#endif - /* - * Still no IRQ? Try to lookup one... - */ - if (pin && !dev->irq) - pcibios_lookup_irq(dev, 0); - } -} - -void pcibios_penalize_isa_irq(int irq) -{ - /* - * If any ISAPnP device reports an IRQ in its list of possible - * IRQ's, we try to avoid assigning it to PCI devices. - */ - pirq_penalty[irq] += 100; -} - -void pcibios_enable_irq(struct pci_dev *dev) -{ - u8 pin; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { - char *msg; - if (io_apic_assign_pci_irqs) - msg = " Probably buggy MP table."; - else if (pci_probe & PCI_BIOS_IRQ_SCAN) - msg = ""; - else - msg = " Please try using pci=biosirq."; - printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", - 'A' + pin - 1, dev->slot_name, msg); - } -} diff -Nru a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c --- a/arch/i386/kernel/pci-pc.c Tue May 7 13:31:16 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1423 +0,0 @@ -/* - * Low-Level PCI Support for PC - * - * (c) 1999--2000 Martin Mares - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "pci-i386.h" - -unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; - -int pcibios_last_bus = -1; -struct pci_bus *pci_root_bus = NULL; -struct pci_ops *pci_root_ops = NULL; - -int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value) = NULL; -int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value) = NULL; - -#ifdef CONFIG_MULTIQUAD -#define BUS2QUAD(global) (mp_bus_id_to_node[global]) -#define BUS2LOCAL(global) (mp_bus_id_to_local[global]) -#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) -#else -#define BUS2QUAD(global) (0) -#define BUS2LOCAL(global) (global) -#define QUADLOCAL2BUS(quad,local) (local) -#endif - -/* - * This interrupt-safe spinlock protects all accesses to PCI - * configuration space. - */ -static spinlock_t pci_config_lock = SPIN_LOCK_UNLOCKED; - - -/* - * Functions for accessing PCI configuration space with type 1 accesses - */ - -#ifdef CONFIG_PCI_DIRECT - -#ifdef CONFIG_MULTIQUAD -#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ - (0x80000000 | (BUS2LOCAL(bus) << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) - -static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) /* CONFIG_MULTIQUAD */ -{ - unsigned long flags; - - if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - spin_lock_irqsave(&pci_config_lock, flags); - - outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); - - switch (len) { - case 1: - *value = inb_quad(0xCFC + (reg & 3), BUS2QUAD(bus)); - break; - case 2: - *value = inw_quad(0xCFC + (reg & 2), BUS2QUAD(bus)); - break; - case 4: - *value = inl_quad(0xCFC, BUS2QUAD(bus)); - break; - } - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return 0; -} - -static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) /* CONFIG_MULTIQUAD */ -{ - unsigned long flags; - - if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - spin_lock_irqsave(&pci_config_lock, flags); - - outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); - - switch (len) { - case 1: - outb_quad((u8)value, 0xCFC + (reg & 3), BUS2QUAD(bus)); - break; - case 2: - outw_quad((u16)value, 0xCFC + (reg & 2), BUS2QUAD(bus)); - break; - case 4: - outl_quad((u32)value, 0xCFC, BUS2QUAD(bus)); - break; - } - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return 0; -} - -#else /* !CONFIG_MULTIQUAD */ -#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ - (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) - -static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) /* !CONFIG_MULTIQUAD */ -{ - unsigned long flags; - - if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - spin_lock_irqsave(&pci_config_lock, flags); - - outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); - - switch (len) { - case 1: - *value = inb(0xCFC + (reg & 3)); - break; - case 2: - *value = inw(0xCFC + (reg & 2)); - break; - case 4: - *value = inl(0xCFC); - break; - } - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return 0; -} - -static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) /* !CONFIG_MULTIQUAD */ -{ - unsigned long flags; - - if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - spin_lock_irqsave(&pci_config_lock, flags); - - outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); - - switch (len) { - case 1: - outb((u8)value, 0xCFC + (reg & 3)); - break; - case 2: - outw((u16)value, 0xCFC + (reg & 2)); - break; - case 4: - outl((u32)value, 0xCFC); - break; - } - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return 0; -} - -#endif /* CONFIG_MULTIQUAD */ - -#undef PCI_CONF1_ADDRESS - -static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) -{ - int result; - u32 data; - - if (!value) - return -EINVAL; - - result = pci_conf1_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 1, &data); - - *value = (u8)data; - - return result; -} - -static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value) -{ - int result; - u32 data; - - if (!value) - return -EINVAL; - - result = pci_conf1_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 2, &data); - - *value = (u16)data; - - return result; -} - -static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value) -{ - if (!value) - return -EINVAL; - - return pci_conf1_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 4, value); -} - -static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value) -{ - return pci_conf1_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 1, value); -} - -static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value) -{ - return pci_conf1_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 2, value); -} - -static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value) -{ - return pci_conf1_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 4, value); -} - -static struct pci_ops pci_direct_conf1 = { - pci_conf1_read_config_byte, - pci_conf1_read_config_word, - pci_conf1_read_config_dword, - pci_conf1_write_config_byte, - pci_conf1_write_config_word, - pci_conf1_write_config_dword -}; - - -/* - * Functions for accessing PCI configuration space with type 2 accesses - */ - -#define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg) - -static int pci_conf2_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) -{ - unsigned long flags; - - if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - if (dev & 0x10) - return PCIBIOS_DEVICE_NOT_FOUND; - - spin_lock_irqsave(&pci_config_lock, flags); - - outb((u8)(0xF0 | (fn << 1)), 0xCF8); - outb((u8)bus, 0xCFA); - - switch (len) { - case 1: - *value = inb(PCI_CONF2_ADDRESS(dev, reg)); - break; - case 2: - *value = inw(PCI_CONF2_ADDRESS(dev, reg)); - break; - case 4: - *value = inl(PCI_CONF2_ADDRESS(dev, reg)); - break; - } - - outb (0, 0xCF8); - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return 0; -} - -static int pci_conf2_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) -{ - unsigned long flags; - - if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - if (dev & 0x10) - return PCIBIOS_DEVICE_NOT_FOUND; - - spin_lock_irqsave(&pci_config_lock, flags); - - outb((u8)(0xF0 | (fn << 1)), 0xCF8); - outb((u8)bus, 0xCFA); - - switch (len) { - case 1: - outb ((u8)value, PCI_CONF2_ADDRESS(dev, reg)); - break; - case 2: - outw ((u16)value, PCI_CONF2_ADDRESS(dev, reg)); - break; - case 4: - outl ((u32)value, PCI_CONF2_ADDRESS(dev, reg)); - break; - } - - outb (0, 0xCF8); - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return 0; -} - -#undef PCI_CONF2_ADDRESS - -static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value) -{ - int result; - u32 data; - result = pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 1, &data); - *value = (u8)data; - return result; -} - -static int pci_conf2_read_config_word(struct pci_dev *dev, int where, u16 *value) -{ - int result; - u32 data; - result = pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 2, &data); - *value = (u16)data; - return result; -} - -static int pci_conf2_read_config_dword(struct pci_dev *dev, int where, u32 *value) -{ - return pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 4, value); -} - -static int pci_conf2_write_config_byte(struct pci_dev *dev, int where, u8 value) -{ - return pci_conf2_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 1, value); -} - -static int pci_conf2_write_config_word(struct pci_dev *dev, int where, u16 value) -{ - return pci_conf2_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 2, value); -} - -static int pci_conf2_write_config_dword(struct pci_dev *dev, int where, u32 value) -{ - return pci_conf2_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 4, value); -} - -static struct pci_ops pci_direct_conf2 = { - pci_conf2_read_config_byte, - pci_conf2_read_config_word, - pci_conf2_read_config_dword, - pci_conf2_write_config_byte, - pci_conf2_write_config_word, - pci_conf2_write_config_dword -}; - - -/* - * Before we decide to use direct hardware access mechanisms, we try to do some - * trivial checks to ensure it at least _seems_ to be working -- we just test - * whether bus 00 contains a host bridge (this is similar to checking - * techniques used in XFree86, but ours should be more reliable since we - * attempt to make use of direct access hints provided by the PCI BIOS). - * - * This should be close to trivial, but it isn't, because there are buggy - * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. - */ -static int __devinit pci_sanity_check(struct pci_ops *o) -{ - u16 x; - struct pci_bus bus; /* Fake bus and device */ - struct pci_dev dev; - - if (pci_probe & PCI_NO_CHECKS) - return 1; - bus.number = 0; - dev.bus = &bus; - for(dev.devfn=0; dev.devfn < 0x100; dev.devfn++) - if ((!o->read_word(&dev, PCI_CLASS_DEVICE, &x) && - (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || - (!o->read_word(&dev, PCI_VENDOR_ID, &x) && - (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) - return 1; - DBG("PCI: Sanity check failed\n"); - return 0; -} - -static struct pci_ops * __devinit pci_check_direct(void) -{ - unsigned int tmp; - unsigned long flags; - - __save_flags(flags); __cli(); - - /* - * Check if configuration type 1 works. - */ - if (pci_probe & PCI_PROBE_CONF1) { - outb (0x01, 0xCFB); - tmp = inl (0xCF8); - outl (0x80000000, 0xCF8); - if (inl (0xCF8) == 0x80000000 && - pci_sanity_check(&pci_direct_conf1)) { - outl (tmp, 0xCF8); - __restore_flags(flags); - printk(KERN_INFO "PCI: Using configuration type 1\n"); - if (!request_region(0xCF8, 8, "PCI conf1")) - return NULL; - return &pci_direct_conf1; - } - outl (tmp, 0xCF8); - } - - /* - * Check if configuration type 2 works. - */ - if (pci_probe & PCI_PROBE_CONF2) { - outb (0x00, 0xCFB); - outb (0x00, 0xCF8); - outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 && - pci_sanity_check(&pci_direct_conf2)) { - __restore_flags(flags); - printk(KERN_INFO "PCI: Using configuration type 2\n"); - if (!request_region(0xCF8, 4, "PCI conf2")) - return NULL; - return &pci_direct_conf2; - } - } - - __restore_flags(flags); - return NULL; -} - -#endif - -/* - * BIOS32 and PCI BIOS handling. - */ - -#ifdef CONFIG_PCI_BIOS - -#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX -#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 -#define PCIBIOS_FIND_PCI_DEVICE 0xb102 -#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 -#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 -#define PCIBIOS_READ_CONFIG_BYTE 0xb108 -#define PCIBIOS_READ_CONFIG_WORD 0xb109 -#define PCIBIOS_READ_CONFIG_DWORD 0xb10a -#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b -#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c -#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d -#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e -#define PCIBIOS_SET_PCI_HW_INT 0xb10f - -/* BIOS32 signature: "_32_" */ -#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) - -/* PCI signature: "PCI " */ -#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) - -/* PCI service signature: "$PCI" */ -#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) - -/* PCI BIOS hardware mechanism flags */ -#define PCIBIOS_HW_TYPE1 0x01 -#define PCIBIOS_HW_TYPE2 0x02 -#define PCIBIOS_HW_TYPE1_SPEC 0x10 -#define PCIBIOS_HW_TYPE2_SPEC 0x20 - -/* - * This is the standard structure used to identify the entry point - * to the BIOS32 Service Directory, as documented in - * Standard BIOS 32-bit Service Directory Proposal - * Revision 0.4 May 24, 1993 - * Phoenix Technologies Ltd. - * Norwood, MA - * and the PCI BIOS specification. - */ - -union bios32 { - struct { - unsigned long signature; /* _32_ */ - unsigned long entry; /* 32 bit physical address */ - unsigned char revision; /* Revision level, 0 */ - unsigned char length; /* Length in paragraphs should be 01 */ - unsigned char checksum; /* All bytes must add up to zero */ - unsigned char reserved[5]; /* Must be zero */ - } fields; - char chars[16]; -}; - -/* - * Physical address of the service directory. I don't know if we're - * allowed to have more than one of these or not, so just in case - * we'll make pcibios_present() take a memory start parameter and store - * the array there. - */ - -static struct { - unsigned long address; - unsigned short segment; -} bios32_indirect = { 0, __KERNEL_CS }; - -/* - * Returns the entry point for the given service, NULL on error - */ - -static unsigned long bios32_service(unsigned long service) -{ - unsigned char return_code; /* %al */ - unsigned long address; /* %ebx */ - unsigned long length; /* %ecx */ - unsigned long entry; /* %edx */ - unsigned long flags; - - __save_flags(flags); __cli(); - __asm__("lcall *(%%edi); cld" - : "=a" (return_code), - "=b" (address), - "=c" (length), - "=d" (entry) - : "0" (service), - "1" (0), - "D" (&bios32_indirect)); - __restore_flags(flags); - - switch (return_code) { - case 0: - return address + entry; - case 0x80: /* Not present */ - printk(KERN_WARNING "bios32_service(0x%lx): not present\n", service); - return 0; - default: /* Shouldn't happen */ - printk(KERN_WARNING "bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n", - service, return_code); - return 0; - } -} - -static struct { - unsigned long address; - unsigned short segment; -} pci_indirect = { 0, __KERNEL_CS }; - -static int pci_bios_present; - -static int __devinit check_pcibios(void) -{ - u32 signature, eax, ebx, ecx; - u8 status, major_ver, minor_ver, hw_mech; - unsigned long flags, pcibios_entry; - - if ((pcibios_entry = bios32_service(PCI_SERVICE))) { - pci_indirect.address = pcibios_entry + PAGE_OFFSET; - - __save_flags(flags); __cli(); - __asm__( - "lcall *(%%edi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=d" (signature), - "=a" (eax), - "=b" (ebx), - "=c" (ecx) - : "1" (PCIBIOS_PCI_BIOS_PRESENT), - "D" (&pci_indirect) - : "memory"); - __restore_flags(flags); - - status = (eax >> 8) & 0xff; - hw_mech = eax & 0xff; - major_ver = (ebx >> 8) & 0xff; - minor_ver = ebx & 0xff; - if (pcibios_last_bus < 0) - pcibios_last_bus = ecx & 0xff; - DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n", - status, hw_mech, major_ver, minor_ver, pcibios_last_bus); - if (status || signature != PCI_SIGNATURE) { - printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found\n", - status, signature); - return 0; - } - printk(KERN_INFO "PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n", - major_ver, minor_ver, pcibios_entry, pcibios_last_bus); -#ifdef CONFIG_PCI_DIRECT - if (!(hw_mech & PCIBIOS_HW_TYPE1)) - pci_probe &= ~PCI_PROBE_CONF1; - if (!(hw_mech & PCIBIOS_HW_TYPE2)) - pci_probe &= ~PCI_PROBE_CONF2; -#endif - return 1; - } - return 0; -} - -static int __devinit pci_bios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, unsigned char *device_fn) -{ - unsigned short bx; - unsigned short ret; - - __asm__("lcall *(%%edi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=b" (bx), - "=a" (ret) - : "1" (PCIBIOS_FIND_PCI_DEVICE), - "c" (device_id), - "d" (vendor), - "S" ((int) index), - "D" (&pci_indirect)); - *bus = (bx >> 8) & 0xff; - *device_fn = bx & 0xff; - return (int) (ret & 0xff00) >> 8; -} - -static int pci_bios_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) -{ - unsigned long result = 0; - unsigned long flags; - unsigned long bx = ((bus << 8) | (dev << 3) | fn); - - if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - spin_lock_irqsave(&pci_config_lock, flags); - - switch (len) { - case 1: - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=c" (*value), - "=a" (result) - : "1" (PCIBIOS_READ_CONFIG_BYTE), - "b" (bx), - "D" ((long)reg), - "S" (&pci_indirect)); - break; - case 2: - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=c" (*value), - "=a" (result) - : "1" (PCIBIOS_READ_CONFIG_WORD), - "b" (bx), - "D" ((long)reg), - "S" (&pci_indirect)); - break; - case 4: - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=c" (*value), - "=a" (result) - : "1" (PCIBIOS_READ_CONFIG_DWORD), - "b" (bx), - "D" ((long)reg), - "S" (&pci_indirect)); - break; - } - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return (int)((result & 0xff00) >> 8); -} - -static int pci_bios_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) -{ - unsigned long result = 0; - unsigned long flags; - unsigned long bx = ((bus << 8) | (dev << 3) | fn); - - if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) - return -EINVAL; - - spin_lock_irqsave(&pci_config_lock, flags); - - switch (len) { - case 1: - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (result) - : "0" (PCIBIOS_WRITE_CONFIG_BYTE), - "c" (value), - "b" (bx), - "D" ((long)reg), - "S" (&pci_indirect)); - break; - case 2: - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (result) - : "0" (PCIBIOS_WRITE_CONFIG_WORD), - "c" (value), - "b" (bx), - "D" ((long)reg), - "S" (&pci_indirect)); - break; - case 4: - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (result) - : "0" (PCIBIOS_WRITE_CONFIG_DWORD), - "c" (value), - "b" (bx), - "D" ((long)reg), - "S" (&pci_indirect)); - break; - } - - spin_unlock_irqrestore(&pci_config_lock, flags); - - return (int)((result & 0xff00) >> 8); -} - -static int pci_bios_read_config_byte(struct pci_dev *dev, int where, u8 *value) -{ - int result; - u32 data; - - if (!value) - return -EINVAL; - - result = pci_bios_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 1, &data); - - *value = (u8)data; - - return result; -} - -static int pci_bios_read_config_word(struct pci_dev *dev, int where, u16 *value) -{ - int result; - u32 data; - - if (!value) - return -EINVAL; - - result = pci_bios_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 2, &data); - - *value = (u16)data; - - return result; -} - -static int pci_bios_read_config_dword(struct pci_dev *dev, int where, u32 *value) -{ - if (!value) - return -EINVAL; - - return pci_bios_read(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 4, value); -} - -static int pci_bios_write_config_byte(struct pci_dev *dev, int where, u8 value) -{ - return pci_bios_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 1, value); -} - -static int pci_bios_write_config_word(struct pci_dev *dev, int where, u16 value) -{ - return pci_bios_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 2, value); -} - -static int pci_bios_write_config_dword(struct pci_dev *dev, int where, u32 value) -{ - return pci_bios_write(0, dev->bus->number, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), where, 4, value); -} - - -/* - * Function table for BIOS32 access - */ - -static struct pci_ops pci_bios_access = { - pci_bios_read_config_byte, - pci_bios_read_config_word, - pci_bios_read_config_dword, - pci_bios_write_config_byte, - pci_bios_write_config_word, - pci_bios_write_config_dword -}; - -/* - * Try to find PCI BIOS. - */ - -static struct pci_ops * __devinit pci_find_bios(void) -{ - union bios32 *check; - unsigned char sum; - int i, length; - - /* - * Follow the standard procedure for locating the BIOS32 Service - * directory by scanning the permissible address range from - * 0xe0000 through 0xfffff for a valid BIOS32 structure. - */ - - for (check = (union bios32 *) __va(0xe0000); - check <= (union bios32 *) __va(0xffff0); - ++check) { - if (check->fields.signature != BIOS32_SIGNATURE) - continue; - length = check->fields.length * 16; - if (!length) - continue; - sum = 0; - for (i = 0; i < length ; ++i) - sum += check->chars[i]; - if (sum != 0) - continue; - if (check->fields.revision != 0) { - printk("PCI: unsupported BIOS32 revision %d at 0x%p\n", - check->fields.revision, check); - continue; - } - DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check); - if (check->fields.entry >= 0x100000) { - printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check); - return NULL; - } else { - unsigned long bios32_entry = check->fields.entry; - DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); - bios32_indirect.address = bios32_entry + PAGE_OFFSET; - if (check_pcibios()) - return &pci_bios_access; - } - break; /* Hopefully more than one BIOS32 cannot happen... */ - } - - return NULL; -} - -/* - * Sort the device list according to PCI BIOS. Nasty hack, but since some - * fool forgot to define the `correct' device order in the PCI BIOS specs - * and we want to be (possibly bug-to-bug ;-]) compatible with older kernels - * which used BIOS ordering, we are bound to do this... - */ - -static void __devinit pcibios_sort(void) -{ - LIST_HEAD(sorted_devices); - struct list_head *ln; - struct pci_dev *dev, *d; - int idx, found; - unsigned char bus, devfn; - - DBG("PCI: Sorting device list...\n"); - while (!list_empty(&pci_devices)) { - ln = pci_devices.next; - dev = pci_dev_g(ln); - idx = found = 0; - while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { - idx++; - for (ln=pci_devices.next; ln != &pci_devices; ln=ln->next) { - d = pci_dev_g(ln); - if (d->bus->number == bus && d->devfn == devfn) { - list_del(&d->global_list); - list_add_tail(&d->global_list, &sorted_devices); - if (d == dev) - found = 1; - break; - } - } - if (ln == &pci_devices) { - printk(KERN_WARNING "PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn); - /* - * We must not continue scanning as several buggy BIOSes - * return garbage after the last device. Grr. - */ - break; - } - } - if (!found) { - printk(KERN_WARNING "PCI: Device %02x:%02x not found by BIOS\n", - dev->bus->number, dev->devfn); - list_del(&dev->global_list); - list_add_tail(&dev->global_list, &sorted_devices); - } - } - list_splice(&sorted_devices, &pci_devices); -} - -/* - * BIOS Functions for IRQ Routing - */ - -struct irq_routing_options { - u16 size; - struct irq_info *table; - u16 segment; -} __attribute__((packed)); - -struct irq_routing_table * __devinit pcibios_get_irq_routing_table(void) -{ - struct irq_routing_options opt; - struct irq_routing_table *rt = NULL; - int ret, map; - unsigned long page; - - if (!pci_bios_present) - return NULL; - page = __get_free_page(GFP_KERNEL); - if (!page) - return NULL; - opt.table = (struct irq_info *) page; - opt.size = PAGE_SIZE; - opt.segment = __KERNEL_DS; - - DBG("PCI: Fetching IRQ routing table... "); - __asm__("push %%es\n\t" - "push %%ds\n\t" - "pop %%es\n\t" - "lcall *(%%esi); cld\n\t" - "pop %%es\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (ret), - "=b" (map) - : "0" (PCIBIOS_GET_ROUTING_OPTIONS), - "1" (0), - "D" ((long) &opt), - "S" (&pci_indirect)); - DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map); - if (ret & 0xff00) - printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff); - else if (opt.size) { - rt = kmalloc(sizeof(struct irq_routing_table) + opt.size, GFP_KERNEL); - if (rt) { - memset(rt, 0, sizeof(struct irq_routing_table)); - rt->size = opt.size + sizeof(struct irq_routing_table); - rt->exclusive_irqs = map; - memcpy(rt->slots, (void *) page, opt.size); - printk(KERN_INFO "PCI: Using BIOS Interrupt Routing Table\n"); - } - } - free_page(page); - return rt; -} - - -int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) -{ - int ret; - - __asm__("lcall *(%%esi); cld\n\t" - "jc 1f\n\t" - "xor %%ah, %%ah\n" - "1:" - : "=a" (ret) - : "0" (PCIBIOS_SET_PCI_HW_INT), - "b" ((dev->bus->number << 8) | dev->devfn), - "c" ((irq << 8) | (pin + 10)), - "S" (&pci_indirect)); - return !(ret & 0xff00); -} - -#endif - -/* - * Several buggy motherboards address only 16 devices and mirror - * them to next 16 IDs. We try to detect this `feature' on all - * primary buses (those containing host bridges as they are - * expected to be unique) and remove the ghost devices. - */ - -static void __devinit pcibios_fixup_ghosts(struct pci_bus *b) -{ - struct list_head *ln, *mn; - struct pci_dev *d, *e; - int mirror = PCI_DEVFN(16,0); - int seen_host_bridge = 0; - int i; - - DBG("PCI: Scanning for ghost devices on bus %d\n", b->number); - for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { - d = pci_dev_b(ln); - if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) - seen_host_bridge++; - for (mn=ln->next; mn != &b->devices; mn=mn->next) { - e = pci_dev_b(mn); - if (e->devfn != d->devfn + mirror || - e->vendor != d->vendor || - e->device != d->device || - e->class != d->class) - continue; - for(i=0; iresource[i].start != d->resource[i].start || - e->resource[i].end != d->resource[i].end || - e->resource[i].flags != d->resource[i].flags) - continue; - break; - } - if (mn == &b->devices) - return; - } - if (!seen_host_bridge) - return; - printk(KERN_WARNING "PCI: Ignoring ghost devices on bus %02x\n", b->number); - - ln = &b->devices; - while (ln->next != &b->devices) { - d = pci_dev_b(ln->next); - if (d->devfn >= mirror) { - list_del(&d->global_list); - list_del(&d->bus_list); - kfree(d); - } else - ln = ln->next; - } -} - -/* - * Discover remaining PCI buses in case there are peer host bridges. - * We use the number of last PCI bus provided by the PCI BIOS. - */ -static void __devinit pcibios_fixup_peer_bridges(void) -{ - int n; - struct pci_bus bus; - struct pci_dev dev; - u16 l; - - if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff) - return; - DBG("PCI: Peer bridge fixup\n"); - for (n=0; n <= pcibios_last_bus; n++) { - if (pci_bus_exists(&pci_root_buses, n)) - continue; - bus.number = n; - bus.ops = pci_root_ops; - dev.bus = &bus; - for(dev.devfn=0; dev.devfn<256; dev.devfn += 8) - if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) && - l != 0x0000 && l != 0xffff) { - DBG("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l); - printk(KERN_INFO "PCI: Discovered peer bus %02x\n", n); - pci_scan_bus(n, pci_root_ops, NULL); - break; - } - } -} - -/* - * Exceptions for specific devices. Usually work-arounds for fatal design flaws. - */ - -static void __devinit pci_fixup_i450nx(struct pci_dev *d) -{ - /* - * i450NX -- Find and scan all secondary buses on all PXB's. - */ - int pxb, reg; - u8 busno, suba, subb; -#ifdef CONFIG_MULTIQUAD - int quad = BUS2QUAD(d->bus->number); -#endif - printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name); - reg = 0xd0; - for(pxb=0; pxb<2; pxb++) { - pci_read_config_byte(d, reg++, &busno); - pci_read_config_byte(d, reg++, &suba); - pci_read_config_byte(d, reg++, &subb); - DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); - if (busno) - pci_scan_bus(QUADLOCAL2BUS(quad,busno), pci_root_ops, NULL); /* Bus A */ - if (suba < subb) - pci_scan_bus(QUADLOCAL2BUS(quad,suba+1), pci_root_ops, NULL); /* Bus B */ - } - pcibios_last_bus = -1; -} - -static void __devinit pci_fixup_i450gx(struct pci_dev *d) -{ - /* - * i450GX and i450KX -- Find and scan all secondary buses. - * (called separately for each PCI bridge found) - */ - u8 busno; - pci_read_config_byte(d, 0x4a, &busno); - printk(KERN_INFO "PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno); - pci_scan_bus(busno, pci_root_ops, NULL); - pcibios_last_bus = -1; -} - -static void __devinit pci_fixup_umc_ide(struct pci_dev *d) -{ - /* - * UM8886BF IDE controller sets region type bits incorrectly, - * therefore they look like memory despite of them being I/O. - */ - int i; - - printk(KERN_WARNING "PCI: Fixing base address flags for device %s\n", d->slot_name); - for(i=0; i<4; i++) - d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; -} - -static void __devinit pci_fixup_ncr53c810(struct pci_dev *d) -{ - /* - * NCR 53C810 returns class code 0 (at least on some systems). - * Fix class to be PCI_CLASS_STORAGE_SCSI - */ - if (!d->class) { - printk("PCI: fixing NCR 53C810 class code for %s\n", d->slot_name); - d->class = PCI_CLASS_STORAGE_SCSI << 8; - } -} - -static void __devinit pci_fixup_ide_bases(struct pci_dev *d) -{ - int i; - - /* - * PCI IDE controllers use non-standard I/O port decoding, respect it. - */ - if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) - return; - DBG("PCI: IDE base address fixup for %s\n", d->slot_name); - for(i=0; i<4; i++) { - struct resource *r = &d->resource[i]; - if ((r->start & ~0x80) == 0x374) { - r->start |= 2; - r->end = r->start; - } - } -} - -static void __devinit pci_fixup_ide_trash(struct pci_dev *d) -{ - int i; - - /* - * There exist PCI IDE controllers which have utter garbage - * in first four base registers. Ignore that. - */ - DBG("PCI: IDE base address trash cleared for %s\n", d->slot_name); - for(i=0; i<4; i++) - d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0; -} - -static void __devinit pci_fixup_latency(struct pci_dev *d) -{ - /* - * SiS 5597 and 5598 chipsets require latency timer set to - * at most 32 to avoid lockups. - */ - DBG("PCI: Setting max latency to 32\n"); - pcibios_max_latency = 32; -} - -static void __devinit pci_fixup_piix4_acpi(struct pci_dev *d) -{ - /* - * PIIX4 ACPI device: hardwired IRQ9 - */ - d->irq = 9; -} - -/* - * Addresses issues with problems in the memory write queue timer in - * certain VIA Northbridges. This bugfix is per VIA's specifications. - * - * VIA 8363,8622,8361 Northbridges: - * - bits 5, 6, 7 at offset 0x55 need to be turned off - * VIA 8367 (KT266x) Northbridges: - * - bits 5, 6, 7 at offset 0x95 need to be turned off - */ -static void __init pci_fixup_via_northbridge_bug(struct pci_dev *d) -{ - u8 v; - int where = 0x55; - - if (d->device == PCI_DEVICE_ID_VIA_8367_0) { - where = 0x95; /* the memory write queue timer register is - different for the kt266x's: 0x95 not 0x55 */ - } - - pci_read_config_byte(d, where, &v); - if (v & 0xe0) { - printk("Disabling broken memory write queue: [%02x] %02x->%02x\n", - where, v, v & 0x1f); - v &= 0x1f; /* clear bits 5, 6, 7 */ - pci_write_config_byte(d, where, v); - } -} - -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash }, - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, pci_fixup_via_northbridge_bug }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8622, pci_fixup_via_northbridge_bug }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8361, pci_fixup_via_northbridge_bug }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8367_0, pci_fixup_via_northbridge_bug }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, pci_fixup_ncr53c810 }, - { 0 } -}; - -/* - * Called after each bus is probed, but before its children - * are examined. - */ - -void __devinit pcibios_fixup_bus(struct pci_bus *b) -{ - pcibios_fixup_ghosts(b); - pci_read_bridge_bases(b); -} - - -struct pci_bus * __devinit pcibios_scan_root(int busnum) -{ - struct list_head *list; - struct pci_bus *bus; - - list_for_each(list, &pci_root_buses) { - bus = pci_bus_b(list); - if (bus->number == busnum) { - /* Already scanned */ - return bus; - } - } - - printk("PCI: Probing PCI hardware (bus %02x)\n", busnum); - - return pci_scan_bus(busnum, pci_root_ops, NULL); -} - -void __devinit pcibios_config_init(void) -{ - struct pci_ops *tmp=NULL; - - /* - * Try all known PCI access methods. Note that we support using - * both PCI BIOS and direct access, with a preference for direct. - */ - -#ifdef CONFIG_PCI_BIOS - if ((pci_probe & PCI_PROBE_BIOS) - && ((pci_root_ops = pci_find_bios()))) { - pci_probe |= PCI_BIOS_SORT; - pci_bios_present = 1; - pci_config_read = pci_bios_read; - pci_config_write = pci_bios_write; - } -#endif - tmp = pci_root_ops; - -#ifdef CONFIG_PCI_DIRECT - if ((pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) - && (pci_root_ops = pci_check_direct())) { - if (pci_root_ops == &pci_direct_conf1) { - pci_config_read = pci_conf1_read; - pci_config_write = pci_conf1_write; - } - else { - pci_config_read = pci_conf2_read; - pci_config_write = pci_conf2_write; - } - } -#endif - - /* if direct access failed, fall back to BIOS access. */ - if (pci_root_ops == NULL) - pci_root_ops = tmp; - - return; -} - -void __init pcibios_init(void) -{ - int quad; - - if (!pci_root_ops) - pcibios_config_init(); - if (!pci_root_ops) { - printk("PCI: System does not support PCI\n"); - return; - } - - printk("PCI: Probing PCI hardware\n"); - pci_root_bus = pcibios_scan_root(0); - if (clustered_apic_mode && (numnodes > 1)) { - for (quad = 1; quad < numnodes; ++quad) { - printk("Scanning PCI bus %d for quad %d\n", - QUADLOCAL2BUS(quad,0), quad); - pci_scan_bus(QUADLOCAL2BUS(quad,0), - pci_root_ops, NULL); - } - } - - pcibios_irq_init(); - - if (!pci_use_acpi_routing) - pcibios_fixup_peer_bridges(); - - pcibios_fixup_irqs(); - pcibios_resource_survey(); - -#ifdef CONFIG_PCI_BIOS - if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT)) - pcibios_sort(); -#endif -} - -char * __devinit pcibios_setup(char *str) -{ - if (!strcmp(str, "off")) { - pci_probe = 0; - return NULL; - } -#ifdef CONFIG_PCI_BIOS - else if (!strcmp(str, "bios")) { - pci_probe = PCI_PROBE_BIOS; - return NULL; - } else if (!strcmp(str, "nobios")) { - pci_probe &= ~PCI_PROBE_BIOS; - return NULL; - } else if (!strcmp(str, "nosort")) { - pci_probe |= PCI_NO_SORT; - return NULL; - } else if (!strcmp(str, "biosirq")) { - pci_probe |= PCI_BIOS_IRQ_SCAN; - return NULL; - } -#endif -#ifdef CONFIG_PCI_DIRECT - else if (!strcmp(str, "conf1")) { - pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS; - return NULL; - } - else if (!strcmp(str, "conf2")) { - pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS; - return NULL; - } -#endif -#ifdef CONFIG_ACPI_PCI - else if (!strcmp(str, "noacpi")) { - pci_probe |= PCI_NO_ACPI_ROUTING; - return NULL; - } -#endif - else if (!strcmp(str, "rom")) { - pci_probe |= PCI_ASSIGN_ROMS; - return NULL; - } else if (!strcmp(str, "assign-busses")) { - pci_probe |= PCI_ASSIGN_ALL_BUSSES; - return NULL; - } else if (!strcmp(str, "usepirqmask")) { - pci_probe |= PCI_USE_PIRQ_MASK; - return NULL; - } else if (!strncmp(str, "irqmask=", 8)) { - pcibios_irq_mask = simple_strtol(str+8, NULL, 0); - return NULL; - } else if (!strncmp(str, "lastbus=", 8)) { - pcibios_last_bus = simple_strtol(str+8, NULL, 0); - return NULL; - } - return str; -} - -unsigned int pcibios_assign_all_busses(void) -{ - return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; -} - -int pcibios_enable_device(struct pci_dev *dev) -{ - int err; - - if ((err = pcibios_enable_resources(dev)) < 0) - return err; - pcibios_enable_irq(dev); - return 0; -} diff -Nru a/arch/i386/kernel/pci-visws.c b/arch/i386/kernel/pci-visws.c --- a/arch/i386/kernel/pci-visws.c Tue May 7 13:31:16 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,141 +0,0 @@ -/* - * Low-Level PCI Support for SGI Visual Workstation - * - * (c) 1999--2000 Martin Mares - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "pci-i386.h" - -unsigned int pci_probe = 0; - -/* - * The VISWS uses configuration access type 1 only. - */ - -#define CONFIG_CMD(dev, where) (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3)) - -static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) -{ - outl(CONFIG_CMD(dev,where), 0xCF8); - *value = inb(0xCFC + (where&3)); - return PCIBIOS_SUCCESSFUL; -} - -static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value) -{ - outl(CONFIG_CMD(dev,where), 0xCF8); - *value = inw(0xCFC + (where&2)); - return PCIBIOS_SUCCESSFUL; -} - -static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value) -{ - outl(CONFIG_CMD(dev,where), 0xCF8); - *value = inl(0xCFC); - return PCIBIOS_SUCCESSFUL; -} - -static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value) -{ - outl(CONFIG_CMD(dev,where), 0xCF8); - outb(value, 0xCFC + (where&3)); - return PCIBIOS_SUCCESSFUL; -} - -static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value) -{ - outl(CONFIG_CMD(dev,where), 0xCF8); - outw(value, 0xCFC + (where&2)); - return PCIBIOS_SUCCESSFUL; -} - -static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value) -{ - outl(CONFIG_CMD(dev,where), 0xCF8); - outl(value, 0xCFC); - return PCIBIOS_SUCCESSFUL; -} - -#undef CONFIG_CMD - -static struct pci_ops visws_pci_ops = { - pci_conf1_read_config_byte, - pci_conf1_read_config_word, - pci_conf1_read_config_dword, - pci_conf1_write_config_byte, - pci_conf1_write_config_word, - pci_conf1_write_config_dword -}; - -static void __init pcibios_fixup_irqs(void) -{ - struct pci_dev *dev, *p; - u8 pin; - int irq; - - pci_for_each_dev(dev) { - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - dev->irq = 0; - if (!pin) - continue; - pin--; - if (dev->bus->parent) { - p = dev->bus->parent->self; - pin = (pin + PCI_SLOT(dev->devfn)) % 4; - } else - p = dev; - irq = visws_get_PCI_irq_vector(p->bus->number, PCI_SLOT(p->devfn), pin+1); - if (irq >= 0) - dev->irq = irq; - DBG("PCI IRQ: %s pin %d -> %d\n", dev->slot_name, pin, irq); - } -} - -void __init pcibios_fixup_bus(struct pci_bus *b) -{ - pci_read_bridge_bases(b); -} - -#if 0 -static struct resource visws_pci_bus_resources[2] = { - { "Host bus 1", 0xf4000000, 0xf7ffffff, 0 }, - { "Host bus 2", 0xf0000000, 0xf3ffffff, 0 } -}; -#endif - -void __init pcibios_init(void) -{ - unsigned int sec_bus = li_pcib_read16(LI_PCI_BUSNUM) & 0xff; - - printk("PCI: Probing PCI hardware on host buses 00 and %02x\n", sec_bus); - pci_scan_bus(0, &visws_pci_ops, NULL); - pci_scan_bus(sec_bus, &visws_pci_ops, NULL); - pcibios_fixup_irqs(); - pcibios_resource_survey(); -} - -char * __init pcibios_setup(char *str) -{ - return str; -} - -int pcibios_enable_device(struct pci_dev *dev) -{ - return pcibios_enable_resources(dev); -} - -void __init pcibios_penalize_isa_irq(irq) -{ -} diff -Nru a/arch/i386/pci/Makefile b/arch/i386/pci/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/Makefile Tue May 7 13:31:16 2002 @@ -0,0 +1,29 @@ +O_TARGET := pci.o + +obj-y := dma.o i386.o + +ifdef CONFIG_VISWS +obj-y += visws.o +else + +obj-$(CONFIG_PCI_BIOS) += pcbios.o +obj-$(CONFIG_PCI_DIRECT) += direct.o + +ifdef CONFIG_MULTIQUAD +obj-y += numa.o +else +obj-y += fixup.o + +ifdef CONFIG_ACPI_PCI +obj-y += acpi.o +endif +obj-y += legacy.o + + +endif # CONFIG_MULTIQUAD +obj-y += irq.o common.o +endif # CONFIG_VISWS + +export-objs += $(obj-y) + +include $(TOPDIR)/Rules.make diff -Nru a/arch/i386/pci/acpi.c b/arch/i386/pci/acpi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/acpi.c Tue May 7 13:31:16 2002 @@ -0,0 +1,70 @@ +#include +#include +#include "pci.h" + +extern void eisa_set_level_irq(int irq); + +static int acpi_lookup_irq ( + struct pci_dev *dev, + int assign) +{ + int result = 0; + int irq = 0; + u8 pin; + + /* TBD: Select IRQ from possible to improve routing performance. */ + + /* Find IRQ pin */ + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { + DBG(" -> no interrupt pin\n"); + return 0; + } + pin = pin - 1; + + result = acpi_prt_get_irq(dev, pin, &irq); + if (!irq) + result = -ENODEV; + if (0 != result) { + printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s\n", + 'A'+pin, dev->slot_name); + return result; + } + + /* only check for the IRQ */ + if (!assign) { + printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", + irq, dev->slot_name); + return 1; + } + + dev->irq = irq; + + /* also assign an IRQ */ + if (irq && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { + result = acpi_prt_set_irq(dev, pin, irq); + if (0 != result) { + printk(KERN_WARNING "PCI: Could not assign IRQ %d to device %s\n", irq, dev->slot_name); + return result; + } + + eisa_set_level_irq(irq); + + printk(KERN_INFO "PCI: Assigned IRQ %d for device %s\n", irq, dev->slot_name); + } + + return 1; +} + +static int __init pci_acpi_init(void) +{ + if (!(pci_probe & PCI_NO_ACPI_ROUTING)) { + printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); + printk(KERN_INFO "PCI: if you experience problems, try using option 'pci=noacpi'\n"); + pci_use_acpi_routing = 1; + pci_lookup_irq = acpi_lookup_irq; + } + return 0; +} + +arch_initcall(pci_acpi_init); diff -Nru a/arch/i386/pci/changelog b/arch/i386/pci/changelog --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/changelog Tue May 7 13:31:16 2002 @@ -0,0 +1,62 @@ +/* + * CHANGELOG : + * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION + * Revision 2.0 present on 's ASUS mainboard. + * + * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic + * Potter, potter@cao-vlsi.ibp.fr + * + * Jan 10, 1995 : Modified to store the information about configured pci + * devices into a list, which can be accessed via /proc/pci by + * Curtis Varner, cvarner@cs.ucr.edu + * + * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter. + * Alpha version. Intel & UMC chipset support only. + * + * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code + * moved to drivers/pci/pci.c. + * + * Dec 7, 1996 : Added support for direct configuration access of boards + * with Intel compatible access schemes (tsbogend@alpha.franken.de) + * + * Feb 3, 1997 : Set internal functions to static, save/restore flags + * avoid dead locks reading broken PCI BIOS, werner@suse.de + * + * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS + * (mj@atrey.karlin.mff.cuni.cz) + * + * May 7, 1997 : Added some missing cli()'s. [mj] + * + * Jun 20, 1997 : Corrected problems in "conf1" type accesses. + * (paubert@iram.es) + * + * Aug 2, 1997 : Split to PCI BIOS handling and direct PCI access parts + * and cleaned it up... Martin Mares + * + * Feb 6, 1998 : No longer using BIOS to find devices and device classes. [mj] + * + * May 1, 1998 : Support for peer host bridges. [mj] + * + * Jun 19, 1998 : Changed to use spinlocks, so that PCI configuration space + * can be accessed from interrupts even on SMP systems. [mj] + * + * August 1998 : Better support for peer host bridges and more paranoid + * checks for direct hardware access. Ugh, this file starts to look as + * a large gallery of common hardware bug workarounds (watch the comments) + * -- the PCI specs themselves are sane, but most implementors should be + * hit hard with \hammer scaled \magstep5. [mj] + * + * Jan 23, 1999 : More improvements to peer host bridge logic. i450NX fixup. [mj] + * + * Feb 8, 1999 : Added UM8886BF I/O address fixup. [mj] + * + * August 1999 : New resource management and configuration access stuff. [mj] + * + * Sep 19, 1999 : Use PCI IRQ routing tables for detection of peer host bridges. + * Based on ideas by Chris Frantz and David Hinds. [mj] + * + * Sep 28, 1999 : Handle unreported/unassigned IRQs. Thanks to Shuu Yamaguchi + * for a lot of patience during testing. [mj] + * + * Oct 8, 1999 : Split to pci-i386.c, pci-pc.c and pci-visws.c. [mj] + */ \ No newline at end of file diff -Nru a/arch/i386/pci/common.c b/arch/i386/pci/common.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/common.c Tue May 7 13:31:16 2002 @@ -0,0 +1,206 @@ +/* + * Low-Level PCI Support for PC + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include + +#include +#include +#include + +#include "pci.h" + +#ifdef CONFIG_PCI_BIOS +extern void pcibios_sort(void); +#endif + +unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus = NULL; +struct pci_ops *pci_root_ops = NULL; + +int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value) = NULL; +int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value) = NULL; + +/* + * This interrupt-safe spinlock protects all accesses to PCI + * configuration space. + */ +spinlock_t pci_config_lock = SPIN_LOCK_UNLOCKED; + +/* + * Several buggy motherboards address only 16 devices and mirror + * them to next 16 IDs. We try to detect this `feature' on all + * primary buses (those containing host bridges as they are + * expected to be unique) and remove the ghost devices. + */ + +static void __devinit pcibios_fixup_ghosts(struct pci_bus *b) +{ + struct list_head *ln, *mn; + struct pci_dev *d, *e; + int mirror = PCI_DEVFN(16,0); + int seen_host_bridge = 0; + int i; + + DBG("PCI: Scanning for ghost devices on bus %d\n", b->number); + for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + d = pci_dev_b(ln); + if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) + seen_host_bridge++; + for (mn=ln->next; mn != &b->devices; mn=mn->next) { + e = pci_dev_b(mn); + if (e->devfn != d->devfn + mirror || + e->vendor != d->vendor || + e->device != d->device || + e->class != d->class) + continue; + for(i=0; iresource[i].start != d->resource[i].start || + e->resource[i].end != d->resource[i].end || + e->resource[i].flags != d->resource[i].flags) + continue; + break; + } + if (mn == &b->devices) + return; + } + if (!seen_host_bridge) + return; + printk(KERN_WARNING "PCI: Ignoring ghost devices on bus %02x\n", b->number); + + ln = &b->devices; + while (ln->next != &b->devices) { + d = pci_dev_b(ln->next); + if (d->devfn >= mirror) { + list_del(&d->global_list); + list_del(&d->bus_list); + kfree(d); + } else + ln = ln->next; + } +} + +/* + * Called after each bus is probed, but before its children + * are examined. + */ + +void __devinit pcibios_fixup_bus(struct pci_bus *b) +{ + pcibios_fixup_ghosts(b); + pci_read_bridge_bases(b); +} + + +struct pci_bus * __devinit pcibios_scan_root(int busnum) +{ + struct list_head *list; + struct pci_bus *bus; + + list_for_each(list, &pci_root_buses) { + bus = pci_bus_b(list); + if (bus->number == busnum) { + /* Already scanned */ + return bus; + } + } + + printk("PCI: Probing PCI hardware (bus %02x)\n", busnum); + + return pci_scan_bus(busnum, pci_root_ops, NULL); +} + +static int __init pcibios_init(void) +{ + if (!pci_root_ops) { + printk("PCI: System does not support PCI\n"); + return 0; + } + + pcibios_resource_survey(); + +#ifdef CONFIG_PCI_BIOS + if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT)) + pcibios_sort(); +#endif + return 0; +} + +subsys_initcall(pcibios_init); + +char * __devinit pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + } +#ifdef CONFIG_PCI_BIOS + else if (!strcmp(str, "bios")) { + pci_probe = PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nobios")) { + pci_probe &= ~PCI_PROBE_BIOS; + return NULL; + } else if (!strcmp(str, "nosort")) { + pci_probe |= PCI_NO_SORT; + return NULL; + } else if (!strcmp(str, "biosirq")) { + pci_probe |= PCI_BIOS_IRQ_SCAN; + return NULL; + } +#endif +#ifdef CONFIG_PCI_DIRECT + else if (!strcmp(str, "conf1")) { + pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS; + return NULL; + } + else if (!strcmp(str, "conf2")) { + pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS; + return NULL; + } +#endif +#ifdef CONFIG_ACPI_PCI + else if (!strcmp(str, "noacpi")) { + pci_probe |= PCI_NO_ACPI_ROUTING; + return NULL; + } +#endif + else if (!strcmp(str, "rom")) { + pci_probe |= PCI_ASSIGN_ROMS; + return NULL; + } else if (!strcmp(str, "assign-busses")) { + pci_probe |= PCI_ASSIGN_ALL_BUSSES; + return NULL; + } else if (!strcmp(str, "usepirqmask")) { + pci_probe |= PCI_USE_PIRQ_MASK; + return NULL; + } else if (!strncmp(str, "irqmask=", 8)) { + pcibios_irq_mask = simple_strtol(str+8, NULL, 0); + return NULL; + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + return str; +} + +unsigned int pcibios_assign_all_busses(void) +{ + return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + int err; + + if ((err = pcibios_enable_resources(dev)) < 0) + return err; + pcibios_enable_irq(dev); + return 0; +} diff -Nru a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/direct.c Tue May 7 13:31:16 2002 @@ -0,0 +1,366 @@ +/* + * direct.c - Low-level direct PCI config space access + */ + +#include +#include "pci.h" + +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ + +#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ + (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) + +static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +{ + unsigned long flags; + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); + + switch (len) { + case 1: + *value = inb(0xCFC + (reg & 3)); + break; + case 2: + *value = inw(0xCFC + (reg & 2)); + break; + case 4: + *value = inl(0xCFC); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) +{ + unsigned long flags; + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8); + + switch (len) { + case 1: + outb((u8)value, 0xCFC + (reg & 3)); + break; + case 2: + outw((u16)value, 0xCFC + (reg & 2)); + break; + case 4: + outl((u32)value, 0xCFC); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + + +#undef PCI_CONF1_ADDRESS + +static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + int result; + u32 data; + + if (!value) + return -EINVAL; + + result = pci_conf1_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, &data); + + *value = (u8)data; + + return result; +} + +static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + int result; + u32 data; + + if (!value) + return -EINVAL; + + result = pci_conf1_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, &data); + + *value = (u16)data; + + return result; +} + +static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + if (!value) + return -EINVAL; + + return pci_conf1_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + +static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + return pci_conf1_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, value); +} + +static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + return pci_conf1_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, value); +} + +static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + return pci_conf1_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + +static struct pci_ops pci_direct_conf1 = { + pci_conf1_read_config_byte, + pci_conf1_read_config_word, + pci_conf1_read_config_dword, + pci_conf1_write_config_byte, + pci_conf1_write_config_word, + pci_conf1_write_config_dword +}; + + +/* + * Functions for accessing PCI configuration space with type 2 accesses + */ + +#define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg) + +static int pci_conf2_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +{ + unsigned long flags; + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + if (dev & 0x10) + return PCIBIOS_DEVICE_NOT_FOUND; + + spin_lock_irqsave(&pci_config_lock, flags); + + outb((u8)(0xF0 | (fn << 1)), 0xCF8); + outb((u8)bus, 0xCFA); + + switch (len) { + case 1: + *value = inb(PCI_CONF2_ADDRESS(dev, reg)); + break; + case 2: + *value = inw(PCI_CONF2_ADDRESS(dev, reg)); + break; + case 4: + *value = inl(PCI_CONF2_ADDRESS(dev, reg)); + break; + } + + outb (0, 0xCF8); + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static int pci_conf2_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) +{ + unsigned long flags; + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + if (dev & 0x10) + return PCIBIOS_DEVICE_NOT_FOUND; + + spin_lock_irqsave(&pci_config_lock, flags); + + outb((u8)(0xF0 | (fn << 1)), 0xCF8); + outb((u8)bus, 0xCFA); + + switch (len) { + case 1: + outb ((u8)value, PCI_CONF2_ADDRESS(dev, reg)); + break; + case 2: + outw ((u16)value, PCI_CONF2_ADDRESS(dev, reg)); + break; + case 4: + outl ((u32)value, PCI_CONF2_ADDRESS(dev, reg)); + break; + } + + outb (0, 0xCF8); + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +#undef PCI_CONF2_ADDRESS + +static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + int result; + u32 data; + result = pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, &data); + *value = (u8)data; + return result; +} + +static int pci_conf2_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + int result; + u32 data; + result = pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, &data); + *value = (u16)data; + return result; +} + +static int pci_conf2_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + return pci_conf2_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + +static int pci_conf2_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + return pci_conf2_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, value); +} + +static int pci_conf2_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + return pci_conf2_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, value); +} + +static int pci_conf2_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + return pci_conf2_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + +static struct pci_ops pci_direct_conf2 = { + pci_conf2_read_config_byte, + pci_conf2_read_config_word, + pci_conf2_read_config_dword, + pci_conf2_write_config_byte, + pci_conf2_write_config_word, + pci_conf2_write_config_dword +}; + + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __devinit pci_sanity_check(struct pci_ops *o) +{ + u16 x; + struct pci_bus bus; /* Fake bus and device */ + struct pci_dev dev; + + if (pci_probe & PCI_NO_CHECKS) + return 1; + bus.number = 0; + dev.bus = &bus; + for(dev.devfn=0; dev.devfn < 0x100; dev.devfn++) + if ((!o->read_word(&dev, PCI_CLASS_DEVICE, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read_word(&dev, PCI_VENDOR_ID, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + DBG("PCI: Sanity check failed\n"); + return 0; +} + +static struct pci_ops * __devinit pci_check_direct(void) +{ + unsigned int tmp; + unsigned long flags; + + __save_flags(flags); __cli(); + + /* + * Check if configuration type 1 works. + */ + if (pci_probe & PCI_PROBE_CONF1) { + outb (0x01, 0xCFB); + tmp = inl (0xCF8); + outl (0x80000000, 0xCF8); + if (inl (0xCF8) == 0x80000000 && + pci_sanity_check(&pci_direct_conf1)) { + outl (tmp, 0xCF8); + __restore_flags(flags); + printk(KERN_INFO "PCI: Using configuration type 1\n"); + if (!request_region(0xCF8, 8, "PCI conf1")) + return NULL; + return &pci_direct_conf1; + } + outl (tmp, 0xCF8); + } + + /* + * Check if configuration type 2 works. + */ + if (pci_probe & PCI_PROBE_CONF2) { + outb (0x00, 0xCFB); + outb (0x00, 0xCF8); + outb (0x00, 0xCFA); + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 && + pci_sanity_check(&pci_direct_conf2)) { + __restore_flags(flags); + printk(KERN_INFO "PCI: Using configuration type 2\n"); + if (!request_region(0xCF8, 4, "PCI conf2")) + return NULL; + return &pci_direct_conf2; + } + } + + __restore_flags(flags); + return NULL; +} + +static int __init pci_direct_init(void) +{ + if ((pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) + && (pci_root_ops = pci_check_direct())) { + if (pci_root_ops == &pci_direct_conf1) { + pci_config_read = pci_conf1_read; + pci_config_write = pci_conf1_write; + } + else { + pci_config_read = pci_conf2_read; + pci_config_write = pci_conf2_write; + } + } + return 0; +} + +arch_initcall(pci_direct_init); diff -Nru a/arch/i386/pci/dma.c b/arch/i386/pci/dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/dma.c Tue May 7 13:31:16 2002 @@ -0,0 +1,37 @@ +/* + * Dynamic DMA mapping support. + * + * On i386 there is no hardware dynamic DMA address translation, + * so consistent alloc/free are merely page allocation/freeing. + * The rest of the dynamic DMA mapping interface is implemented + * in asm/pci.h. + */ + +#include +#include +#include +#include +#include + +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC; + + if (hwdev == NULL || ((u32)hwdev->dma_mask != 0xffffffff)) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/fixup.c Tue May 7 13:31:16 2002 @@ -0,0 +1,163 @@ +/* + * Exceptions for specific devices. Usually work-arounds for fatal design flaws. + */ + +#include +#include "pci.h" + + +static void __devinit pci_fixup_i450nx(struct pci_dev *d) +{ + /* + * i450NX -- Find and scan all secondary buses on all PXB's. + */ + int pxb, reg; + u8 busno, suba, subb; + + printk(KERN_WARNING "PCI: Searching for i450NX host bridges on %s\n", d->slot_name); + reg = 0xd0; + for(pxb=0; pxb<2; pxb++) { + pci_read_config_byte(d, reg++, &busno); + pci_read_config_byte(d, reg++, &suba); + pci_read_config_byte(d, reg++, &subb); + DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); + if (busno) + pci_scan_bus(busno, pci_root_ops, NULL); /* Bus A */ + if (suba < subb) + pci_scan_bus(suba+1, pci_root_ops, NULL); /* Bus B */ + } + pcibios_last_bus = -1; +} + +static void __devinit pci_fixup_i450gx(struct pci_dev *d) +{ + /* + * i450GX and i450KX -- Find and scan all secondary buses. + * (called separately for each PCI bridge found) + */ + u8 busno; + pci_read_config_byte(d, 0x4a, &busno); + printk(KERN_INFO "PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno); + pci_scan_bus(busno, pci_root_ops, NULL); + pcibios_last_bus = -1; +} + +static void __devinit pci_fixup_umc_ide(struct pci_dev *d) +{ + /* + * UM8886BF IDE controller sets region type bits incorrectly, + * therefore they look like memory despite of them being I/O. + */ + int i; + + printk(KERN_WARNING "PCI: Fixing base address flags for device %s\n", d->slot_name); + for(i=0; i<4; i++) + d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; +} + +static void __devinit pci_fixup_ncr53c810(struct pci_dev *d) +{ + /* + * NCR 53C810 returns class code 0 (at least on some systems). + * Fix class to be PCI_CLASS_STORAGE_SCSI + */ + if (!d->class) { + printk(KERN_WARNING "PCI: fixing NCR 53C810 class code for %s\n", d->slot_name); + d->class = PCI_CLASS_STORAGE_SCSI << 8; + } +} + +static void __devinit pci_fixup_ide_bases(struct pci_dev *d) +{ + int i; + + /* + * PCI IDE controllers use non-standard I/O port decoding, respect it. + */ + if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) + return; + DBG("PCI: IDE base address fixup for %s\n", d->slot_name); + for(i=0; i<4; i++) { + struct resource *r = &d->resource[i]; + if ((r->start & ~0x80) == 0x374) { + r->start |= 2; + r->end = r->start; + } + } +} + +static void __devinit pci_fixup_ide_trash(struct pci_dev *d) +{ + int i; + + /* + * There exist PCI IDE controllers which have utter garbage + * in first four base registers. Ignore that. + */ + DBG("PCI: IDE base address trash cleared for %s\n", d->slot_name); + for(i=0; i<4; i++) + d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0; +} + +static void __devinit pci_fixup_latency(struct pci_dev *d) +{ + /* + * SiS 5597 and 5598 chipsets require latency timer set to + * at most 32 to avoid lockups. + */ + DBG("PCI: Setting max latency to 32\n"); + pcibios_max_latency = 32; +} + +static void __devinit pci_fixup_piix4_acpi(struct pci_dev *d) +{ + /* + * PIIX4 ACPI device: hardwired IRQ9 + */ + d->irq = 9; +} + +/* + * Addresses issues with problems in the memory write queue timer in + * certain VIA Northbridges. This bugfix is per VIA's specifications. + * + * VIA 8363,8622,8361 Northbridges: + * - bits 5, 6, 7 at offset 0x55 need to be turned off + * VIA 8367 (KT266x) Northbridges: + * - bits 5, 6, 7 at offset 0x95 need to be turned off + */ +static void __init pci_fixup_via_northbridge_bug(struct pci_dev *d) +{ + u8 v; + int where = 0x55; + + if (d->device == PCI_DEVICE_ID_VIA_8367_0) { + where = 0x95; /* the memory write queue timer register is + different for the kt266x's: 0x95 not 0x55 */ + } + + pci_read_config_byte(d, where, &v); + if (v & 0xe0) { + printk(KERN_WARNING "Disabling broken memory write queue: [%02x] %02x->%02x\n", + where, v, v & 0x1f); + v &= 0x1f; /* clear bits 5, 6, 7 */ + pci_write_config_byte(d, where, v); + } +} + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash }, + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, pci_fixup_via_northbridge_bug }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8622, pci_fixup_via_northbridge_bug }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8361, pci_fixup_via_northbridge_bug }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8367_0, pci_fixup_via_northbridge_bug }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, pci_fixup_ncr53c810 }, + { 0 } +}; diff -Nru a/arch/i386/pci/i386.c b/arch/i386/pci/i386.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/i386.c Tue May 7 13:31:16 2002 @@ -0,0 +1,323 @@ +/* + * Low-Level PCI Access for i386 machines + * + * Copyright 1993, 1994 Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * Drew@Colorado.EDU + * +1 (303) 786-7975 + * + * Drew's work was sponsored by: + * iX Multiuser Multitasking Magazine + * Hannover, Germany + * hm@ix.de + * + * Copyright 1997--2000 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +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; + new |= 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); + } +} + +/* + * 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) +{ + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + 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->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); + } + } +} + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); + pcibios_assign_resources(); +} + +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 (dev->resource[PCI_ROM_RESOURCE].start) + 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; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + printk("PCI: Setting latency timer of device %s to %d\n", dev->slot_name, lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* I/O space cannot be accessed via normal processor loads and + * stores on this platform. + */ + if (mmap_state == pci_mmap_io) + return -EINVAL; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO); + + prot = pgprot_val(vma->vm_page_prot); + if (boot_cpu_data.x86 > 3) + prot |= _PAGE_PCD | _PAGE_PWT; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored, it is changed via the mtrr + * interfaces on this platform. + */ + if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff -Nru a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/irq.c Tue May 7 13:31:16 2002 @@ -0,0 +1,810 @@ +/* + * Low-Level PCI Support for PC -- Routing of Interrupts + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) +#define PIRQ_VERSION 0x0100 + +int pci_use_acpi_routing = 0; +int broken_hp_bios_irq9; + +static struct irq_routing_table *pirq_table; + +/* + * Never use: 0, 1, 2 (timer, keyboard, and cascade) + * Avoid using: 13, 14 and 15 (FP error and IDE). + * Penalize: 3, 4, 6, 7, 12 (known ISA uses: serial, floppy, parallel and mouse) + */ +unsigned int pcibios_irq_mask = 0xfff8; + +static int pirq_penalty[16] = { + 1000000, 1000000, 1000000, 1000, 1000, 0, 1000, 1000, + 0, 0, 0, 0, 1000, 100000, 100000, 100000 +}; + +struct irq_router { + char *name; + u16 vendor, device; + int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); + int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new); +}; + +int (*pci_lookup_irq)(struct pci_dev * dev, int assign) = NULL; + +/* + * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. + */ + +static struct irq_routing_table * __init pirq_find_routing_table(void) +{ + u8 *addr; + struct irq_routing_table *rt; + int i; + u8 sum; + + for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) { + rt = (struct irq_routing_table *) addr; + if (rt->signature != PIRQ_SIGNATURE || + rt->version != PIRQ_VERSION || + rt->size % 16 || + rt->size < sizeof(struct irq_routing_table)) + continue; + sum = 0; + for(i=0; isize; i++) + sum += addr[i]; + if (!sum) { + DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); + return rt; + } + } + return NULL; +} + +/* + * If we have a IRQ routing table, use it to search for peer host + * bridges. It's a gross hack, but since there are no other known + * ways how to get a list of buses, we have to go this way. + */ + +static void __init pirq_peer_trick(void) +{ + struct irq_routing_table *rt = pirq_table; + u8 busmap[256]; + int i; + struct irq_info *e; + + memset(busmap, 0, sizeof(busmap)); + for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) { + e = &rt->slots[i]; +#ifdef DEBUG + { + int j; + DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot); + for(j=0; j<4; j++) + DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap); + DBG("\n"); + } +#endif + busmap[e->bus] = 1; + } + for(i=1; i<256; i++) + /* + * It might be a secondary bus, but in this case its parent is already + * known (ascending bus order) and therefore pci_scan_bus returns immediately. + */ + if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL)) + printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i); + pcibios_last_bus = -1; +} + +/* + * Code for querying and setting of IRQ routes on various interrupt routers. + */ + +void eisa_set_level_irq(unsigned int irq) +{ + unsigned char mask = 1 << (irq & 7); + unsigned int port = 0x4d0 + (irq >> 3); + unsigned char val = inb(port); + + if (!(val & mask)) { + DBG(" -> edge"); + outb(val | mask, port); + } +} + +/* + * Common IRQ routing practice: nybbles in config space, + * offset by some magic constant. + */ +static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr) +{ + u8 x; + unsigned reg = offset + (nr >> 1); + + pci_read_config_byte(router, reg, &x); + return (nr & 1) ? (x >> 4) : (x & 0xf); +} + +static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val) +{ + u8 x; + unsigned reg = offset + (nr >> 1); + + pci_read_config_byte(router, reg, &x); + x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val); + pci_write_config_byte(router, reg, x); +} + +/* + * ALI pirq entries are damn ugly, and completely undocumented. + * This has been figured out from pirq tables, and it's not a pretty + * picture. + */ +static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + static unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 }; + + return irqmap[read_config_nybble(router, 0x48, pirq-1)]; +} + +static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + static unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 }; + unsigned int val = irqmap[irq]; + + if (val) { + write_config_nybble(router, 0x48, pirq-1, val); + return 1; + } + return 0; +} + +/* + * The Intel PIIX4 pirq rules are fairly simple: "pirq" is + * just a pointer to the config space. + */ +static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 x; + + pci_read_config_byte(router, pirq, &x); + return (x < 16) ? x : 0; +} + +static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + pci_write_config_byte(router, pirq, irq); + return 1; +} + +/* + * The VIA pirq rules are nibble-based, like ALI, + * but without the ugly irq number munging. + */ +static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0x55, pirq); +} + +static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0x55, pirq, irq); + return 1; +} + +/* + * ITE 8330G pirq rules are nibble-based + * FIXME: pirqmap may be { 1, 0, 3, 2 }, + * 2+3 are both mapped to irq 9 on my system + */ +static int pirq_ite_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + static unsigned char pirqmap[4] = { 1, 0, 2, 3 }; + return read_config_nybble(router,0x43, pirqmap[pirq-1]); +} + +static int pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + static unsigned char pirqmap[4] = { 1, 0, 2, 3 }; + write_config_nybble(router, 0x43, pirqmap[pirq-1], irq); + return 1; +} + +/* + * OPTI: high four bits are nibble pointer.. + * I wonder what the low bits do? + */ +static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0xb8, pirq >> 4); +} + +static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0xb8, pirq >> 4, irq); + return 1; +} + +/* + * Cyrix: nibble offset 0x5C + */ +static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + return read_config_nybble(router, 0x5C, (pirq-1)^1); +} + +static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + write_config_nybble(router, 0x5C, (pirq-1)^1, irq); + return 1; +} + +/* + * PIRQ routing for SiS 85C503 router used in several SiS chipsets + * According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997) + * the related registers work as follows: + * + * general: one byte per re-routable IRQ, + * bit 7 IRQ mapping enabled (0) or disabled (1) + * bits [6:4] reserved + * bits [3:0] IRQ to map to + * allowed: 3-7, 9-12, 14-15 + * reserved: 0, 1, 2, 8, 13 + * + * individual registers in device config space: + * + * 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case + * + * 0x61: IDEIRQ: bits as in general case - but: + * bits [6:5] must be written 01 + * bit 4 channel-select primary (0), secondary (1) + * + * 0x62: USBIRQ: bits as in general case - but: + * bit 4 OHCI function disabled (0), enabled (1) + * + * 0x6a: ACPI/SCI IRQ - bits as in general case + * + * 0x7e: Data Acq. Module IRQ - bits as in general case + * + * Apparently there are systems implementing PCI routing table using both + * link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets + * like 0x62 as link values for USBIRQ e.g. So there is no simple + * "register = offset + pirq" relation. + * Currently we support PCI INTA..D and USBIRQ and try our best to handle + * both link mappings. + * IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS). + */ + +static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 x; + int reg = pirq; + + switch(pirq) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: + reg += 0x40; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x62: + pci_read_config_byte(router, reg, &x); + if (reg != 0x62) + break; + if (!(x & 0x40)) + return 0; + break; + case 0x61: + case 0x6a: + case 0x7e: + printk(KERN_INFO "SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n"); + return 0; + default: + printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); + return 0; + } + return (x & 0x80) ? 0 : (x & 0x0f); +} + +static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + u8 x; + int reg = pirq; + + switch(pirq) { + case 0x01: + case 0x02: + case 0x03: + case 0x04: + reg += 0x40; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x62: + x = (irq&0x0f) ? (irq&0x0f) : 0x80; + if (reg != 0x62) + break; + /* always mark OHCI enabled, as nothing else knows about this */ + x |= 0x40; + break; + case 0x61: + case 0x6a: + case 0x7e: + printk(KERN_INFO "advanced SiS pirq mapping not yet implemented\n"); + return 0; + default: + printk(KERN_INFO "SiS router pirq escape (%d)\n", pirq); + return 0; + } + pci_write_config_byte(router, reg, x); + + return 1; +} + +/* + * VLSI: nibble offset 0x74 - educated guess due to routing table and + * config space of VLSI 82C534 PCI-bridge/router (1004:0102) + * Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard + * devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6 + * for the busbridge to the docking station. + */ + +static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + if (pirq > 8) { + printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); + return 0; + } + return read_config_nybble(router, 0x74, pirq-1); +} + +static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + if (pirq > 8) { + printk(KERN_INFO "VLSI router pirq escape (%d)\n", pirq); + return 0; + } + write_config_nybble(router, 0x74, pirq-1, irq); + return 1; +} + +/* + * ServerWorks: PCI interrupts mapped to system IRQ lines through Index + * and Redirect I/O registers (0x0c00 and 0x0c01). The Index register + * format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a. The Redirect + * register is a straight binary coding of desired PIC IRQ (low nibble). + * + * The 'link' value in the PIRQ table is already in the correct format + * for the Index register. There are some special index values: + * 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1, + * and 0x03 for SMBus. + */ +static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + outb_p(pirq, 0xc00); + return inb(0xc01) & 0xf; +} + +static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + outb_p(pirq, 0xc00); + outb_p(irq, 0xc01); + return 1; +} + +/* Support for AMD756 PCI IRQ Routing + * Jhon H. Caicedo + * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced) + * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced) + * The AMD756 pirq rules are nibble-based + * offset 0x56 0-3 PIRQA 4-7 PIRQB + * offset 0x57 0-3 PIRQC 4-7 PIRQD + */ +static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq) +{ + u8 irq; + irq = 0; + if (pirq <= 4) + { + irq = read_config_nybble(router, 0x56, pirq - 1); + } + printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n", + dev->vendor, dev->device, pirq, irq); + return irq; +} + +static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", + dev->vendor, dev->device, pirq, irq); + if (pirq <= 4) + { + write_config_nybble(router, 0x56, pirq - 1, irq); + } + return 1; +} + +#ifdef CONFIG_PCI_BIOS + +static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) +{ + struct pci_dev *bridge; + int pin = pci_get_interrupt_pin(dev, &bridge); + return pcibios_set_irq_routing(bridge, pin, irq); +} + +static struct irq_router pirq_bios_router = + { "BIOS", 0, 0, NULL, pirq_bios_set }; + +#endif + +static struct irq_router pirq_routers[] = { + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, pirq_piix_get, pirq_piix_set }, + { "PIIX", PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, pirq_piix_get, pirq_piix_set }, + + { "ALI", PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pirq_ali_get, pirq_ali_set }, + + { "ITE", PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8330G_0, pirq_ite_get, pirq_ite_set }, + + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0, pirq_via_get, pirq_via_set }, + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596, pirq_via_get, pirq_via_set }, + { "VIA", PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, pirq_via_get, pirq_via_set }, + + { "OPTI", PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700, pirq_opti_get, pirq_opti_set }, + + { "NatSemi", PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, pirq_cyrix_get, pirq_cyrix_set }, + { "SIS", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, pirq_sis_get, pirq_sis_set }, + { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set }, + { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, + pirq_serverworks_get, pirq_serverworks_set }, + { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, + pirq_serverworks_get, pirq_serverworks_set }, + { "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B, + pirq_amd756_get, pirq_amd756_set }, + + { "default", 0, 0, NULL, NULL } +}; + +static struct irq_router *pirq_router; +static struct pci_dev *pirq_router_dev; + +static void __init pirq_find_router(void) +{ + struct irq_routing_table *rt = pirq_table; + struct irq_router *r; + +#ifdef CONFIG_PCI_BIOS + if (!rt->signature) { + printk(KERN_INFO "PCI: Using BIOS for IRQ routing\n"); + pirq_router = &pirq_bios_router; + return; + } +#endif + + DBG("PCI: Attempting to find IRQ router for %04x:%04x\n", + rt->rtr_vendor, rt->rtr_device); + + /* fall back to default router if nothing else found */ + pirq_router = &pirq_routers[ARRAY_SIZE(pirq_routers) - 1]; + + pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn); + if (!pirq_router_dev) { + DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn); + return; + } + + for(r=pirq_routers; r->vendor; r++) { + /* Exact match against router table entry? Use it! */ + if (r->vendor == rt->rtr_vendor && r->device == rt->rtr_device) { + pirq_router = r; + break; + } + /* Match against router device entry? Use it as a fallback */ + if (r->vendor == pirq_router_dev->vendor && r->device == pirq_router_dev->device) { + pirq_router = r; + } + } + printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", + pirq_router->name, + pirq_router_dev->vendor, + pirq_router_dev->device, + pirq_router_dev->slot_name); +} + +static struct irq_info *pirq_get_info(struct pci_dev *dev) +{ + struct irq_routing_table *rt = pirq_table; + int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); + struct irq_info *info; + + for (info = rt->slots; entries--; info++) + if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn)) + return info; + return NULL; +} + +static void pcibios_test_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int pcibios_lookup_irq(struct pci_dev *dev, int assign) +{ + u8 pin; + struct irq_info *info; + int i, pirq, newirq; + int irq = 0; + u32 mask; + struct irq_router *r = pirq_router; + struct pci_dev *dev2; + char *msg = NULL; + + /* Find IRQ pin */ + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { + DBG(" -> no interrupt pin\n"); + return 0; + } + pin = pin - 1; + + /* Find IRQ routing entry */ + + if (!pirq_table) + return 0; + + DBG("IRQ for %s:%d", dev->slot_name, pin); + info = pirq_get_info(dev); + if (!info) { + DBG(" -> not found in routing table\n"); + return 0; + } + pirq = info->irq[pin].link; + mask = info->irq[pin].bitmap; + if (!pirq) { + DBG(" -> not routed\n"); + return 0; + } + DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); + mask &= pcibios_irq_mask; + + /* Work around broken HP Pavilion Notebooks which assign USB to + IRQ 9 even though it is actually wired to IRQ 11 */ + + if (broken_hp_bios_irq9 && pirq == 0x59 && dev->irq == 9) { + dev->irq = 11; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 11); + r->set(pirq_router_dev, dev, pirq, 11); + } + + /* + * Find the best IRQ to assign: use the one + * reported by the device if possible. + */ + newirq = dev->irq; + if (!((1 << newirq) & mask)) { + if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0; + else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, dev->slot_name); + } + if (!newirq && assign) { + for (i = 0; i < 16; i++) { + if (!(mask & (1 << i))) + continue; + if (pirq_penalty[i] < pirq_penalty[newirq] && + !request_irq(i, pcibios_test_irq_handler, SA_SHIRQ, "pci-test", dev)) { + free_irq(i, dev); + newirq = i; + } + } + } + DBG(" -> newirq=%d", newirq); + + /* Check if it is hardcoded */ + if ((pirq & 0xf0) == 0xf0) { + irq = pirq & 0xf; + DBG(" -> hardcoded IRQ %d\n", irq); + msg = "Hardcoded"; + } else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \ + ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) { + DBG(" -> got IRQ %d\n", irq); + msg = "Found"; + } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { + DBG(" -> assigning IRQ %d", newirq); + if (r->set(pirq_router_dev, dev, pirq, newirq)) { + eisa_set_level_irq(newirq); + DBG(" ... OK\n"); + msg = "Assigned"; + irq = newirq; + } + } + + if (!irq) { + DBG(" ... failed\n"); + if (newirq && mask == (1 << newirq)) { + msg = "Guessed"; + irq = newirq; + } else + return 0; + } + printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, dev->slot_name); + + /* Update IRQ for all devices with the same pirq value */ + pci_for_each_dev(dev2) { + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin); + if (!pin) + continue; + pin--; + info = pirq_get_info(dev2); + if (!info) + continue; + if (info->irq[pin].link == pirq) { + /* We refuse to override the dev->irq information. Give a warning! */ + if ( dev2->irq && dev2->irq != irq && \ + (!(pci_probe & PCI_USE_PIRQ_MASK) || \ + ((1 << dev2->irq) & mask)) ) { + printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n", + dev2->slot_name, dev2->irq, irq); + continue; + } + dev2->irq = irq; + pirq_penalty[irq]++; + if (dev != dev2) + printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, dev2->slot_name); + } + } + return 1; +} + +static int __init pcibios_irq_init(void) +{ + DBG("PCI: IRQ init\n"); + + if (pci_lookup_irq) + return 0; + + pirq_table = pirq_find_routing_table(); + +#ifdef CONFIG_PCI_BIOS + if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN)) + pirq_table = pcibios_get_irq_routing_table(); +#endif + if (pirq_table) { + pirq_peer_trick(); + pirq_find_router(); + if (pirq_table->exclusive_irqs) { + int i; + for (i=0; i<16; i++) + if (!(pirq_table->exclusive_irqs & (1 << i))) + pirq_penalty[i] += 100; + } + /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */ + if (io_apic_assign_pci_irqs) + pirq_table = NULL; + } + pci_lookup_irq = pcibios_lookup_irq; + pcibios_fixup_irqs(); + return 0; +} + +subsys_initcall(pcibios_irq_init); + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev; + u8 pin; + + DBG("PCI: IRQ fixup\n"); + pci_for_each_dev(dev) { + /* + * If the BIOS has set an out of range IRQ number, just ignore it. + * Also keep track of which IRQ's are already in use. + */ + if (dev->irq >= 16) { + DBG("%s: ignoring bogus IRQ %d\n", dev->slot_name, dev->irq); + dev->irq = 0; + } + /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ + if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000) + pirq_penalty[dev->irq] = 0; + pirq_penalty[dev->irq]++; + } + + pci_for_each_dev(dev) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); +#ifdef CONFIG_X86_IO_APIC + /* + * Recalculate IRQ numbers if we use the I/O APIC. + */ + if (io_apic_assign_pci_irqs) + { + int irq; + + if (pin) { + pin--; /* interrupt pins are numbered starting from 1 */ + irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); + /* + * Busses behind bridges are typically not listed in the MP-table. + * In this case we have to look up the IRQ based on the parent bus, + * parent slot, and pin number. The SMP code detects such bridged + * busses itself so we should get into this branch reliably. + */ + if (irq < 0 && dev->bus->parent) { /* go back to the bridge */ + struct pci_dev * bridge = dev->bus->self; + + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, + PCI_SLOT(bridge->devfn), pin); + if (irq >= 0) + printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", + bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); + } + if (irq >= 0) { + printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + } + } + } +#endif + /* + * Still no IRQ? Try to lookup one... + */ + if (pin && !dev->irq) + pci_lookup_irq(dev, 0); + } +} + +void pcibios_penalize_isa_irq(int irq) +{ + /* + * If any ISAPnP device reports an IRQ in its list of possible + * IRQ's, we try to avoid assigning it to PCI devices. + */ + pirq_penalty[irq] += 100; +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + u8 pin; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin && !pci_lookup_irq(dev, 1) && !dev->irq) { + char *msg; + if (io_apic_assign_pci_irqs) + msg = " Probably buggy MP table."; + else if (pci_probe & PCI_BIOS_IRQ_SCAN) + msg = ""; + else + msg = " Please try using pci=biosirq."; + printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", + 'A' + pin - 1, dev->slot_name, msg); + } +} diff -Nru a/arch/i386/pci/legacy.c b/arch/i386/pci/legacy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/legacy.c Tue May 7 13:31:16 2002 @@ -0,0 +1,53 @@ +/* + * legacy.c - traditional, old school PCI bus probing + */ +#include +#include "pci.h" + +/* + * Discover remaining PCI buses in case there are peer host bridges. + * We use the number of last PCI bus provided by the PCI BIOS. + */ +static void __devinit pcibios_fixup_peer_bridges(void) +{ + int n; + struct pci_bus bus; + struct pci_dev dev; + u16 l; + + if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff) + return; + DBG("PCI: Peer bridge fixup\n"); + for (n=0; n <= pcibios_last_bus; n++) { + if (pci_bus_exists(&pci_root_buses, n)) + continue; + bus.number = n; + bus.ops = pci_root_ops; + dev.bus = &bus; + for(dev.devfn=0; dev.devfn<256; dev.devfn += 8) + if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) && + l != 0x0000 && l != 0xffff) { + DBG("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l); + printk(KERN_INFO "PCI: Discovered peer bus %02x\n", n); + pci_scan_bus(n, pci_root_ops, NULL); + break; + } + } +} + +static int __init pci_legacy_init(void) +{ + if (!pci_root_ops) { + printk("PCI: System does not support PCI\n"); + return 0; + } + + printk("PCI: Probing PCI hardware\n"); + pci_root_bus = pcibios_scan_root(0); + + if (!pci_use_acpi_routing) + pcibios_fixup_peer_bridges(); + return 0; +} + +subsys_initcall(pci_legacy_init); diff -Nru a/arch/i386/pci/numa.c b/arch/i386/pci/numa.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/numa.c Tue May 7 13:31:16 2002 @@ -0,0 +1,119 @@ +/* + * numa.c - Low-level PCI access for NUMA-Q machines + */ +#include + +#include "pci.h" + +#define BUS2QUAD(global) (mp_bus_id_to_node[global]) +#define BUS2LOCAL(global) (mp_bus_id_to_local[global]) +#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) + +#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \ + (0x80000000 | (BUS2LOCAL(bus) << 16) | (dev << 11) | (fn << 8) | (reg & ~3)) + +static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +{ + unsigned long flags; + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); + + switch (len) { + case 1: + *value = inb_quad(0xCFC + (reg & 3), BUS2QUAD(bus)); + break; + case 2: + *value = inw_quad(0xCFC + (reg & 2), BUS2QUAD(bus)); + break; + case 4: + *value = inl_quad(0xCFC, BUS2QUAD(bus)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + +static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) +{ + unsigned long flags; + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + outl_quad(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8, BUS2QUAD(bus)); + + switch (len) { + case 1: + outb_quad((u8)value, 0xCFC + (reg & 3), BUS2QUAD(bus)); + break; + case 2: + outw_quad((u16)value, 0xCFC + (reg & 2), BUS2QUAD(bus)); + break; + case 4: + outl_quad((u32)value, 0xCFC, BUS2QUAD(bus)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return 0; +} + + +static void __devinit pci_fixup_i450nx(struct pci_dev *d) +{ + /* + * i450NX -- Find and scan all secondary buses on all PXB's. + */ + int pxb, reg; + u8 busno, suba, subb; + int quad = BUS2QUAD(d->bus->number); + + printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name); + reg = 0xd0; + for(pxb=0; pxb<2; pxb++) { + pci_read_config_byte(d, reg++, &busno); + pci_read_config_byte(d, reg++, &suba); + pci_read_config_byte(d, reg++, &subb); + DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); + if (busno) + pci_scan_bus(QUADLOCAL2BUS(quad,busno), pci_root_ops, NULL); /* Bus A */ + if (suba < subb) + pci_scan_bus(QUADLOCAL2BUS(quad,suba+1), pci_root_ops, NULL); /* Bus B */ + } + pcibios_last_bus = -1; +} + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, +}; + +static int __init pci_numa_init(void) +{ + int quad; + + pci_config_read = pci_conf1_read; + pci_config_write = pci_conf1_write; + + pci_root_bus = pcibios_scan_root(0); + if (clustered_apic_mode && (numnodes > 1)) { + for (quad = 1; quad < numnodes; ++quad) { + printk("Scanning PCI bus %d for quad %d\n", + QUADLOCAL2BUS(quad,0), quad); + pci_scan_bus(QUADLOCAL2BUS(quad,0), + pci_root_ops, NULL); + } + } + return 0; +} + +subsys_initcall(pci_numa_init); diff -Nru a/arch/i386/pci/pcbios.c b/arch/i386/pci/pcbios.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/pcbios.c Tue May 7 13:31:16 2002 @@ -0,0 +1,559 @@ +/* + * BIOS32 and PCI BIOS handling. + */ + +#include +#include "pci.h" + + +#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d +#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e +#define PCIBIOS_SET_PCI_HW_INT 0xb10f + +/* BIOS32 signature: "_32_" */ +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +/* PCI BIOS hardware mechanism flags */ +#define PCIBIOS_HW_TYPE1 0x01 +#define PCIBIOS_HW_TYPE2 0x02 +#define PCIBIOS_HW_TYPE1_SPEC 0x10 +#define PCIBIOS_HW_TYPE2_SPEC 0x20 + +/* + * This is the standard structure used to identify the entry point + * to the BIOS32 Service Directory, as documented in + * Standard BIOS 32-bit Service Directory Proposal + * Revision 0.4 May 24, 1993 + * Phoenix Technologies Ltd. + * Norwood, MA + * and the PCI BIOS specification. + */ + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +/* + * Physical address of the service directory. I don't know if we're + * allowed to have more than one of these or not, so just in case + * we'll make pcibios_present() take a memory start parameter and store + * the array there. + */ + +static struct { + unsigned long address; + unsigned short segment; +} bios32_indirect = { 0, __KERNEL_CS }; + +/* + * Returns the entry point for the given service, NULL on error + */ + +static unsigned long bios32_service(unsigned long service) +{ + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + unsigned long flags; + + __save_flags(flags); __cli(); + __asm__("lcall *(%%edi); cld" + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "D" (&bios32_indirect)); + __restore_flags(flags); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + printk(KERN_WARNING "bios32_service(0x%lx): not present\n", service); + return 0; + default: /* Shouldn't happen */ + printk(KERN_WARNING "bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n", + service, return_code); + return 0; + } +} + +static struct { + unsigned long address; + unsigned short segment; +} pci_indirect = { 0, __KERNEL_CS }; + +static int pci_bios_present; + +static int __devinit check_pcibios(void) +{ + u32 signature, eax, ebx, ecx; + u8 status, major_ver, minor_ver, hw_mech; + unsigned long flags, pcibios_entry; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + pci_indirect.address = pcibios_entry + PAGE_OFFSET; + + __save_flags(flags); __cli(); + __asm__( + "lcall *(%%edi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=d" (signature), + "=a" (eax), + "=b" (ebx), + "=c" (ecx) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "D" (&pci_indirect) + : "memory"); + __restore_flags(flags); + + status = (eax >> 8) & 0xff; + hw_mech = eax & 0xff; + major_ver = (ebx >> 8) & 0xff; + minor_ver = ebx & 0xff; + if (pcibios_last_bus < 0) + pcibios_last_bus = ecx & 0xff; + DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n", + status, hw_mech, major_ver, minor_ver, pcibios_last_bus); + if (status || signature != PCI_SIGNATURE) { + printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found\n", + status, signature); + return 0; + } + printk(KERN_INFO "PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n", + major_ver, minor_ver, pcibios_entry, pcibios_last_bus); +#ifdef CONFIG_PCI_DIRECT + if (!(hw_mech & PCIBIOS_HW_TYPE1)) + pci_probe &= ~PCI_PROBE_CONF1; + if (!(hw_mech & PCIBIOS_HW_TYPE2)) + pci_probe &= ~PCI_PROBE_CONF2; +#endif + return 1; + } + return 0; +} + +static int __devinit pci_bios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, unsigned char *device_fn) +{ + unsigned short bx; + unsigned short ret; + + __asm__("lcall *(%%edi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=b" (bx), + "=a" (ret) + : "1" (PCIBIOS_FIND_PCI_DEVICE), + "c" (device_id), + "d" (vendor), + "S" ((int) index), + "D" (&pci_indirect)); + *bus = (bx >> 8) & 0xff; + *device_fn = bx & 0xff; + return (int) (ret & 0xff00) >> 8; +} + +static int pci_bios_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value) +{ + unsigned long result = 0; + unsigned long flags; + unsigned long bx = ((bus << 8) | (dev << 3) | fn); + + if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + switch (len) { + case 1: + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (result) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long)reg), + "S" (&pci_indirect)); + break; + case 2: + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (result) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long)reg), + "S" (&pci_indirect)); + break; + case 4: + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (result) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long)reg), + "S" (&pci_indirect)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return (int)((result & 0xff00) >> 8); +} + +static int pci_bios_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value) +{ + unsigned long result = 0; + unsigned long flags; + unsigned long bx = ((bus << 8) | (dev << 3) | fn); + + if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255)) + return -EINVAL; + + spin_lock_irqsave(&pci_config_lock, flags); + + switch (len) { + case 1: + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (result) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long)reg), + "S" (&pci_indirect)); + break; + case 2: + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (result) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long)reg), + "S" (&pci_indirect)); + break; + case 4: + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (result) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long)reg), + "S" (&pci_indirect)); + break; + } + + spin_unlock_irqrestore(&pci_config_lock, flags); + + return (int)((result & 0xff00) >> 8); +} + +static int pci_bios_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + int result; + u32 data; + + if (!value) + return -EINVAL; + + result = pci_bios_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, &data); + + *value = (u8)data; + + return result; +} + +static int pci_bios_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + int result; + u32 data; + + if (!value) + return -EINVAL; + + result = pci_bios_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, &data); + + *value = (u16)data; + + return result; +} + +static int pci_bios_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + if (!value) + return -EINVAL; + + return pci_bios_read(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + +static int pci_bios_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + return pci_bios_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 1, value); +} + +static int pci_bios_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + return pci_bios_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 2, value); +} + +static int pci_bios_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + return pci_bios_write(0, dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), where, 4, value); +} + + +/* + * Function table for BIOS32 access + */ + +static struct pci_ops pci_bios_access = { + pci_bios_read_config_byte, + pci_bios_read_config_word, + pci_bios_read_config_dword, + pci_bios_write_config_byte, + pci_bios_write_config_word, + pci_bios_write_config_dword +}; + +/* + * Try to find PCI BIOS. + */ + +static struct pci_ops * __devinit pci_find_bios(void) +{ + union bios32 *check; + unsigned char sum; + int i, length; + + /* + * Follow the standard procedure for locating the BIOS32 Service + * directory by scanning the permissible address range from + * 0xe0000 through 0xfffff for a valid BIOS32 structure. + */ + + for (check = (union bios32 *) __va(0xe0000); + check <= (union bios32 *) __va(0xffff0); + ++check) { + if (check->fields.signature != BIOS32_SIGNATURE) + continue; + length = check->fields.length * 16; + if (!length) + continue; + sum = 0; + for (i = 0; i < length ; ++i) + sum += check->chars[i]; + if (sum != 0) + continue; + if (check->fields.revision != 0) { + printk("PCI: unsupported BIOS32 revision %d at 0x%p\n", + check->fields.revision, check); + continue; + } + DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check); + if (check->fields.entry >= 0x100000) { + printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check); + return NULL; + } else { + unsigned long bios32_entry = check->fields.entry; + DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); + bios32_indirect.address = bios32_entry + PAGE_OFFSET; + if (check_pcibios()) + return &pci_bios_access; + } + break; /* Hopefully more than one BIOS32 cannot happen... */ + } + + return NULL; +} + +/* + * Sort the device list according to PCI BIOS. Nasty hack, but since some + * fool forgot to define the `correct' device order in the PCI BIOS specs + * and we want to be (possibly bug-to-bug ;-]) compatible with older kernels + * which used BIOS ordering, we are bound to do this... + */ + +void __devinit pcibios_sort(void) +{ + LIST_HEAD(sorted_devices); + struct list_head *ln; + struct pci_dev *dev, *d; + int idx, found; + unsigned char bus, devfn; + + DBG("PCI: Sorting device list...\n"); + while (!list_empty(&pci_devices)) { + ln = pci_devices.next; + dev = pci_dev_g(ln); + idx = found = 0; + while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { + idx++; + for (ln=pci_devices.next; ln != &pci_devices; ln=ln->next) { + d = pci_dev_g(ln); + if (d->bus->number == bus && d->devfn == devfn) { + list_del(&d->global_list); + list_add_tail(&d->global_list, &sorted_devices); + if (d == dev) + found = 1; + break; + } + } + if (ln == &pci_devices) { + printk(KERN_WARNING "PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn); + /* + * We must not continue scanning as several buggy BIOSes + * return garbage after the last device. Grr. + */ + break; + } + } + if (!found) { + printk(KERN_WARNING "PCI: Device %02x:%02x not found by BIOS\n", + dev->bus->number, dev->devfn); + list_del(&dev->global_list); + list_add_tail(&dev->global_list, &sorted_devices); + } + } + list_splice(&sorted_devices, &pci_devices); +} + +/* + * BIOS Functions for IRQ Routing + */ + +struct irq_routing_options { + u16 size; + struct irq_info *table; + u16 segment; +} __attribute__((packed)); + +struct irq_routing_table * __devinit pcibios_get_irq_routing_table(void) +{ + struct irq_routing_options opt; + struct irq_routing_table *rt = NULL; + int ret, map; + unsigned long page; + + if (!pci_bios_present) + return NULL; + page = __get_free_page(GFP_KERNEL); + if (!page) + return NULL; + opt.table = (struct irq_info *) page; + opt.size = PAGE_SIZE; + opt.segment = __KERNEL_DS; + + DBG("PCI: Fetching IRQ routing table... "); + __asm__("push %%es\n\t" + "push %%ds\n\t" + "pop %%es\n\t" + "lcall *(%%esi); cld\n\t" + "pop %%es\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret), + "=b" (map) + : "0" (PCIBIOS_GET_ROUTING_OPTIONS), + "1" (0), + "D" ((long) &opt), + "S" (&pci_indirect)); + DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map); + if (ret & 0xff00) + printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff); + else if (opt.size) { + rt = kmalloc(sizeof(struct irq_routing_table) + opt.size, GFP_KERNEL); + if (rt) { + memset(rt, 0, sizeof(struct irq_routing_table)); + rt->size = opt.size + sizeof(struct irq_routing_table); + rt->exclusive_irqs = map; + memcpy(rt->slots, (void *) page, opt.size); + printk(KERN_INFO "PCI: Using BIOS Interrupt Routing Table\n"); + } + } + free_page(page); + return rt; +} + + +int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) +{ + int ret; + + __asm__("lcall *(%%esi); cld\n\t" + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_SET_PCI_HW_INT), + "b" ((dev->bus->number << 8) | dev->devfn), + "c" ((irq << 8) | (pin + 10)), + "S" (&pci_indirect)); + return !(ret & 0xff00); +} + +static int __init pci_pcbios_init(void) +{ + if ((pci_probe & PCI_PROBE_BIOS) + && ((pci_root_ops = pci_find_bios()))) { + pci_probe |= PCI_BIOS_SORT; + pci_bios_present = 1; + pci_config_read = pci_bios_read; + pci_config_write = pci_bios_write; + } + return 0; +} + +arch_initcall(pci_pcbios_init); diff -Nru a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/pci.h Tue May 7 13:31:16 2002 @@ -0,0 +1,75 @@ +/* + * Low-Level PCI Access for i386 machines. + * + * (c) 1999 Martin Mares + */ + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 0x0001 +#define PCI_PROBE_CONF1 0x0002 +#define PCI_PROBE_CONF2 0x0004 +#define PCI_NO_SORT 0x0100 +#define PCI_BIOS_SORT 0x0200 +#define PCI_NO_CHECKS 0x0400 +#define PCI_USE_PIRQ_MASK 0x0800 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 +#define PCI_ASSIGN_ALL_BUSSES 0x4000 +#define PCI_NO_ACPI_ROUTING 0x8000 + +extern unsigned int pci_probe; + +/* pci-i386.c */ + +extern unsigned int pcibios_max_latency; + +void pcibios_resource_survey(void); +int pcibios_enable_resources(struct pci_dev *); + +/* pci-pc.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +extern int pci_use_acpi_routing; +extern spinlock_t pci_config_lock; + +void pcibios_fixup_irqs(void); +void pcibios_enable_irq(struct pci_dev *dev); + +extern int (*pci_lookup_irq)(struct pci_dev * dev, int assign); diff -Nru a/arch/i386/pci/visws.c b/arch/i386/pci/visws.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/pci/visws.c Tue May 7 13:31:16 2002 @@ -0,0 +1,141 @@ +/* + * Low-Level PCI Support for SGI Visual Workstation + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pci-i386.h" + +unsigned int pci_probe = 0; + +/* + * The VISWS uses configuration access type 1 only. + */ + +#define CONFIG_CMD(dev, where) (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3)) + +static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inb(0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_word(struct pci_dev *dev, int where, u16 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inw(0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config_dword(struct pci_dev *dev, int where, u32 *value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + *value = inl(0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_byte(struct pci_dev *dev, int where, u8 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outb(value, 0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_word(struct pci_dev *dev, int where, u16 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outw(value, 0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config_dword(struct pci_dev *dev, int where, u32 value) +{ + outl(CONFIG_CMD(dev,where), 0xCF8); + outl(value, 0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +#undef CONFIG_CMD + +static struct pci_ops visws_pci_ops = { + pci_conf1_read_config_byte, + pci_conf1_read_config_word, + pci_conf1_read_config_dword, + pci_conf1_write_config_byte, + pci_conf1_write_config_word, + pci_conf1_write_config_dword +}; + +static void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev, *p; + u8 pin; + int irq; + + pci_for_each_dev(dev) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + dev->irq = 0; + if (!pin) + continue; + pin--; + if (dev->bus->parent) { + p = dev->bus->parent->self; + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + } else + p = dev; + irq = visws_get_PCI_irq_vector(p->bus->number, PCI_SLOT(p->devfn), pin+1); + if (irq >= 0) + dev->irq = irq; + DBG("PCI IRQ: %s pin %d -> %d\n", dev->slot_name, pin, irq); + } +} + +void __init pcibios_fixup_bus(struct pci_bus *b) +{ + pci_read_bridge_bases(b); +} + +#if 0 +static struct resource visws_pci_bus_resources[2] = { + { "Host bus 1", 0xf4000000, 0xf7ffffff, 0 }, + { "Host bus 2", 0xf0000000, 0xf3ffffff, 0 } +}; +#endif + +void __init pcibios_init(void) +{ + unsigned int sec_bus = li_pcib_read16(LI_PCI_BUSNUM) & 0xff; + + printk("PCI: Probing PCI hardware on host buses 00 and %02x\n", sec_bus); + pci_scan_bus(0, &visws_pci_ops, NULL); + pci_scan_bus(sec_bus, &visws_pci_ops, NULL); + pcibios_fixup_irqs(); + pcibios_resource_survey(); +} + +char * __init pcibios_setup(char *str) +{ + return str; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + return pcibios_enable_resources(dev); +} + +void __init pcibios_penalize_isa_irq(irq) +{ +} diff -Nru a/drivers/acpi/acpi_osl.c b/drivers/acpi/acpi_osl.c --- a/drivers/acpi/acpi_osl.c Tue May 7 13:31:16 2002 +++ b/drivers/acpi/acpi_osl.c Tue May 7 13:31:16 2002 @@ -77,12 +77,13 @@ * Initialize PCI configuration space access, as we'll need to access * it while walking the namespace (bus 0 and root bridges w/ _BBNs). */ +#if 0 pcibios_config_init(); if (!pci_config_read || !pci_config_write) { printk(KERN_ERR PREFIX "Access to PCI configuration space unavailable\n"); return AE_NULL_ENTRY; } - +#endif return AE_OK; } diff -Nru a/drivers/hotplug/cpqphp_core.c b/drivers/hotplug/cpqphp_core.c --- a/drivers/hotplug/cpqphp_core.c Tue May 7 13:31:16 2002 +++ b/drivers/hotplug/cpqphp_core.c Tue May 7 13:31:16 2002 @@ -38,7 +38,7 @@ #include #include "cpqphp.h" #include "cpqphp_nvram.h" -#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */ +#include "../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */ /* Global variables */ diff -Nru a/drivers/hotplug/cpqphp_pci.c b/drivers/hotplug/cpqphp_pci.c --- a/drivers/hotplug/cpqphp_pci.c Tue May 7 13:31:16 2002 +++ b/drivers/hotplug/cpqphp_pci.c Tue May 7 13:31:16 2002 @@ -35,7 +35,7 @@ #include #include "cpqphp.h" #include "cpqphp_nvram.h" -#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */ +#include "../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */ u8 cpqhp_nic_irq; diff -Nru a/drivers/hotplug/ibmphp_core.c b/drivers/hotplug/ibmphp_core.c --- a/drivers/hotplug/ibmphp_core.c Tue May 7 13:31:16 2002 +++ b/drivers/hotplug/ibmphp_core.c Tue May 7 13:31:16 2002 @@ -35,7 +35,7 @@ #include #include #include -#include "../../arch/i386/kernel/pci-i386.h" /* for struct irq_routing_table */ +#include "../../arch/i386/pci/pci.h" /* for struct irq_routing_table */ #include "ibmphp.h" #define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON) diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile --- a/drivers/pci/Makefile Tue May 7 13:31:16 2002 +++ b/drivers/pci/Makefile Tue May 7 13:31:16 2002 @@ -11,9 +11,11 @@ O_TARGET := driver.o -export-objs := pci.o +obj-y += access.o probe.o pci.o pool.o quirks.o \ + compat.o names.o pci-driver.o search.o +obj-$(CONFIG_PM) += power.o +obj-$(CONFIG_HOTPLUG) += hotplug.o -obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o pci-driver.o obj-$(CONFIG_PROC_FS) += proc.o ifndef CONFIG_SPARC64 @@ -34,6 +36,8 @@ ifndef CONFIG_X86 obj-y += syscall.o endif + +export-objs := $(obj-y) include $(TOPDIR)/Rules.make diff -Nru a/drivers/pci/access.c b/drivers/pci/access.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/access.c Tue May 7 13:31:16 2002 @@ -0,0 +1,46 @@ +#include +#include +#include + +/* + * This interrupt-safe spinlock protects all accesses to PCI + * configuration space. + */ + +static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED; + +/* + * Wrappers for all PCI configuration access functions. They just check + * alignment, do locking and call the low-level functions pointed to + * by pci_dev->ops. + */ + +#define PCI_byte_BAD 0 +#define PCI_word_BAD (pos & 1) +#define PCI_dword_BAD (pos & 3) + +#define PCI_OP(rw,size,type) \ +int pci_##rw##_config_##size (struct pci_dev *dev, int pos, type value) \ +{ \ + int res; \ + unsigned long flags; \ + if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ + spin_lock_irqsave(&pci_lock, flags); \ + res = dev->bus->ops->rw##_##size(dev, pos, value); \ + spin_unlock_irqrestore(&pci_lock, flags); \ + return res; \ +} + +PCI_OP(read, byte, u8 *) +PCI_OP(read, word, u16 *) +PCI_OP(read, dword, u32 *) +PCI_OP(write, byte, u8) +PCI_OP(write, word, u16) +PCI_OP(write, dword, u32) + +EXPORT_SYMBOL(pci_read_config_byte); +EXPORT_SYMBOL(pci_read_config_word); +EXPORT_SYMBOL(pci_read_config_dword); +EXPORT_SYMBOL(pci_write_config_byte); +EXPORT_SYMBOL(pci_write_config_word); +EXPORT_SYMBOL(pci_write_config_dword); diff -Nru a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/hotplug.c Tue May 7 13:31:16 2002 @@ -0,0 +1,132 @@ +#include +#include +#include /* for hotplug_path */ + +extern struct list_head pci_drivers; +extern int pci_announce_device(struct pci_driver *drv, struct pci_dev *dev); + +#ifndef FALSE +#define FALSE (0) +#define TRUE (!FALSE) +#endif + +static void +run_sbin_hotplug(struct pci_dev *pdev, int insert) +{ + int i; + char *argv[3], *envp[8]; + char id[20], sub_id[24], bus_id[24], class_id[20]; + + if (!hotplug_path[0]) + return; + + sprintf(class_id, "PCI_CLASS=%04X", pdev->class); + sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device); + sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device); + sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name); + + i = 0; + argv[i++] = hotplug_path; + argv[i++] = "pci"; + argv[i] = 0; + + i = 0; + /* minimal command environment */ + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + /* other stuff we want to pass to /sbin/hotplug */ + envp[i++] = class_id; + envp[i++] = id; + envp[i++] = sub_id; + envp[i++] = bus_id; + if (insert) + envp[i++] = "ACTION=add"; + else + envp[i++] = "ACTION=remove"; + envp[i] = 0; + + call_usermodehelper (argv [0], argv, envp); +} + +/** + * pci_announce_device_to_drivers - tell the drivers a new device has appeared + * @dev: the device that has shown up + * + * Notifys the drivers that a new device has appeared, and also notifys + * userspace through /sbin/hotplug. + */ +void +pci_announce_device_to_drivers(struct pci_dev *dev) +{ + struct list_head *ln; + + for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) { + struct pci_driver *drv = list_entry(ln, struct pci_driver, node); + if (drv->remove && pci_announce_device(drv, dev)) + break; + } + + /* notify userspace of new hotplug device */ + run_sbin_hotplug(dev, TRUE); +} + +/** + * pci_insert_device - insert a hotplug device + * @dev: the device to insert + * @bus: where to insert it + * + * Add a new device to the device lists and notify userspace (/sbin/hotplug). + */ +void +pci_insert_device(struct pci_dev *dev, struct pci_bus *bus) +{ + list_add_tail(&dev->bus_list, &bus->devices); + list_add_tail(&dev->global_list, &pci_devices); +#ifdef CONFIG_PROC_FS + pci_proc_attach_device(dev); +#endif + pci_announce_device_to_drivers(dev); +} + +static void +pci_free_resources(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource *res = dev->resource + i; + if (res->parent) + release_resource(res); + } +} + +/** + * pci_remove_device - remove a hotplug device + * @dev: the device to remove + * + * Delete the device structure from the device lists and + * notify userspace (/sbin/hotplug). + */ +void +pci_remove_device(struct pci_dev *dev) +{ + if (dev->driver) { + if (dev->driver->remove) + dev->driver->remove(dev); + dev->driver = NULL; + } + list_del(&dev->bus_list); + list_del(&dev->global_list); + pci_free_resources(dev); +#ifdef CONFIG_PROC_FS + pci_proc_detach_device(dev); +#endif + + /* notify userspace of hotplug device removal */ + run_sbin_hotplug(dev, FALSE); +} + +EXPORT_SYMBOL(pci_insert_device); +EXPORT_SYMBOL(pci_remove_device); +EXPORT_SYMBOL(pci_announce_device_to_drivers); diff -Nru a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c --- a/drivers/pci/pci-driver.c Tue May 7 13:31:16 2002 +++ b/drivers/pci/pci-driver.c Tue May 7 13:31:16 2002 @@ -1,9 +1,138 @@ /* - * drivers/pci/pci-driver.c - default PCI driver. + * drivers/pci/pci-driver.c * */ #include +#include + +/* + * Registration of PCI drivers and handling of hot-pluggable devices. + */ + +LIST_HEAD(pci_drivers); + +/** + * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure + * @ids: array of PCI device id structures to search in + * @dev: the PCI device structure to match against + * + * Used by a driver to check whether a PCI device present in the + * system is in its list of supported devices.Returns the matching + * pci_device_id structure or %NULL if there is no match. + */ +const struct pci_device_id * +pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) +{ + while (ids->vendor || ids->subvendor || ids->class_mask) { + if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) && + (ids->device == PCI_ANY_ID || ids->device == dev->device) && + (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) && + (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) && + !((ids->class ^ dev->class) & ids->class_mask)) + return ids; + ids++; + } + return NULL; +} + +int +pci_announce_device(struct pci_driver *drv, struct pci_dev *dev) +{ + const struct pci_device_id *id; + int ret = 0; + + if (drv->id_table) { + id = pci_match_device(drv->id_table, dev); + if (!id) { + ret = 0; + goto out; + } + } else + id = NULL; + + dev_probe_lock(); + if (drv->probe(dev, id) >= 0) { + dev->driver = drv; + ret = 1; + } + dev_probe_unlock(); +out: + return ret; +} + +/** + * pci_register_driver - register a new pci driver + * @drv: the driver structure to register + * + * Adds the driver structure to the list of registered drivers + * Returns the number of pci devices which were claimed by the driver + * during registration. The driver remains registered even if the + * return value is zero. + */ +int +pci_register_driver(struct pci_driver *drv) +{ + struct pci_dev *dev; + int count = 0; + + list_add_tail(&drv->node, &pci_drivers); + pci_for_each_dev(dev) { + if (!pci_dev_driver(dev)) + count += pci_announce_device(drv, dev); + } + return count; +} + +/** + * pci_unregister_driver - unregister a pci driver + * @drv: the driver structure to unregister + * + * Deletes the driver structure from the list of registered PCI drivers, + * gives it a chance to clean up by calling its remove() function for + * each device it was responsible for, and marks those devices as + * driverless. + */ + +void +pci_unregister_driver(struct pci_driver *drv) +{ + struct pci_dev *dev; + + list_del(&drv->node); + pci_for_each_dev(dev) { + if (dev->driver == drv) { + if (drv->remove) + drv->remove(dev); + dev->driver = NULL; + } + } +} + +static struct pci_driver pci_compat_driver = { + name: "compat" +}; + +/** + * pci_dev_driver - get the pci_driver of a device + * @dev: the device to query + * + * Returns the appropriate pci_driver structure or %NULL if there is no + * registered driver for the device. + */ +struct pci_driver * +pci_dev_driver(const struct pci_dev *dev) +{ + if (dev->driver) + return dev->driver; + else { + int i; + for(i=0; i<=PCI_ROM_RESOURCE; i++) + if (dev->resource[i].flags & IORESOURCE_BUSY) + return &pci_compat_driver; + } + return NULL; +} static int pci_device_suspend(struct device * dev, u32 state, u32 level) { @@ -34,3 +163,8 @@ suspend: pci_device_suspend, resume: pci_device_resume, }; + +EXPORT_SYMBOL(pci_match_device); +EXPORT_SYMBOL(pci_register_driver); +EXPORT_SYMBOL(pci_unregister_driver); +EXPORT_SYMBOL(pci_dev_driver); diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c --- a/drivers/pci/pci.c Tue May 7 13:31:16 2002 +++ b/drivers/pci/pci.c Tue May 7 13:31:16 2002 @@ -9,23 +9,11 @@ * Copyright 1997 -- 2000 Martin Mares */ -#include -#include -#include -#include -#include -#include +#include #include -#include -#include +#include +#include #include -#include -#include /* for hotplug_path */ -#include -#include -#include - -#include #include /* isa_dma_bridge_buggy */ #undef DEBUG @@ -36,114 +24,6 @@ #define DBG(x...) #endif -LIST_HEAD(pci_root_buses); -LIST_HEAD(pci_devices); - -extern struct device_driver pci_device_driver; - -/** - * pci_find_slot - locate PCI device from a given PCI slot - * @bus: number of PCI bus on which desired PCI device resides - * @devfn: encodes number of PCI slot in which the desired PCI - * device resides and the logical device number within that slot - * in case of multi-function devices. - * - * Given a PCI bus and slot/function number, the desired PCI device - * is located in system global list of PCI devices. If the device - * is found, a pointer to its data structure is returned. If no - * device is found, %NULL is returned. - */ -struct pci_dev * -pci_find_slot(unsigned int bus, unsigned int devfn) -{ - struct pci_dev *dev; - - pci_for_each_dev(dev) { - if (dev->bus->number == bus && dev->devfn == devfn) - return dev; - } - return NULL; -} - -/** - * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id - * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids - * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids - * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids - * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids - * @from: Previous PCI device found in search, or %NULL for new search. - * - * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its - * device structure is returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device on the global list. - */ -struct pci_dev * -pci_find_subsys(unsigned int vendor, unsigned int device, - unsigned int ss_vendor, unsigned int ss_device, - const struct pci_dev *from) -{ - struct list_head *n = from ? from->global_list.next : pci_devices.next; - - while (n != &pci_devices) { - struct pci_dev *dev = pci_dev_g(n); - if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && - (device == PCI_ANY_ID || dev->device == device) && - (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) && - (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)) - return dev; - n = n->next; - } - return NULL; -} - - -/** - * pci_find_device - begin or continue searching for a PCI device by vendor/device id - * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids - * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids - * @from: Previous PCI device found in search, or %NULL for new search. - * - * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @vendor and @device, a pointer to its device structure is - * returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device on the global list. - */ -struct pci_dev * -pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from) -{ - return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); -} - - -/** - * pci_find_class - begin or continue searching for a PCI device by class - * @class: search for a PCI device with this class designation - * @from: Previous PCI device found in search, or %NULL for new search. - * - * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @class, a pointer to its device structure is - * returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device - * on the global list. - */ -struct pci_dev * -pci_find_class(unsigned int class, const struct pci_dev *from) -{ - struct list_head *n = from ? from->global_list.next : pci_devices.next; - - while (n != &pci_devices) { - struct pci_dev *dev = pci_dev_g(n); - if (dev->class == class) - return dev; - n = n->next; - } - return NULL; -} - /** * pci_find_capability - query for devices' capabilities * @dev: PCI device to query @@ -536,298 +416,6 @@ return -EBUSY; } - -/* - * Registration of PCI drivers and handling of hot-pluggable devices. - */ - -static LIST_HEAD(pci_drivers); - -/** - * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure - * @ids: array of PCI device id structures to search in - * @dev: the PCI device structure to match against - * - * Used by a driver to check whether a PCI device present in the - * system is in its list of supported devices.Returns the matching - * pci_device_id structure or %NULL if there is no match. - */ -const struct pci_device_id * -pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) -{ - while (ids->vendor || ids->subvendor || ids->class_mask) { - if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) && - (ids->device == PCI_ANY_ID || ids->device == dev->device) && - (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) && - (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) && - !((ids->class ^ dev->class) & ids->class_mask)) - return ids; - ids++; - } - return NULL; -} - -static int -pci_announce_device(struct pci_driver *drv, struct pci_dev *dev) -{ - const struct pci_device_id *id; - int ret = 0; - - if (drv->id_table) { - id = pci_match_device(drv->id_table, dev); - if (!id) { - ret = 0; - goto out; - } - } else - id = NULL; - - dev_probe_lock(); - if (drv->probe(dev, id) >= 0) { - dev->driver = drv; - ret = 1; - } - dev_probe_unlock(); -out: - return ret; -} - -/** - * pci_register_driver - register a new pci driver - * @drv: the driver structure to register - * - * Adds the driver structure to the list of registered drivers - * Returns the number of pci devices which were claimed by the driver - * during registration. The driver remains registered even if the - * return value is zero. - */ -int -pci_register_driver(struct pci_driver *drv) -{ - struct pci_dev *dev; - int count = 0; - - list_add_tail(&drv->node, &pci_drivers); - pci_for_each_dev(dev) { - if (!pci_dev_driver(dev)) - count += pci_announce_device(drv, dev); - } - return count; -} - -/** - * pci_unregister_driver - unregister a pci driver - * @drv: the driver structure to unregister - * - * Deletes the driver structure from the list of registered PCI drivers, - * gives it a chance to clean up by calling its remove() function for - * each device it was responsible for, and marks those devices as - * driverless. - */ - -void -pci_unregister_driver(struct pci_driver *drv) -{ - struct pci_dev *dev; - - list_del(&drv->node); - pci_for_each_dev(dev) { - if (dev->driver == drv) { - if (drv->remove) - drv->remove(dev); - dev->driver = NULL; - } - } -} - -#ifdef CONFIG_HOTPLUG - -#ifndef FALSE -#define FALSE (0) -#define TRUE (!FALSE) -#endif - -static void -run_sbin_hotplug(struct pci_dev *pdev, int insert) -{ - int i; - char *argv[3], *envp[8]; - char id[20], sub_id[24], bus_id[24], class_id[20]; - - if (!hotplug_path[0]) - return; - - sprintf(class_id, "PCI_CLASS=%04X", pdev->class); - sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device); - sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device); - sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name); - - i = 0; - argv[i++] = hotplug_path; - argv[i++] = "pci"; - argv[i] = 0; - - i = 0; - /* minimal command environment */ - envp[i++] = "HOME=/"; - envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - - /* other stuff we want to pass to /sbin/hotplug */ - envp[i++] = class_id; - envp[i++] = id; - envp[i++] = sub_id; - envp[i++] = bus_id; - if (insert) - envp[i++] = "ACTION=add"; - else - envp[i++] = "ACTION=remove"; - envp[i] = 0; - - call_usermodehelper (argv [0], argv, envp); -} - -/** - * pci_announce_device_to_drivers - tell the drivers a new device has appeared - * @dev: the device that has shown up - * - * Notifys the drivers that a new device has appeared, and also notifys - * userspace through /sbin/hotplug. - */ -void -pci_announce_device_to_drivers(struct pci_dev *dev) -{ - struct list_head *ln; - - for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) { - struct pci_driver *drv = list_entry(ln, struct pci_driver, node); - if (drv->remove && pci_announce_device(drv, dev)) - break; - } - - /* notify userspace of new hotplug device */ - run_sbin_hotplug(dev, TRUE); -} - -/** - * pci_insert_device - insert a hotplug device - * @dev: the device to insert - * @bus: where to insert it - * - * Add a new device to the device lists and notify userspace (/sbin/hotplug). - */ -void -pci_insert_device(struct pci_dev *dev, struct pci_bus *bus) -{ - list_add_tail(&dev->bus_list, &bus->devices); - list_add_tail(&dev->global_list, &pci_devices); -#ifdef CONFIG_PROC_FS - pci_proc_attach_device(dev); -#endif - pci_announce_device_to_drivers(dev); -} - -static void -pci_free_resources(struct pci_dev *dev) -{ - int i; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *res = dev->resource + i; - if (res->parent) - release_resource(res); - } -} - -/** - * pci_remove_device - remove a hotplug device - * @dev: the device to remove - * - * Delete the device structure from the device lists and - * notify userspace (/sbin/hotplug). - */ -void -pci_remove_device(struct pci_dev *dev) -{ - if (dev->driver) { - if (dev->driver->remove) - dev->driver->remove(dev); - dev->driver = NULL; - } - list_del(&dev->bus_list); - list_del(&dev->global_list); - pci_free_resources(dev); -#ifdef CONFIG_PROC_FS - pci_proc_detach_device(dev); -#endif - - /* notify userspace of hotplug device removal */ - run_sbin_hotplug(dev, FALSE); -} - -#endif - -static struct pci_driver pci_compat_driver = { - name: "compat" -}; - -/** - * pci_dev_driver - get the pci_driver of a device - * @dev: the device to query - * - * Returns the appropriate pci_driver structure or %NULL if there is no - * registered driver for the device. - */ -struct pci_driver * -pci_dev_driver(const struct pci_dev *dev) -{ - if (dev->driver) - return dev->driver; - else { - int i; - for(i=0; i<=PCI_ROM_RESOURCE; i++) - if (dev->resource[i].flags & IORESOURCE_BUSY) - return &pci_compat_driver; - } - return NULL; -} - - -/* - * This interrupt-safe spinlock protects all accesses to PCI - * configuration space. - */ - -static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED; - -/* - * Wrappers for all PCI configuration access functions. They just check - * alignment, do locking and call the low-level functions pointed to - * by pci_dev->ops. - */ - -#define PCI_byte_BAD 0 -#define PCI_word_BAD (pos & 1) -#define PCI_dword_BAD (pos & 3) - -#define PCI_OP(rw,size,type) \ -int pci_##rw##_config_##size (struct pci_dev *dev, int pos, type value) \ -{ \ - int res; \ - unsigned long flags; \ - if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ - spin_lock_irqsave(&pci_lock, flags); \ - res = dev->bus->ops->rw##_##size(dev, pos, value); \ - spin_unlock_irqrestore(&pci_lock, flags); \ - return res; \ -} - -PCI_OP(read, byte, u8 *) -PCI_OP(read, word, u16 *) -PCI_OP(read, dword, u32 *) -PCI_OP(write, byte, u8) -PCI_OP(write, word, u16) -PCI_OP(write, dword, u32) - /** * pci_set_master - enables bus-mastering for device dev * @dev: the PCI device to enable @@ -966,1080 +554,14 @@ return 0; } - -/* - * Translate the low bits of the PCI base - * to the resource type - */ -static inline unsigned int pci_calc_resource_flags(unsigned int flags) -{ - if (flags & PCI_BASE_ADDRESS_SPACE_IO) - return IORESOURCE_IO; - - if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) - return IORESOURCE_MEM | IORESOURCE_PREFETCH; - - return IORESOURCE_MEM; -} - -/* - * Find the extent of a PCI decode.. - */ -static u32 pci_size(u32 base, unsigned long mask) -{ - u32 size = mask & base; /* Find the significant bits */ - size = size & ~(size-1); /* Get the lowest of them to find the decode size */ - return size-1; /* extent = size - 1 */ -} - -static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) -{ - unsigned int pos, reg, next; - u32 l, sz; - struct resource *res; - - for(pos=0; posresource[pos]; - res->name = dev->name; - reg = PCI_BASE_ADDRESS_0 + (pos << 2); - pci_read_config_dword(dev, reg, &l); - pci_write_config_dword(dev, reg, ~0); - pci_read_config_dword(dev, reg, &sz); - pci_write_config_dword(dev, reg, l); - if (!sz || sz == 0xffffffff) - continue; - if (l == 0xffffffff) - l = 0; - if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { - res->start = l & PCI_BASE_ADDRESS_MEM_MASK; - res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; - sz = pci_size(sz, PCI_BASE_ADDRESS_MEM_MASK); - } else { - res->start = l & PCI_BASE_ADDRESS_IO_MASK; - res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; - sz = pci_size(sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); - } - res->end = res->start + (unsigned long) sz; - res->flags |= pci_calc_resource_flags(l); - if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) - == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) { - pci_read_config_dword(dev, reg+4, &l); - next++; -#if BITS_PER_LONG == 64 - res->start |= ((unsigned long) l) << 32; - res->end = res->start + sz; - pci_write_config_dword(dev, reg+4, ~0); - pci_read_config_dword(dev, reg+4, &sz); - pci_write_config_dword(dev, reg+4, l); - if (~sz) - res->end = res->start + 0xffffffff + - (((unsigned long) ~sz) << 32); -#else - if (l) { - printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name); - res->start = 0; - res->flags = 0; - continue; - } -#endif - } - } - if (rom) { - dev->rom_base_reg = rom; - res = &dev->resource[PCI_ROM_RESOURCE]; - pci_read_config_dword(dev, rom, &l); - pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE); - pci_read_config_dword(dev, rom, &sz); - pci_write_config_dword(dev, rom, l); - if (l == 0xffffffff) - l = 0; - if (sz && sz != 0xffffffff) { - res->flags = (l & PCI_ROM_ADDRESS_ENABLE) | - IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_READONLY | IORESOURCE_CACHEABLE; - res->start = l & PCI_ROM_ADDRESS_MASK; - sz = pci_size(sz, PCI_ROM_ADDRESS_MASK); - res->end = res->start + (unsigned long) sz; - } - res->name = dev->name; - } -} - -void __devinit pci_read_bridge_bases(struct pci_bus *child) -{ - struct pci_dev *dev = child->self; - u8 io_base_lo, io_limit_lo; - u16 mem_base_lo, mem_limit_lo; - unsigned long base, limit; - struct resource *res; - int i; - - if (!dev) /* It's a host bus, nothing to read */ - return; - - for(i=0; i<3; i++) - child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; - - res = child->resource[0]; - pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); - pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; - limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; - - if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { - u16 io_base_hi, io_limit_hi; - pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); - pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); - base |= (io_base_hi << 16); - limit |= (io_limit_hi << 16); - } - - if (base && base <= limit) { - res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; - res->start = base; - res->end = limit + 0xfff; - res->name = child->name; - } else { - /* - * Ugh. We don't know enough about this bridge. Just assume - * that it's entirely transparent. - */ - printk(KERN_ERR "Unknown bridge resource %d: assuming transparent\n", 0); - child->resource[0] = child->parent->resource[0]; - } - - res = child->resource[1]; - pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); - pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); - base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; - limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; - if (base && base <= limit) { - res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; - res->start = base; - res->end = limit + 0xfffff; - res->name = child->name; - } else { - /* See comment above. Same thing */ - printk(KERN_ERR "Unknown bridge resource %d: assuming transparent\n", 1); - child->resource[1] = child->parent->resource[1]; - } - - res = child->resource[2]; - pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); - pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); - base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; - limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; - - if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { - u32 mem_base_hi, mem_limit_hi; - pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); - pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); -#if BITS_PER_LONG == 64 - base |= ((long) mem_base_hi) << 32; - limit |= ((long) mem_limit_hi) << 32; -#else - if (mem_base_hi || mem_limit_hi) { - printk(KERN_ERR "PCI: Unable to handle 64-bit address space for %s\n", child->name); - return; - } -#endif - } - if (base && base <= limit) { - res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; - res->start = base; - res->end = limit + 0xfffff; - res->name = child->name; - } else { - /* See comments above */ - printk(KERN_ERR "Unknown bridge resource %d: assuming transparent\n", 2); - child->resource[2] = child->parent->resource[2]; - } -} - -static struct pci_bus * __devinit pci_alloc_bus(void) -{ - struct pci_bus *b; - - b = kmalloc(sizeof(*b), GFP_KERNEL); - if (b) { - memset(b, 0, sizeof(*b)); - INIT_LIST_HEAD(&b->children); - INIT_LIST_HEAD(&b->devices); - } - return b; -} - -struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) -{ - struct pci_bus *child; - int i; - - /* - * Allocate a new bus, and inherit stuff from the parent.. - */ - child = pci_alloc_bus(); - - list_add_tail(&child->node, &parent->children); - child->self = dev; - dev->subordinate = child; - child->parent = parent; - child->ops = parent->ops; - child->sysdata = parent->sysdata; - child->dev = &dev->dev; - - /* - * Set up the primary, secondary and subordinate - * bus numbers. - */ - child->number = child->secondary = busnr; - child->primary = parent->secondary; - child->subordinate = 0xff; - - /* Set up default resource pointers.. */ - for (i = 0; i < 4; i++) - child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; - - return child; -} - -unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus); - -/* - * If it's a bridge, configure it and scan the bus behind it. - * For CardBus bridges, we don't scan behind as the devices will - * be handled by the bridge driver itself. - * - * We need to process bridges in two passes -- first we scan those - * already configured by the BIOS and after we are done with all of - * them, we proceed to assigning numbers to the remaining buses in - * order to avoid overlaps between old and new bus numbers. - */ -static int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass) -{ - unsigned int buses; - unsigned short cr; - struct pci_bus *child; - int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); - - pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); - DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", dev->slot_name, buses & 0xffffff, pass); - if ((buses & 0xffff00) && !pcibios_assign_all_busses()) { - /* - * Bus already configured by firmware, process it in the first - * pass and just note the configuration. - */ - if (pass) - return max; - child = pci_add_new_bus(bus, dev, 0); - child->primary = buses & 0xFF; - child->secondary = (buses >> 8) & 0xFF; - child->subordinate = (buses >> 16) & 0xFF; - child->number = child->secondary; - if (!is_cardbus) { - unsigned int cmax = pci_do_scan_bus(child); - if (cmax > max) max = cmax; - } else { - unsigned int cmax = child->subordinate; - if (cmax > max) max = cmax; - } - } else { - /* - * We need to assign a number to this bus which we always - * do in the second pass. We also keep all address decoders - * on the bridge disabled during scanning. FIXME: Why? - */ - if (!pass) - return max; - pci_read_config_word(dev, PCI_COMMAND, &cr); - pci_write_config_word(dev, PCI_COMMAND, 0x0000); - pci_write_config_word(dev, PCI_STATUS, 0xffff); - - child = pci_add_new_bus(bus, dev, ++max); - buses = (buses & 0xff000000) - | ((unsigned int)(child->primary) << 0) - | ((unsigned int)(child->secondary) << 8) - | ((unsigned int)(child->subordinate) << 16); - /* - * We need to blast all three values with a single write. - */ - pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); - if (!is_cardbus) { - /* Now we can scan all subordinate buses... */ - max = pci_do_scan_bus(child); - } else { - /* - * For CardBus bridges, we leave 4 bus numbers - * as cards with a PCI-to-PCI bridge can be - * inserted later. - */ - max += 3; - } - /* - * Set the subordinate bus number to its real value. - */ - child->subordinate = max; - pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); - pci_write_config_word(dev, PCI_COMMAND, cr); - } - sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number); - - return max; -} - -/* - * Read interrupt line and base address registers. - * The architecture-dependent code can tweak these, of course. - */ -static void pci_read_irq(struct pci_dev *dev) -{ - unsigned char irq; - - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); - if (irq) - pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); - dev->irq = irq; -} - -/** - * pci_setup_device - fill in class and map information of a device - * @dev: the device structure to fill - * - * Initialize the device structure with information about the device's - * vendor,class,memory and IO-space addresses,IRQ lines etc. - * Called at initialisation of the PCI subsystem and by CardBus services. - * Returns 0 on success and -1 if unknown type of device (not normal, bridge - * or CardBus). - */ -int pci_setup_device(struct pci_dev * dev) -{ - u32 class; - - sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device); - - pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); - class >>= 8; /* upper 3 bytes */ - dev->class = class; - class >>= 8; - - DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, dev->devfn, dev->vendor, dev->device, class, dev->hdr_type); - - /* "Unknown power state" */ - dev->current_state = 4; - - switch (dev->hdr_type) { /* header type */ - case PCI_HEADER_TYPE_NORMAL: /* standard header */ - if (class == PCI_CLASS_BRIDGE_PCI) - goto bad; - pci_read_irq(dev); - pci_read_bases(dev, 6, PCI_ROM_ADDRESS); - pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); - pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); - break; - - case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ - if (class != PCI_CLASS_BRIDGE_PCI) - goto bad; - pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); - break; - - case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ - if (class != PCI_CLASS_BRIDGE_CARDBUS) - goto bad; - pci_read_irq(dev); - pci_read_bases(dev, 1, 0); - pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); - pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device); - break; - - default: /* unknown header */ - printk(KERN_ERR "PCI: device %s has unknown header type %02x, ignoring.\n", - dev->slot_name, dev->hdr_type); - return -1; - - bad: - printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n", - dev->slot_name, class, dev->hdr_type); - dev->class = PCI_CLASS_NOT_DEFINED; - } - - /* We found a fine healthy device, go go go... */ - return 0; -} - -/* - * Read the config data for a PCI device, sanity-check it - * and fill in the dev structure... - */ -struct pci_dev * __devinit pci_scan_device(struct pci_dev *temp) -{ - struct pci_dev *dev; - u32 l; - - if (pci_read_config_dword(temp, PCI_VENDOR_ID, &l)) - return NULL; - - /* some broken boards return 0 or ~0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) - return NULL; - - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - memcpy(dev, temp, sizeof(*dev)); - dev->vendor = l & 0xffff; - dev->device = (l >> 16) & 0xffff; - - /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) - set this higher, assuming the system even supports it. */ - dev->dma_mask = 0xffffffff; - if (pci_setup_device(dev) < 0) { - kfree(dev); - return NULL; - } - - /* now put in global tree */ - strcpy(dev->dev.name,dev->name); - strcpy(dev->dev.bus_id,dev->slot_name); - - device_register(&dev->dev); - return dev; -} - -struct pci_dev * __devinit pci_scan_slot(struct pci_dev *temp) -{ - struct pci_bus *bus = temp->bus; - struct pci_dev *dev; - struct pci_dev *first_dev = NULL; - int func = 0; - int is_multi = 0; - u8 hdr_type; - - for (func = 0; func < 8; func++, temp->devfn++) { - if (func && !is_multi) /* not a multi-function device */ - continue; - if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) - continue; - temp->hdr_type = hdr_type & 0x7f; - - dev = pci_scan_device(temp); - if (!dev) - continue; - pci_name_device(dev); - if (!func) { - is_multi = hdr_type & 0x80; - first_dev = dev; - } - - /* - * Link the device to both the global PCI device chain and - * the per-bus list of devices. - */ - list_add_tail(&dev->global_list, &pci_devices); - list_add_tail(&dev->bus_list, &bus->devices); - - /* Fix up broken headers */ - pci_fixup_device(PCI_FIXUP_HEADER, dev); - } - return first_dev; -} - -unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus) -{ - unsigned int devfn, max, pass; - struct list_head *ln; - struct pci_dev *dev, dev0; - - DBG("Scanning bus %02x\n", bus->number); - max = bus->secondary; - - /* Create a device template */ - memset(&dev0, 0, sizeof(dev0)); - dev0.bus = bus; - dev0.sysdata = bus->sysdata; - dev0.dev.parent = bus->dev; - dev0.dev.driver = &pci_device_driver; - - /* Go find them, Rover! */ - for (devfn = 0; devfn < 0x100; devfn += 8) { - dev0.devfn = devfn; - pci_scan_slot(&dev0); - } - - /* - * After performing arch-dependent fixup of the bus, look behind - * all PCI-to-PCI bridges on this bus. - */ - DBG("Fixups for bus %02x\n", bus->number); - pcibios_fixup_bus(bus); - for (pass=0; pass < 2; pass++) - for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) { - dev = pci_dev_b(ln); - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - max = pci_scan_bridge(bus, dev, max, pass); - } - - /* - * We've scanned the bus and so we know all about what's on - * the other side of any bridges that may be on this bus plus - * any devices. - * - * Return how far we've got finding sub-buses. - */ - DBG("Bus scan for %02x returning with max=%02x\n", bus->number, max); - return max; -} - -int __devinit pci_bus_exists(const struct list_head *list, int nr) -{ - const struct list_head *l; - - for(l=list->next; l != list; l = l->next) { - const struct pci_bus *b = pci_bus_b(l); - if (b->number == nr || pci_bus_exists(&b->children, nr)) - return 1; - } - return 0; -} - -struct pci_bus * __devinit pci_alloc_primary_bus(int bus) -{ - struct pci_bus *b; - - if (pci_bus_exists(&pci_root_buses, bus)) { - /* If we already got to this bus through a different bridge, ignore it */ - DBG("PCI: Bus %02x already known\n", bus); - return NULL; - } - - b = pci_alloc_bus(); - if (!b) - return NULL; - list_add_tail(&b->node, &pci_root_buses); - - b->dev = kmalloc(sizeof(*(b->dev)),GFP_KERNEL); - memset(b->dev,0,sizeof(*(b->dev))); - sprintf(b->dev->bus_id,"pci%d",bus); - strcpy(b->dev->name,"Host/PCI Bridge"); - device_register(b->dev); - - b->number = b->secondary = bus; - b->resource[0] = &ioport_resource; - b->resource[1] = &iomem_resource; - return b; -} - -struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata) -{ - struct pci_bus *b = pci_alloc_primary_bus(bus); - if (b) { - b->sysdata = sysdata; - b->ops = ops; - b->subordinate = pci_do_scan_bus(b); - } - return b; -} - -#ifdef CONFIG_PM - -/* - * PCI Power management.. - * - * This needs to be done centralized, so that we power manage PCI - * devices in the right order: we should not shut down PCI bridges - * before we've shut down the devices behind them, and we should - * not wake up devices before we've woken up the bridge to the - * device.. Eh? - * - * We do not touch devices that don't have a driver that exports - * a suspend/resume function. That is just too dangerous. If the default - * PCI suspend/resume functions work for a device, the driver can - * easily implement them (ie just have a suspend function that calls - * the pci_set_power_state() function). - */ - -static int pci_pm_save_state_device(struct pci_dev *dev, u32 state) -{ - int error = 0; - if (dev) { - struct pci_driver *driver = dev->driver; - if (driver && driver->save_state) - error = driver->save_state(dev,state); - } - return error; -} - -static int pci_pm_suspend_device(struct pci_dev *dev, u32 state) -{ - int error = 0; - if (dev) { - struct pci_driver *driver = dev->driver; - if (driver && driver->suspend) - error = driver->suspend(dev,state); - } - return error; -} - -static int pci_pm_resume_device(struct pci_dev *dev) -{ - int error = 0; - if (dev) { - struct pci_driver *driver = dev->driver; - if (driver && driver->resume) - error = driver->resume(dev); - } - return error; -} - -static int pci_pm_save_state_bus(struct pci_bus *bus, u32 state) -{ - struct list_head *list; - int error = 0; - - list_for_each(list, &bus->children) { - error = pci_pm_save_state_bus(pci_bus_b(list),state); - if (error) return error; - } - list_for_each(list, &bus->devices) { - error = pci_pm_save_state_device(pci_dev_b(list),state); - if (error) return error; - } - return 0; -} - -static int pci_pm_suspend_bus(struct pci_bus *bus, u32 state) -{ - struct list_head *list; - - /* Walk the bus children list */ - list_for_each(list, &bus->children) - pci_pm_suspend_bus(pci_bus_b(list),state); - - /* Walk the device children list */ - list_for_each(list, &bus->devices) - pci_pm_suspend_device(pci_dev_b(list),state); - return 0; -} - -static int pci_pm_resume_bus(struct pci_bus *bus) -{ - struct list_head *list; - - /* Walk the device children list */ - list_for_each(list, &bus->devices) - pci_pm_resume_device(pci_dev_b(list)); - - /* And then walk the bus children */ - list_for_each(list, &bus->children) - pci_pm_resume_bus(pci_bus_b(list)); - return 0; -} - -static int pci_pm_save_state(u32 state) -{ - struct list_head *list; - struct pci_bus *bus; - int error = 0; - - list_for_each(list, &pci_root_buses) { - bus = pci_bus_b(list); - error = pci_pm_save_state_bus(bus,state); - if (!error) - error = pci_pm_save_state_device(bus->self,state); - } - return error; -} - -static int pci_pm_suspend(u32 state) -{ - struct list_head *list; - struct pci_bus *bus; - - list_for_each(list, &pci_root_buses) { - bus = pci_bus_b(list); - pci_pm_suspend_bus(bus,state); - pci_pm_suspend_device(bus->self,state); - } - return 0; -} - -static int pci_pm_resume(void) -{ - struct list_head *list; - struct pci_bus *bus; - - list_for_each(list, &pci_root_buses) { - bus = pci_bus_b(list); - pci_pm_resume_device(bus->self); - pci_pm_resume_bus(bus); - } - return 0; -} - -static int -pci_pm_callback(struct pm_dev *pm_device, pm_request_t rqst, void *data) -{ - int error = 0; - - switch (rqst) { - case PM_SAVE_STATE: - error = pci_pm_save_state((unsigned long)data); - break; - case PM_SUSPEND: - error = pci_pm_suspend((unsigned long)data); - break; - case PM_RESUME: - error = pci_pm_resume(); - break; - default: break; - } - return error; -} - -#endif - -/* - * Pool allocator ... wraps the pci_alloc_consistent page allocator, so - * small blocks are easily used by drivers for bus mastering controllers. - * This should probably be sharing the guts of the slab allocator. - */ - -struct pci_pool { /* the pool */ - struct list_head page_list; - spinlock_t lock; - size_t blocks_per_page; - size_t size; - struct pci_dev *dev; - size_t allocation; - char name [32]; - wait_queue_head_t waitq; -}; - -struct pci_page { /* cacheable header for 'allocation' bytes */ - struct list_head page_list; - void *vaddr; - dma_addr_t dma; - unsigned long bitmap [0]; -}; - -#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000) -#define POOL_POISON_BYTE 0xa7 - - -/** - * pci_pool_create - Creates a pool of pci consistent memory blocks, for dma. - * @name: name of pool, for diagnostics - * @pdev: pci device that will be doing the DMA - * @size: size of the blocks in this pool. - * @align: alignment requirement for blocks; must be a power of two - * @allocation: returned blocks won't cross this boundary (or zero) - * @mem_flags: SLAB_* flags. - * - * Returns a pci allocation pool with the requested characteristics, or - * null if one can't be created. Given one of these pools, pci_pool_alloc() - * may be used to allocate memory. Such memory will all have "consistent" - * DMA mappings, accessible by the device and its driver without using - * cache flushing primitives. The actual size of blocks allocated may be - * larger than requested because of alignment. - * - * If allocation is nonzero, objects returned from pci_pool_alloc() won't - * cross that size boundary. This is useful for devices which have - * addressing restrictions on individual DMA transfers, such as not crossing - * boundaries of 4KBytes. - */ -struct pci_pool * -pci_pool_create (const char *name, struct pci_dev *pdev, - size_t size, size_t align, size_t allocation, int mem_flags) -{ - struct pci_pool *retval; - - if (align == 0) - align = 1; - if (size == 0) - return 0; - else if (size < align) - size = align; - else if ((size % align) != 0) { - size += align + 1; - size &= ~(align - 1); - } - - if (allocation == 0) { - if (PAGE_SIZE < size) - allocation = size; - else - allocation = PAGE_SIZE; - // FIXME: round up for less fragmentation - } else if (allocation < size) - return 0; - - if (!(retval = kmalloc (sizeof *retval, mem_flags))) - return retval; - - strncpy (retval->name, name, sizeof retval->name); - retval->name [sizeof retval->name - 1] = 0; - - retval->dev = pdev; - INIT_LIST_HEAD (&retval->page_list); - spin_lock_init (&retval->lock); - retval->size = size; - retval->allocation = allocation; - retval->blocks_per_page = allocation / size; - init_waitqueue_head (&retval->waitq); - - return retval; -} - - -static struct pci_page * -pool_alloc_page (struct pci_pool *pool, int mem_flags) -{ - struct pci_page *page; - int mapsize; - - mapsize = pool->blocks_per_page; - mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG; - mapsize *= sizeof (long); - - page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags); - if (!page) - return 0; - page->vaddr = pci_alloc_consistent (pool->dev, - pool->allocation, - &page->dma); - if (page->vaddr) { - memset (page->bitmap, 0xff, mapsize); // bit set == free -#ifdef CONFIG_DEBUG_SLAB - memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); -#endif - list_add (&page->page_list, &pool->page_list); - } else { - kfree (page); - page = 0; - } - return page; -} - - -static inline int -is_page_busy (int blocks, unsigned long *bitmap) -{ - while (blocks > 0) { - if (*bitmap++ != ~0UL) - return 1; - blocks -= BITS_PER_LONG; - } - return 0; -} - -static void -pool_free_page (struct pci_pool *pool, struct pci_page *page) -{ - dma_addr_t dma = page->dma; - -#ifdef CONFIG_DEBUG_SLAB - memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); -#endif - pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma); - list_del (&page->page_list); - kfree (page); -} - - -/** - * pci_pool_destroy - destroys a pool of pci memory blocks. - * @pool: pci pool that will be destroyed - * - * Caller guarantees that no more memory from the pool is in use, - * and that nothing will try to use the pool after this call. - */ -void -pci_pool_destroy (struct pci_pool *pool) -{ - unsigned long flags; - - spin_lock_irqsave (&pool->lock, flags); - while (!list_empty (&pool->page_list)) { - struct pci_page *page; - page = list_entry (pool->page_list.next, - struct pci_page, page_list); - if (is_page_busy (pool->blocks_per_page, page->bitmap)) { - printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n", - pool->dev ? pool->dev->slot_name : NULL, - pool->name, page->vaddr); - /* leak the still-in-use consistent memory */ - list_del (&page->page_list); - kfree (page); - } else - pool_free_page (pool, page); - } - spin_unlock_irqrestore (&pool->lock, flags); - kfree (pool); -} - - -/** - * pci_pool_alloc - get a block of consistent memory - * @pool: pci pool that will produce the block - * @mem_flags: SLAB_KERNEL or SLAB_ATOMIC - * @handle: pointer to dma address of block - * - * This returns the kernel virtual address of a currently unused block, - * and reports its dma address through the handle. - * If such a memory block can't be allocated, null is returned. - */ -void * -pci_pool_alloc (struct pci_pool *pool, int mem_flags, dma_addr_t *handle) -{ - unsigned long flags; - struct list_head *entry; - struct pci_page *page; - int map, block; - size_t offset; - void *retval; - -restart: - spin_lock_irqsave (&pool->lock, flags); - list_for_each (entry, &pool->page_list) { - int i; - page = list_entry (entry, struct pci_page, page_list); - /* only cachable accesses here ... */ - for (map = 0, i = 0; - i < pool->blocks_per_page; - i += BITS_PER_LONG, map++) { - if (page->bitmap [map] == 0) - continue; - block = ffz (~ page->bitmap [map]); - if ((i + block) < pool->blocks_per_page) { - clear_bit (block, &page->bitmap [map]); - offset = (BITS_PER_LONG * map) + block; - offset *= pool->size; - goto ready; - } - } - } - if (!(page = pool_alloc_page (pool, mem_flags))) { - if (mem_flags == SLAB_KERNEL) { - DECLARE_WAITQUEUE (wait, current); - - current->state = TASK_INTERRUPTIBLE; - add_wait_queue (&pool->waitq, &wait); - spin_unlock_irqrestore (&pool->lock, flags); - - schedule_timeout (POOL_TIMEOUT_JIFFIES); - - current->state = TASK_RUNNING; - remove_wait_queue (&pool->waitq, &wait); - goto restart; - } - retval = 0; - goto done; - } - - clear_bit (0, &page->bitmap [0]); - offset = 0; -ready: - retval = offset + page->vaddr; - *handle = offset + page->dma; -done: - spin_unlock_irqrestore (&pool->lock, flags); - return retval; -} - - -static struct pci_page * -pool_find_page (struct pci_pool *pool, dma_addr_t dma) -{ - unsigned long flags; - struct list_head *entry; - struct pci_page *page; - - spin_lock_irqsave (&pool->lock, flags); - list_for_each (entry, &pool->page_list) { - page = list_entry (entry, struct pci_page, page_list); - if (dma < page->dma) - continue; - if (dma < (page->dma + pool->allocation)) - goto done; - } - page = 0; -done: - spin_unlock_irqrestore (&pool->lock, flags); - return page; -} - - -/** - * pci_pool_free - put block back into pci pool - * @pool: the pci pool holding the block - * @vaddr: virtual address of block - * @dma: dma address of block - * - * Caller promises neither device nor driver will again touch this block - * unless it is first re-allocated. - */ -void -pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma) -{ - struct pci_page *page; - unsigned long flags; - int map, block; - - if ((page = pool_find_page (pool, dma)) == 0) { - printk (KERN_ERR "pci_pool_free %s/%s, %p/%lx (bad dma)\n", - pool->dev ? pool->dev->slot_name : NULL, - pool->name, vaddr, (unsigned long) dma); - return; - } - - block = dma - page->dma; - block /= pool->size; - map = block / BITS_PER_LONG; - block %= BITS_PER_LONG; - -#ifdef CONFIG_DEBUG_SLAB - if (((dma - page->dma) + (void *)page->vaddr) != vaddr) { - printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%lx\n", - pool->dev ? pool->dev->slot_name : NULL, - pool->name, vaddr, (unsigned long) dma); - return; - } - if (page->bitmap [map] & (1UL << block)) { - printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n", - pool->dev ? pool->dev->slot_name : NULL, - pool->name, dma); - return; - } - memset (vaddr, POOL_POISON_BYTE, pool->size); -#endif - - spin_lock_irqsave (&pool->lock, flags); - set_bit (block, &page->bitmap [map]); - if (waitqueue_active (&pool->waitq)) - wake_up (&pool->waitq); - /* - * Resist a temptation to do - * if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page); - * it is not interrupt safe. Better have empty pages hang around. - */ - spin_unlock_irqrestore (&pool->lock, flags); -} - static int __devinit pci_init(void) { struct pci_dev *dev; - pcibios_init(); - pci_for_each_dev(dev) { pci_fixup_device(PCI_FIXUP_FINAL, dev); } - -#ifdef CONFIG_PM - pm_register(PM_PCI_DEV, 0, pci_pm_callback); -#endif return 0; } @@ -2058,55 +580,23 @@ return 1; } -subsys_initcall(pci_init); +device_initcall(pci_init); __setup("pci=", pci_setup); -EXPORT_SYMBOL(pci_read_config_byte); -EXPORT_SYMBOL(pci_read_config_word); -EXPORT_SYMBOL(pci_read_config_dword); -EXPORT_SYMBOL(pci_write_config_byte); -EXPORT_SYMBOL(pci_write_config_word); -EXPORT_SYMBOL(pci_write_config_dword); -EXPORT_SYMBOL(pci_devices); -EXPORT_SYMBOL(pci_root_buses); EXPORT_SYMBOL(pci_enable_device); EXPORT_SYMBOL(pci_disable_device); EXPORT_SYMBOL(pci_find_capability); EXPORT_SYMBOL(pci_release_regions); EXPORT_SYMBOL(pci_request_regions); -EXPORT_SYMBOL(pci_find_class); -EXPORT_SYMBOL(pci_find_device); -EXPORT_SYMBOL(pci_find_slot); -EXPORT_SYMBOL(pci_find_subsys); EXPORT_SYMBOL(pci_set_master); EXPORT_SYMBOL(pci_set_mwi); EXPORT_SYMBOL(pci_clear_mwi); EXPORT_SYMBOL(pci_set_dma_mask); EXPORT_SYMBOL(pci_dac_set_dma_mask); EXPORT_SYMBOL(pci_assign_resource); -EXPORT_SYMBOL(pci_register_driver); -EXPORT_SYMBOL(pci_unregister_driver); -EXPORT_SYMBOL(pci_dev_driver); -EXPORT_SYMBOL(pci_match_device); EXPORT_SYMBOL(pci_find_parent_resource); -#ifdef CONFIG_HOTPLUG -EXPORT_SYMBOL(pci_setup_device); -EXPORT_SYMBOL(pci_insert_device); -EXPORT_SYMBOL(pci_remove_device); -EXPORT_SYMBOL(pci_announce_device_to_drivers); -EXPORT_SYMBOL(pci_add_new_bus); -EXPORT_SYMBOL(pci_do_scan_bus); -EXPORT_SYMBOL(pci_scan_slot); -#ifdef CONFIG_PROC_FS -EXPORT_SYMBOL(pci_proc_attach_device); -EXPORT_SYMBOL(pci_proc_detach_device); -EXPORT_SYMBOL(pci_proc_attach_bus); -EXPORT_SYMBOL(pci_proc_detach_bus); -#endif -#endif - EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); @@ -2128,11 +618,3 @@ EXPORT_SYMBOL(isa_dma_bridge_buggy); EXPORT_SYMBOL(pci_pci_problems); - -/* Pool allocator */ - -EXPORT_SYMBOL (pci_pool_create); -EXPORT_SYMBOL (pci_pool_destroy); -EXPORT_SYMBOL (pci_pool_alloc); -EXPORT_SYMBOL (pci_pool_free); - diff -Nru a/drivers/pci/pool.c b/drivers/pci/pool.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/pool.c Tue May 7 13:31:16 2002 @@ -0,0 +1,336 @@ +#include +#include +#include + +/* + * Pool allocator ... wraps the pci_alloc_consistent page allocator, so + * small blocks are easily used by drivers for bus mastering controllers. + * This should probably be sharing the guts of the slab allocator. + */ + +struct pci_pool { /* the pool */ + struct list_head page_list; + spinlock_t lock; + size_t blocks_per_page; + size_t size; + struct pci_dev *dev; + size_t allocation; + char name [32]; + wait_queue_head_t waitq; +}; + +struct pci_page { /* cacheable header for 'allocation' bytes */ + struct list_head page_list; + void *vaddr; + dma_addr_t dma; + unsigned long bitmap [0]; +}; + +#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000) +#define POOL_POISON_BYTE 0xa7 + + +/** + * pci_pool_create - Creates a pool of pci consistent memory blocks, for dma. + * @name: name of pool, for diagnostics + * @pdev: pci device that will be doing the DMA + * @size: size of the blocks in this pool. + * @align: alignment requirement for blocks; must be a power of two + * @allocation: returned blocks won't cross this boundary (or zero) + * @mem_flags: SLAB_* flags. + * + * Returns a pci allocation pool with the requested characteristics, or + * null if one can't be created. Given one of these pools, pci_pool_alloc() + * may be used to allocate memory. Such memory will all have "consistent" + * DMA mappings, accessible by the device and its driver without using + * cache flushing primitives. The actual size of blocks allocated may be + * larger than requested because of alignment. + * + * If allocation is nonzero, objects returned from pci_pool_alloc() won't + * cross that size boundary. This is useful for devices which have + * addressing restrictions on individual DMA transfers, such as not crossing + * boundaries of 4KBytes. + */ +struct pci_pool * +pci_pool_create (const char *name, struct pci_dev *pdev, + size_t size, size_t align, size_t allocation, int mem_flags) +{ + struct pci_pool *retval; + + if (align == 0) + align = 1; + if (size == 0) + return 0; + else if (size < align) + size = align; + else if ((size % align) != 0) { + size += align + 1; + size &= ~(align - 1); + } + + if (allocation == 0) { + if (PAGE_SIZE < size) + allocation = size; + else + allocation = PAGE_SIZE; + // FIXME: round up for less fragmentation + } else if (allocation < size) + return 0; + + if (!(retval = kmalloc (sizeof *retval, mem_flags))) + return retval; + + strncpy (retval->name, name, sizeof retval->name); + retval->name [sizeof retval->name - 1] = 0; + + retval->dev = pdev; + INIT_LIST_HEAD (&retval->page_list); + spin_lock_init (&retval->lock); + retval->size = size; + retval->allocation = allocation; + retval->blocks_per_page = allocation / size; + init_waitqueue_head (&retval->waitq); + + return retval; +} + + +static struct pci_page * +pool_alloc_page (struct pci_pool *pool, int mem_flags) +{ + struct pci_page *page; + int mapsize; + + mapsize = pool->blocks_per_page; + mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG; + mapsize *= sizeof (long); + + page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags); + if (!page) + return 0; + page->vaddr = pci_alloc_consistent (pool->dev, + pool->allocation, + &page->dma); + if (page->vaddr) { + memset (page->bitmap, 0xff, mapsize); // bit set == free +#ifdef CONFIG_DEBUG_SLAB + memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); +#endif + list_add (&page->page_list, &pool->page_list); + } else { + kfree (page); + page = 0; + } + return page; +} + + +static inline int +is_page_busy (int blocks, unsigned long *bitmap) +{ + while (blocks > 0) { + if (*bitmap++ != ~0UL) + return 1; + blocks -= BITS_PER_LONG; + } + return 0; +} + +static void +pool_free_page (struct pci_pool *pool, struct pci_page *page) +{ + dma_addr_t dma = page->dma; + +#ifdef CONFIG_DEBUG_SLAB + memset (page->vaddr, POOL_POISON_BYTE, pool->allocation); +#endif + pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma); + list_del (&page->page_list); + kfree (page); +} + + +/** + * pci_pool_destroy - destroys a pool of pci memory blocks. + * @pool: pci pool that will be destroyed + * + * Caller guarantees that no more memory from the pool is in use, + * and that nothing will try to use the pool after this call. + */ +void +pci_pool_destroy (struct pci_pool *pool) +{ + unsigned long flags; + + spin_lock_irqsave (&pool->lock, flags); + while (!list_empty (&pool->page_list)) { + struct pci_page *page; + page = list_entry (pool->page_list.next, + struct pci_page, page_list); + if (is_page_busy (pool->blocks_per_page, page->bitmap)) { + printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n", + pool->dev ? pool->dev->slot_name : NULL, + pool->name, page->vaddr); + /* leak the still-in-use consistent memory */ + list_del (&page->page_list); + kfree (page); + } else + pool_free_page (pool, page); + } + spin_unlock_irqrestore (&pool->lock, flags); + kfree (pool); +} + + +/** + * pci_pool_alloc - get a block of consistent memory + * @pool: pci pool that will produce the block + * @mem_flags: SLAB_KERNEL or SLAB_ATOMIC + * @handle: pointer to dma address of block + * + * This returns the kernel virtual address of a currently unused block, + * and reports its dma address through the handle. + * If such a memory block can't be allocated, null is returned. + */ +void * +pci_pool_alloc (struct pci_pool *pool, int mem_flags, dma_addr_t *handle) +{ + unsigned long flags; + struct list_head *entry; + struct pci_page *page; + int map, block; + size_t offset; + void *retval; + +restart: + spin_lock_irqsave (&pool->lock, flags); + list_for_each (entry, &pool->page_list) { + int i; + page = list_entry (entry, struct pci_page, page_list); + /* only cachable accesses here ... */ + for (map = 0, i = 0; + i < pool->blocks_per_page; + i += BITS_PER_LONG, map++) { + if (page->bitmap [map] == 0) + continue; + block = ffz (~ page->bitmap [map]); + if ((i + block) < pool->blocks_per_page) { + clear_bit (block, &page->bitmap [map]); + offset = (BITS_PER_LONG * map) + block; + offset *= pool->size; + goto ready; + } + } + } + if (!(page = pool_alloc_page (pool, mem_flags))) { + if (mem_flags == SLAB_KERNEL) { + DECLARE_WAITQUEUE (wait, current); + + current->state = TASK_INTERRUPTIBLE; + add_wait_queue (&pool->waitq, &wait); + spin_unlock_irqrestore (&pool->lock, flags); + + schedule_timeout (POOL_TIMEOUT_JIFFIES); + + current->state = TASK_RUNNING; + remove_wait_queue (&pool->waitq, &wait); + goto restart; + } + retval = 0; + goto done; + } + + clear_bit (0, &page->bitmap [0]); + offset = 0; +ready: + retval = offset + page->vaddr; + *handle = offset + page->dma; +done: + spin_unlock_irqrestore (&pool->lock, flags); + return retval; +} + + +static struct pci_page * +pool_find_page (struct pci_pool *pool, dma_addr_t dma) +{ + unsigned long flags; + struct list_head *entry; + struct pci_page *page; + + spin_lock_irqsave (&pool->lock, flags); + list_for_each (entry, &pool->page_list) { + page = list_entry (entry, struct pci_page, page_list); + if (dma < page->dma) + continue; + if (dma < (page->dma + pool->allocation)) + goto done; + } + page = 0; +done: + spin_unlock_irqrestore (&pool->lock, flags); + return page; +} + + +/** + * pci_pool_free - put block back into pci pool + * @pool: the pci pool holding the block + * @vaddr: virtual address of block + * @dma: dma address of block + * + * Caller promises neither device nor driver will again touch this block + * unless it is first re-allocated. + */ +void +pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma) +{ + struct pci_page *page; + unsigned long flags; + int map, block; + + if ((page = pool_find_page (pool, dma)) == 0) { + printk (KERN_ERR "pci_pool_free %s/%s, %p/%lx (bad dma)\n", + pool->dev ? pool->dev->slot_name : NULL, + pool->name, vaddr, (unsigned long) dma); + return; + } + + block = dma - page->dma; + block /= pool->size; + map = block / BITS_PER_LONG; + block %= BITS_PER_LONG; + +#ifdef CONFIG_DEBUG_SLAB + if (((dma - page->dma) + (void *)page->vaddr) != vaddr) { + printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%lx\n", + pool->dev ? pool->dev->slot_name : NULL, + pool->name, vaddr, (unsigned long) dma); + return; + } + if (page->bitmap [map] & (1UL << block)) { + printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n", + pool->dev ? pool->dev->slot_name : NULL, + pool->name, dma); + return; + } + memset (vaddr, POOL_POISON_BYTE, pool->size); +#endif + + spin_lock_irqsave (&pool->lock, flags); + set_bit (block, &page->bitmap [map]); + if (waitqueue_active (&pool->waitq)) + wake_up (&pool->waitq); + /* + * Resist a temptation to do + * if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page); + * it is not interrupt safe. Better have empty pages hang around. + */ + spin_unlock_irqrestore (&pool->lock, flags); +} + + +EXPORT_SYMBOL (pci_pool_create); +EXPORT_SYMBOL (pci_pool_destroy); +EXPORT_SYMBOL (pci_pool_alloc); +EXPORT_SYMBOL (pci_pool_free); diff -Nru a/drivers/pci/power.c b/drivers/pci/power.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/power.c Tue May 7 13:31:16 2002 @@ -0,0 +1,164 @@ +#include +#include + +/* + * PCI Power management.. + * + * This needs to be done centralized, so that we power manage PCI + * devices in the right order: we should not shut down PCI bridges + * before we've shut down the devices behind them, and we should + * not wake up devices before we've woken up the bridge to the + * device.. Eh? + * + * We do not touch devices that don't have a driver that exports + * a suspend/resume function. That is just too dangerous. If the default + * PCI suspend/resume functions work for a device, the driver can + * easily implement them (ie just have a suspend function that calls + * the pci_set_power_state() function). + */ + +static int pci_pm_save_state_device(struct pci_dev *dev, u32 state) +{ + int error = 0; + if (dev) { + struct pci_driver *driver = dev->driver; + if (driver && driver->save_state) + error = driver->save_state(dev,state); + } + return error; +} + +static int pci_pm_suspend_device(struct pci_dev *dev, u32 state) +{ + int error = 0; + if (dev) { + struct pci_driver *driver = dev->driver; + if (driver && driver->suspend) + error = driver->suspend(dev,state); + } + return error; +} + +static int pci_pm_resume_device(struct pci_dev *dev) +{ + int error = 0; + if (dev) { + struct pci_driver *driver = dev->driver; + if (driver && driver->resume) + error = driver->resume(dev); + } + return error; +} + +static int pci_pm_save_state_bus(struct pci_bus *bus, u32 state) +{ + struct list_head *list; + int error = 0; + + list_for_each(list, &bus->children) { + error = pci_pm_save_state_bus(pci_bus_b(list),state); + if (error) return error; + } + list_for_each(list, &bus->devices) { + error = pci_pm_save_state_device(pci_dev_b(list),state); + if (error) return error; + } + return 0; +} + +static int pci_pm_suspend_bus(struct pci_bus *bus, u32 state) +{ + struct list_head *list; + + /* Walk the bus children list */ + list_for_each(list, &bus->children) + pci_pm_suspend_bus(pci_bus_b(list),state); + + /* Walk the device children list */ + list_for_each(list, &bus->devices) + pci_pm_suspend_device(pci_dev_b(list),state); + return 0; +} + +static int pci_pm_resume_bus(struct pci_bus *bus) +{ + struct list_head *list; + + /* Walk the device children list */ + list_for_each(list, &bus->devices) + pci_pm_resume_device(pci_dev_b(list)); + + /* And then walk the bus children */ + list_for_each(list, &bus->children) + pci_pm_resume_bus(pci_bus_b(list)); + return 0; +} + +static int pci_pm_save_state(u32 state) +{ + struct list_head *list; + struct pci_bus *bus; + int error = 0; + + list_for_each(list, &pci_root_buses) { + bus = pci_bus_b(list); + error = pci_pm_save_state_bus(bus,state); + if (!error) + error = pci_pm_save_state_device(bus->self,state); + } + return error; +} + +static int pci_pm_suspend(u32 state) +{ + struct list_head *list; + struct pci_bus *bus; + + list_for_each(list, &pci_root_buses) { + bus = pci_bus_b(list); + pci_pm_suspend_bus(bus,state); + pci_pm_suspend_device(bus->self,state); + } + return 0; +} + +static int pci_pm_resume(void) +{ + struct list_head *list; + struct pci_bus *bus; + + list_for_each(list, &pci_root_buses) { + bus = pci_bus_b(list); + pci_pm_resume_device(bus->self); + pci_pm_resume_bus(bus); + } + return 0; +} + +static int +pci_pm_callback(struct pm_dev *pm_device, pm_request_t rqst, void *data) +{ + int error = 0; + + switch (rqst) { + case PM_SAVE_STATE: + error = pci_pm_save_state((unsigned long)data); + break; + case PM_SUSPEND: + error = pci_pm_suspend((unsigned long)data); + break; + case PM_RESUME: + error = pci_pm_resume(); + break; + default: break; + } + return error; +} + +static int __init pci_pm_init(void) +{ + pm_register(PM_PCI_DEV, 0, pci_pm_callback); + return 0; +} + +subsys_initcall(pci_pm_init); diff -Nru a/drivers/pci/probe.c b/drivers/pci/probe.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/probe.c Tue May 7 13:31:16 2002 @@ -0,0 +1,605 @@ +/* + * probe.c - PCI detection and setup code + */ + +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +LIST_HEAD(pci_root_buses); +LIST_HEAD(pci_devices); + +extern struct device_driver pci_device_driver; + +/* + * Translate the low bits of the PCI base + * to the resource type + */ +static inline unsigned int pci_calc_resource_flags(unsigned int flags) +{ + if (flags & PCI_BASE_ADDRESS_SPACE_IO) + return IORESOURCE_IO; + + if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) + return IORESOURCE_MEM | IORESOURCE_PREFETCH; + + return IORESOURCE_MEM; +} + +/* + * Find the extent of a PCI decode.. + */ +static u32 pci_size(u32 base, unsigned long mask) +{ + u32 size = mask & base; /* Find the significant bits */ + size = size & ~(size-1); /* Get the lowest of them to find the decode size */ + return size-1; /* extent = size - 1 */ +} + +static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) +{ + unsigned int pos, reg, next; + u32 l, sz; + struct resource *res; + + for(pos=0; posresource[pos]; + res->name = dev->name; + reg = PCI_BASE_ADDRESS_0 + (pos << 2); + pci_read_config_dword(dev, reg, &l); + pci_write_config_dword(dev, reg, ~0); + pci_read_config_dword(dev, reg, &sz); + pci_write_config_dword(dev, reg, l); + if (!sz || sz == 0xffffffff) + continue; + if (l == 0xffffffff) + l = 0; + if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { + res->start = l & PCI_BASE_ADDRESS_MEM_MASK; + res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; + sz = pci_size(sz, PCI_BASE_ADDRESS_MEM_MASK); + } else { + res->start = l & PCI_BASE_ADDRESS_IO_MASK; + res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; + sz = pci_size(sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); + } + res->end = res->start + (unsigned long) sz; + res->flags |= pci_calc_resource_flags(l); + if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) + == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) { + pci_read_config_dword(dev, reg+4, &l); + next++; +#if BITS_PER_LONG == 64 + res->start |= ((unsigned long) l) << 32; + res->end = res->start + sz; + pci_write_config_dword(dev, reg+4, ~0); + pci_read_config_dword(dev, reg+4, &sz); + pci_write_config_dword(dev, reg+4, l); + if (~sz) + res->end = res->start + 0xffffffff + + (((unsigned long) ~sz) << 32); +#else + if (l) { + printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name); + res->start = 0; + res->flags = 0; + continue; + } +#endif + } + } + if (rom) { + dev->rom_base_reg = rom; + res = &dev->resource[PCI_ROM_RESOURCE]; + pci_read_config_dword(dev, rom, &l); + pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE); + pci_read_config_dword(dev, rom, &sz); + pci_write_config_dword(dev, rom, l); + if (l == 0xffffffff) + l = 0; + if (sz && sz != 0xffffffff) { + res->flags = (l & PCI_ROM_ADDRESS_ENABLE) | + IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_READONLY | IORESOURCE_CACHEABLE; + res->start = l & PCI_ROM_ADDRESS_MASK; + sz = pci_size(sz, PCI_ROM_ADDRESS_MASK); + res->end = res->start + (unsigned long) sz; + } + res->name = dev->name; + } +} + +void __devinit pci_read_bridge_bases(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + u8 io_base_lo, io_limit_lo; + u16 mem_base_lo, mem_limit_lo; + unsigned long base, limit; + struct resource *res; + int i; + + if (!dev) /* It's a host bus, nothing to read */ + return; + + for(i=0; i<3; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + + res = child->resource[0]; + pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); + pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); + base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; + limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + + if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + u16 io_base_hi, io_limit_hi; + pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); + pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); + base |= (io_base_hi << 16); + limit |= (io_limit_hi << 16); + } + + if (base && base <= limit) { + res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + res->start = base; + res->end = limit + 0xfff; + res->name = child->name; + } else { + /* + * Ugh. We don't know enough about this bridge. Just assume + * that it's entirely transparent. + */ + printk(KERN_ERR "Unknown bridge resource %d: assuming transparent\n", 0); + child->resource[0] = child->parent->resource[0]; + } + + res = child->resource[1]; + pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); + pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); + base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; + limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; + if (base && base <= limit) { + res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; + res->start = base; + res->end = limit + 0xfffff; + res->name = child->name; + } else { + /* See comment above. Same thing */ + printk(KERN_ERR "Unknown bridge resource %d: assuming transparent\n", 1); + child->resource[1] = child->parent->resource[1]; + } + + res = child->resource[2]; + pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); + pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); + base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; + limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; + + if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + u32 mem_base_hi, mem_limit_hi; + pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); + pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); +#if BITS_PER_LONG == 64 + base |= ((long) mem_base_hi) << 32; + limit |= ((long) mem_limit_hi) << 32; +#else + if (mem_base_hi || mem_limit_hi) { + printk(KERN_ERR "PCI: Unable to handle 64-bit address space for %s\n", child->name); + return; + } +#endif + } + if (base && base <= limit) { + res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; + res->start = base; + res->end = limit + 0xfffff; + res->name = child->name; + } else { + /* See comments above */ + printk(KERN_ERR "Unknown bridge resource %d: assuming transparent\n", 2); + child->resource[2] = child->parent->resource[2]; + } +} + +static struct pci_bus * __devinit pci_alloc_bus(void) +{ + struct pci_bus *b; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (b) { + memset(b, 0, sizeof(*b)); + INIT_LIST_HEAD(&b->children); + INIT_LIST_HEAD(&b->devices); + } + return b; +} + +struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) +{ + struct pci_bus *child; + int i; + + /* + * Allocate a new bus, and inherit stuff from the parent.. + */ + child = pci_alloc_bus(); + + list_add_tail(&child->node, &parent->children); + child->self = dev; + dev->subordinate = child; + child->parent = parent; + child->ops = parent->ops; + child->sysdata = parent->sysdata; + child->dev = &dev->dev; + + /* + * Set up the primary, secondary and subordinate + * bus numbers. + */ + child->number = child->secondary = busnr; + child->primary = parent->secondary; + child->subordinate = 0xff; + + /* Set up default resource pointers.. */ + for (i = 0; i < 4; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + + return child; +} + +unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus); + +/* + * If it's a bridge, configure it and scan the bus behind it. + * For CardBus bridges, we don't scan behind as the devices will + * be handled by the bridge driver itself. + * + * We need to process bridges in two passes -- first we scan those + * already configured by the BIOS and after we are done with all of + * them, we proceed to assigning numbers to the remaining buses in + * order to avoid overlaps between old and new bus numbers. + */ +static int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass) +{ + unsigned int buses; + unsigned short cr; + struct pci_bus *child; + int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); + + pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); + DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", dev->slot_name, buses & 0xffffff, pass); + if ((buses & 0xffff00) && !pcibios_assign_all_busses()) { + /* + * Bus already configured by firmware, process it in the first + * pass and just note the configuration. + */ + if (pass) + return max; + child = pci_add_new_bus(bus, dev, 0); + child->primary = buses & 0xFF; + child->secondary = (buses >> 8) & 0xFF; + child->subordinate = (buses >> 16) & 0xFF; + child->number = child->secondary; + if (!is_cardbus) { + unsigned int cmax = pci_do_scan_bus(child); + if (cmax > max) max = cmax; + } else { + unsigned int cmax = child->subordinate; + if (cmax > max) max = cmax; + } + } else { + /* + * We need to assign a number to this bus which we always + * do in the second pass. We also keep all address decoders + * on the bridge disabled during scanning. FIXME: Why? + */ + if (!pass) + return max; + pci_read_config_word(dev, PCI_COMMAND, &cr); + pci_write_config_word(dev, PCI_COMMAND, 0x0000); + pci_write_config_word(dev, PCI_STATUS, 0xffff); + + child = pci_add_new_bus(bus, dev, ++max); + buses = (buses & 0xff000000) + | ((unsigned int)(child->primary) << 0) + | ((unsigned int)(child->secondary) << 8) + | ((unsigned int)(child->subordinate) << 16); + /* + * We need to blast all three values with a single write. + */ + pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); + if (!is_cardbus) { + /* Now we can scan all subordinate buses... */ + max = pci_do_scan_bus(child); + } else { + /* + * For CardBus bridges, we leave 4 bus numbers + * as cards with a PCI-to-PCI bridge can be + * inserted later. + */ + max += 3; + } + /* + * Set the subordinate bus number to its real value. + */ + child->subordinate = max; + pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); + pci_write_config_word(dev, PCI_COMMAND, cr); + } + sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number); + + return max; +} + +/* + * Read interrupt line and base address registers. + * The architecture-dependent code can tweak these, of course. + */ +static void pci_read_irq(struct pci_dev *dev) +{ + unsigned char irq; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); + if (irq) + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + dev->irq = irq; +} + +/** + * pci_setup_device - fill in class and map information of a device + * @dev: the device structure to fill + * + * Initialize the device structure with information about the device's + * vendor,class,memory and IO-space addresses,IRQ lines etc. + * Called at initialisation of the PCI subsystem and by CardBus services. + * Returns 0 on success and -1 if unknown type of device (not normal, bridge + * or CardBus). + */ +int pci_setup_device(struct pci_dev * dev) +{ + u32 class; + + sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device); + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); + class >>= 8; /* upper 3 bytes */ + dev->class = class; + class >>= 8; + + DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, dev->devfn, dev->vendor, dev->device, class, dev->hdr_type); + + /* "Unknown power state" */ + dev->current_state = 4; + + switch (dev->hdr_type) { /* header type */ + case PCI_HEADER_TYPE_NORMAL: /* standard header */ + if (class == PCI_CLASS_BRIDGE_PCI) + goto bad; + pci_read_irq(dev); + pci_read_bases(dev, 6, PCI_ROM_ADDRESS); + pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); + pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); + break; + + case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ + if (class != PCI_CLASS_BRIDGE_PCI) + goto bad; + pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); + break; + + case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ + if (class != PCI_CLASS_BRIDGE_CARDBUS) + goto bad; + pci_read_irq(dev); + pci_read_bases(dev, 1, 0); + pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); + pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device); + break; + + default: /* unknown header */ + printk(KERN_ERR "PCI: device %s has unknown header type %02x, ignoring.\n", + dev->slot_name, dev->hdr_type); + return -1; + + bad: + printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n", + dev->slot_name, class, dev->hdr_type); + dev->class = PCI_CLASS_NOT_DEFINED; + } + + /* We found a fine healthy device, go go go... */ + return 0; +} + +/* + * Read the config data for a PCI device, sanity-check it + * and fill in the dev structure... + */ +struct pci_dev * __devinit pci_scan_device(struct pci_dev *temp) +{ + struct pci_dev *dev; + u32 l; + + if (pci_read_config_dword(temp, PCI_VENDOR_ID, &l)) + return NULL; + + /* some broken boards return 0 or ~0 if a slot is empty: */ + if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) + return NULL; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + memcpy(dev, temp, sizeof(*dev)); + dev->vendor = l & 0xffff; + dev->device = (l >> 16) & 0xffff; + + /* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) + set this higher, assuming the system even supports it. */ + dev->dma_mask = 0xffffffff; + if (pci_setup_device(dev) < 0) { + kfree(dev); + return NULL; + } + + /* now put in global tree */ + strcpy(dev->dev.name,dev->name); + strcpy(dev->dev.bus_id,dev->slot_name); + + device_register(&dev->dev); + return dev; +} + +struct pci_dev * __devinit pci_scan_slot(struct pci_dev *temp) +{ + struct pci_bus *bus = temp->bus; + struct pci_dev *dev; + struct pci_dev *first_dev = NULL; + int func = 0; + int is_multi = 0; + u8 hdr_type; + + for (func = 0; func < 8; func++, temp->devfn++) { + if (func && !is_multi) /* not a multi-function device */ + continue; + if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) + continue; + temp->hdr_type = hdr_type & 0x7f; + + dev = pci_scan_device(temp); + if (!dev) + continue; + pci_name_device(dev); + if (!func) { + is_multi = hdr_type & 0x80; + first_dev = dev; + } + + /* + * Link the device to both the global PCI device chain and + * the per-bus list of devices. + */ + list_add_tail(&dev->global_list, &pci_devices); + list_add_tail(&dev->bus_list, &bus->devices); + + /* Fix up broken headers */ + pci_fixup_device(PCI_FIXUP_HEADER, dev); + } + return first_dev; +} + +unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus) +{ + unsigned int devfn, max, pass; + struct list_head *ln; + struct pci_dev *dev, dev0; + + DBG("Scanning bus %02x\n", bus->number); + max = bus->secondary; + + /* Create a device template */ + memset(&dev0, 0, sizeof(dev0)); + dev0.bus = bus; + dev0.sysdata = bus->sysdata; + dev0.dev.parent = bus->dev; + dev0.dev.driver = &pci_device_driver; + + /* Go find them, Rover! */ + for (devfn = 0; devfn < 0x100; devfn += 8) { + dev0.devfn = devfn; + pci_scan_slot(&dev0); + } + + /* + * After performing arch-dependent fixup of the bus, look behind + * all PCI-to-PCI bridges on this bus. + */ + DBG("Fixups for bus %02x\n", bus->number); + pcibios_fixup_bus(bus); + for (pass=0; pass < 2; pass++) + for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) { + dev = pci_dev_b(ln); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + max = pci_scan_bridge(bus, dev, max, pass); + } + + /* + * We've scanned the bus and so we know all about what's on + * the other side of any bridges that may be on this bus plus + * any devices. + * + * Return how far we've got finding sub-buses. + */ + DBG("Bus scan for %02x returning with max=%02x\n", bus->number, max); + return max; +} + +int __devinit pci_bus_exists(const struct list_head *list, int nr) +{ + const struct list_head *l; + + for(l=list->next; l != list; l = l->next) { + const struct pci_bus *b = pci_bus_b(l); + if (b->number == nr || pci_bus_exists(&b->children, nr)) + return 1; + } + return 0; +} + +struct pci_bus * __devinit pci_alloc_primary_bus(int bus) +{ + struct pci_bus *b; + + if (pci_bus_exists(&pci_root_buses, bus)) { + /* If we already got to this bus through a different bridge, ignore it */ + DBG("PCI: Bus %02x already known\n", bus); + return NULL; + } + + b = pci_alloc_bus(); + if (!b) + return NULL; + list_add_tail(&b->node, &pci_root_buses); + + b->dev = kmalloc(sizeof(*(b->dev)),GFP_KERNEL); + memset(b->dev,0,sizeof(*(b->dev))); + sprintf(b->dev->bus_id,"pci%d",bus); + strcpy(b->dev->name,"Host/PCI Bridge"); + device_register(b->dev); + + b->number = b->secondary = bus; + b->resource[0] = &ioport_resource; + b->resource[1] = &iomem_resource; + return b; +} + +struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata) +{ + struct pci_bus *b = pci_alloc_primary_bus(bus); + if (b) { + b->sysdata = sysdata; + b->ops = ops; + b->subordinate = pci_do_scan_bus(b); + } + return b; +} + +EXPORT_SYMBOL(pci_devices); +EXPORT_SYMBOL(pci_root_buses); + +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pci_setup_device); +EXPORT_SYMBOL(pci_add_new_bus); +EXPORT_SYMBOL(pci_do_scan_bus); +EXPORT_SYMBOL(pci_scan_slot); +#endif diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c --- a/drivers/pci/proc.c Tue May 7 13:31:16 2002 +++ b/drivers/pci/proc.c Tue May 7 13:31:16 2002 @@ -6,11 +6,10 @@ * Copyright (c) 1997--1999 Martin Mares */ -#include -#include +#include #include +#include #include -#include #include #include @@ -615,3 +614,11 @@ } __initcall(pci_proc_init); + +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pci_proc_attach_device); +EXPORT_SYMBOL(pci_proc_detach_device); +EXPORT_SYMBOL(pci_proc_attach_bus); +EXPORT_SYMBOL(pci_proc_detach_bus); +#endif + diff -Nru a/drivers/pci/search.c b/drivers/pci/search.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/pci/search.c Tue May 7 13:31:16 2002 @@ -0,0 +1,110 @@ +#include +#include + +/** + * pci_find_slot - locate PCI device from a given PCI slot + * @bus: number of PCI bus on which desired PCI device resides + * @devfn: encodes number of PCI slot in which the desired PCI + * device resides and the logical device number within that slot + * in case of multi-function devices. + * + * Given a PCI bus and slot/function number, the desired PCI device + * is located in system global list of PCI devices. If the device + * is found, a pointer to its data structure is returned. If no + * device is found, %NULL is returned. + */ +struct pci_dev * +pci_find_slot(unsigned int bus, unsigned int devfn) +{ + struct pci_dev *dev; + + pci_for_each_dev(dev) { + if (dev->bus->number == bus && dev->devfn == devfn) + return dev; + } + return NULL; +} + +/** + * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id + * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids + * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids + * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids + * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids + * @from: Previous PCI device found in search, or %NULL for new search. + * + * Iterates through the list of known PCI devices. If a PCI device is + * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its + * device structure is returned. Otherwise, %NULL is returned. + * A new search is initiated by passing %NULL to the @from argument. + * Otherwise if @from is not %NULL, searches continue from next device on the global list. + */ +struct pci_dev * +pci_find_subsys(unsigned int vendor, unsigned int device, + unsigned int ss_vendor, unsigned int ss_device, + const struct pci_dev *from) +{ + struct list_head *n = from ? from->global_list.next : pci_devices.next; + + while (n != &pci_devices) { + struct pci_dev *dev = pci_dev_g(n); + if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && + (device == PCI_ANY_ID || dev->device == device) && + (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) && + (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)) + return dev; + n = n->next; + } + return NULL; +} + + +/** + * pci_find_device - begin or continue searching for a PCI device by vendor/device id + * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids + * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids + * @from: Previous PCI device found in search, or %NULL for new search. + * + * Iterates through the list of known PCI devices. If a PCI device is + * found with a matching @vendor and @device, a pointer to its device structure is + * returned. Otherwise, %NULL is returned. + * A new search is initiated by passing %NULL to the @from argument. + * Otherwise if @from is not %NULL, searches continue from next device on the global list. + */ +struct pci_dev * +pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from) +{ + return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); +} + + +/** + * pci_find_class - begin or continue searching for a PCI device by class + * @class: search for a PCI device with this class designation + * @from: Previous PCI device found in search, or %NULL for new search. + * + * Iterates through the list of known PCI devices. If a PCI device is + * found with a matching @class, a pointer to its device structure is + * returned. Otherwise, %NULL is returned. + * A new search is initiated by passing %NULL to the @from argument. + * Otherwise if @from is not %NULL, searches continue from next device + * on the global list. + */ +struct pci_dev * +pci_find_class(unsigned int class, const struct pci_dev *from) +{ + struct list_head *n = from ? from->global_list.next : pci_devices.next; + + while (n != &pci_devices) { + struct pci_dev *dev = pci_dev_g(n); + if (dev->class == class) + return dev; + n = n->next; + } + return NULL; +} + +EXPORT_SYMBOL(pci_find_class); +EXPORT_SYMBOL(pci_find_device); +EXPORT_SYMBOL(pci_find_slot); +EXPORT_SYMBOL(pci_find_subsys); diff -Nru a/include/linux/pci.h b/include/linux/pci.h --- a/include/linux/pci.h Tue May 7 13:31:16 2002 +++ b/include/linux/pci.h Tue May 7 13:31:16 2002 @@ -495,7 +495,6 @@ #define pci_for_each_dev(dev) \ for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next)) -void pcibios_init(void); void pcibios_fixup_bus(struct pci_bus *); int pcibios_enable_device(struct pci_dev *); char *pcibios_setup (char *str);