X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fia64%2Fpci%2Fpci.c;h=2a0dcf6083d62b130fbd887071efae90f385e9f9;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=dd33eaf87c326f60d77d3d071e95c7ababf9816c;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index dd33eaf87..2a0dcf608 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -6,6 +6,7 @@ * Copyright (C) 2002 Hewlett-Packard Co * David Mosberger-Tang * Bjorn Helgaas + * Copyright (C) 2004 Silicon Graphics, Inc. * * Note: Above list of copyright holders is incomplete... */ @@ -46,6 +47,8 @@ #define DBG(x...) #endif +static int pci_routeirq; + /* * Low-level SAL-based PCI configuration access functions. Note that SAL * calls are already serialized (via sal_lock), so we don't need another @@ -68,7 +71,7 @@ pci_sal_read (int seg, int bus, int devfn, int reg, int len, u32 *value) u64 addr, mode, data = 0; int result = 0; - if ((seg > 255) || (bus > 255) || (devfn > 255) || (reg > 4095)) + if ((seg > 65535) || (bus > 255) || (devfn > 255) || (reg > 4095)) return -EINVAL; if ((seg | reg) <= 255) { @@ -124,11 +127,24 @@ pci_write (struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val devfn, where, size, value); } -static struct pci_ops pci_root_ops = { +struct pci_ops pci_root_ops = { .read = pci_read, .write = pci_write, }; +#ifdef CONFIG_NUMA +extern acpi_status acpi_map_iosapic(acpi_handle, u32, void *, void **); +static void acpi_map_iosapics(void) +{ + acpi_get_devices(NULL, acpi_map_iosapic, NULL, NULL); +} +#else +static void acpi_map_iosapics(void) +{ + return; +} +#endif /* CONFIG_NUMA */ + static int __init pci_acpi_init (void) { @@ -136,18 +152,29 @@ pci_acpi_init (void) printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); -#ifdef CONFIG_NUMA -extern acpi_status acpi_map_iosapic (acpi_handle, u32, void*, void**); + acpi_map_iosapics(); - acpi_get_devices(NULL, acpi_map_iosapic, NULL, NULL); -#endif - /* - * PCI IRQ routing is set up by pci_enable_device(), but we - * also do it here in case there are still broken drivers that - * don't use pci_enable_device(). - */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) - acpi_pci_irq_enable(dev); + if (pci_routeirq) { + /* + * PCI IRQ routing is set up by pci_enable_device(), but we + * also do it here in case there are still broken drivers that + * don't use pci_enable_device(). + */ + printk(KERN_INFO "** Routing PCI interrupts for all devices because \"pci=routeirq\"\n"); + printk(KERN_INFO "** was specified. If this was required to make a driver work,\n"); + printk(KERN_INFO "** please email the output of \"lspci\" to bjorn.helgaas@hp.com\n"); + printk(KERN_INFO "** so I can fix the driver.\n"); + for_each_pci_dev(dev) + acpi_pci_irq_enable(dev); + } else { + printk(KERN_INFO "** PCI interrupts are no longer routed automatically. If this\n"); + printk(KERN_INFO "** causes a device to stop working, it is probably because the\n"); + printk(KERN_INFO "** driver failed to call pci_enable_device(). As a temporary\n"); + printk(KERN_INFO "** workaround, the \"pci=routeirq\" argument restores the old\n"); + printk(KERN_INFO "** behavior. If this argument makes the device work again,\n"); + printk(KERN_INFO "** please email the output of \"lspci\" to bjorn.helgaas@hp.com\n"); + printk(KERN_INFO "** so I can fix the driver.\n"); + } return 0; } @@ -275,7 +302,7 @@ add_window (struct acpi_resource *res, void *data) return AE_OK; window = &info->controller->window[info->controller->windows++]; - window->resource.flags |= flags; + window->resource.flags = flags; window->resource.start = addr.min_address_range; window->resource.end = addr.max_address_range; window->offset = offset; @@ -328,33 +355,66 @@ out1: return NULL; } -void __init -pcibios_fixup_device_resources (struct pci_dev *dev, struct pci_bus *bus) +void pcibios_resource_to_bus(struct pci_dev *dev, + struct pci_bus_region *region, struct resource *res) { struct pci_controller *controller = PCI_CONTROLLER(dev); - struct pci_window *window; - int i, j; + unsigned long offset = 0; + int i; + + for (i = 0; i < controller->windows; i++) { + struct pci_window *window = &controller->window[i]; + if (!(window->resource.flags & res->flags)) + continue; + if (window->resource.start > res->start - window->offset) + continue; + if (window->resource.end < res->end - window->offset) + continue; + offset = window->offset; + break; + } + + region->start = res->start - offset; + region->end = res->end - offset; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +void pcibios_bus_to_resource(struct pci_dev *dev, + struct resource *res, struct pci_bus_region *region) +{ + struct pci_controller *controller = PCI_CONTROLLER(dev); + unsigned long offset = 0; + int i; + + for (i = 0; i < controller->windows; i++) { + struct pci_window *window = &controller->window[i]; + if (!(window->resource.flags & res->flags)) + continue; + if (window->resource.start > region->start) + continue; + if (window->resource.end < region->end) + continue; + offset = window->offset; + break; + } + + res->start = region->start + offset; + res->end = region->end + offset; +} + +static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev) +{ + struct pci_bus_region region; + int i; int limit = (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) ? \ PCI_BRIDGE_RESOURCES : PCI_NUM_RESOURCES; for (i = 0; i < limit; i++) { - if (!dev->resource[i].start) + if (!dev->resource[i].flags) continue; - -#define contains(win, res) ((res)->start >= (win)->start && \ - (res)->end <= (win)->end) - - for (j = 0; j < controller->windows; j++) { - window = &controller->window[j]; - if (((dev->resource[i].flags & IORESOURCE_MEM && - window->resource.flags & IORESOURCE_MEM) || - (dev->resource[i].flags & IORESOURCE_IO && - window->resource.flags & IORESOURCE_IO)) && - contains(&window->resource, &dev->resource[i])) { - dev->resource[i].start += window->offset; - dev->resource[i].end += window->offset; - } - } + region.start = dev->resource[i].start; + region.end = dev->resource[i].end; + pcibios_bus_to_resource(dev, &dev->resource[i], ®ion); pci_claim_resource(dev, i); } } @@ -365,10 +425,10 @@ pcibios_fixup_device_resources (struct pci_dev *dev, struct pci_bus *bus) void __devinit pcibios_fixup_bus (struct pci_bus *b) { - struct list_head *ln; + struct pci_dev *dev; - for (ln = b->devices.next; ln != &b->devices; ln = ln->next) - pcibios_fixup_device_resources(pci_dev_b(ln), b); + list_for_each_entry(dev, &b->devices, bus_list) + pcibios_fixup_device_resources(dev); return; } @@ -431,6 +491,14 @@ pcibios_enable_device (struct pci_dev *dev, int mask) return acpi_pci_irq_enable(dev); } +#ifdef CONFIG_ACPI_DEALLOCATE_IRQ +void +pcibios_disable_device (struct pci_dev *dev) +{ + acpi_pci_irq_disable(dev); +} +#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ + void pcibios_align_resource (void *data, struct resource *res, unsigned long size, unsigned long align) @@ -443,6 +511,8 @@ pcibios_align_resource (void *data, struct resource *res, char * __init pcibios_setup (char *str) { + if (!strcmp(str, "routeirq")) + pci_routeirq = 1; return NULL; } @@ -451,36 +521,148 @@ pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { /* - * I/O space cannot be accessed via normal processor loads and stores on this - * platform. + * I/O space cannot be accessed via normal processor loads and + * stores on this platform. */ if (mmap_state == pci_mmap_io) /* - * XXX we could relax this for I/O spaces for which ACPI indicates that - * the space is 1-to-1 mapped. But at the moment, we don't support - * multiple PCI address spaces and the legacy I/O space is not 1-to-1 - * mapped, so this is moot. + * XXX we could relax this for I/O spaces for which ACPI + * indicates that the space is 1-to-1 mapped. But at the + * moment, we don't support multiple PCI address spaces and + * the legacy I/O space is not 1-to-1 mapped, so this is moot. */ return -EINVAL; /* - * Leave vm_pgoff as-is, the PCI space address is the physical address on this - * platform. + * 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); + vma->vm_flags |= (VM_SHM | VM_RESERVED | VM_IO); - if (write_combine) + if (write_combine && efi_range_is_wc(vma->vm_start, + vma->vm_end - vma->vm_start)) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); else vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; } +/** + * ia64_pci_get_legacy_mem - generic legacy mem routine + * @bus: bus to get legacy memory base address for + * + * Find the base of legacy memory for @bus. This is typically the first + * megabyte of bus address space for @bus or is simply 0 on platforms whose + * chipsets support legacy I/O and memory routing. Returns the base address + * or an error pointer if an error occurred. + * + * This is the ia64 generic version of this routine. Other platforms + * are free to override it with a machine vector. + */ +char *ia64_pci_get_legacy_mem(struct pci_bus *bus) +{ + return (char *)__IA64_UNCACHED_OFFSET; +} + +/** + * pci_mmap_legacy_page_range - map legacy memory space to userland + * @bus: bus whose legacy space we're mapping + * @vma: vma passed in by mmap + * + * Map legacy memory space for this device back to userspace using a machine + * vector to get the base address. + */ +int +pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma) +{ + char *addr; + + addr = pci_get_legacy_mem(bus); + if (IS_ERR(addr)) + return PTR_ERR(addr); + + vma->vm_pgoff += (unsigned long)addr >> PAGE_SHIFT; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= (VM_SHM | VM_RESERVED | VM_IO); + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +/** + * ia64_pci_legacy_read - read from legacy I/O space + * @bus: bus to read + * @port: legacy port value + * @val: caller allocated storage for returned value + * @size: number of bytes to read + * + * Simply reads @size bytes from @port and puts the result in @val. + * + * Again, this (and the write routine) are generic versions that can be + * overridden by the platform. This is necessary on platforms that don't + * support legacy I/O routing or that hard fail on legacy I/O timeouts. + */ +int ia64_pci_legacy_read(struct pci_bus *bus, u16 port, u32 *val, u8 size) +{ + int ret = size; + + switch (size) { + case 1: + *val = inb(port); + break; + case 2: + *val = inw(port); + break; + case 4: + *val = inl(port); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * ia64_pci_legacy_write - perform a legacy I/O write + * @bus: bus pointer + * @port: port to write + * @val: value to write + * @size: number of bytes to write from @val + * + * Simply writes @size bytes of @val to @port. + */ +int ia64_pci_legacy_write(struct pci_dev *bus, u16 port, u32 val, u8 size) +{ + int ret = 0; + + switch (size) { + case 1: + outb(val, port); + break; + case 2: + outw(val, port); + break; + case 4: + outl(val, port); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + /** * pci_cacheline_size - determine cacheline size for PCI devices * @dev: void