X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fprobe.c;h=663ce3aa73bfedaedb024a8721f9b81229e5e428;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=e88017d6fd38922c031fc5f6ad74cfba89c8aa02;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e88017d6f..663ce3aa7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2,6 +2,7 @@ * probe.c - PCI detection and setup code */ +#include #include #include #include @@ -27,12 +28,73 @@ EXPORT_SYMBOL(pci_root_buses); LIST_HEAD(pci_devices); +#ifdef HAVE_PCI_LEGACY +/** + * pci_create_legacy_files - create legacy I/O port and memory files + * @b: bus to create files under + * + * Some platforms allow access to legacy I/O port and ISA memory space on + * a per-bus basis. This routine creates the files and ties them into + * their associated read, write and mmap files from pci-sysfs.c + */ +static void pci_create_legacy_files(struct pci_bus *b) +{ + b->legacy_io = kmalloc(sizeof(struct bin_attribute) * 2, + GFP_ATOMIC); + if (b->legacy_io) { + memset(b->legacy_io, 0, sizeof(struct bin_attribute) * 2); + b->legacy_io->attr.name = "legacy_io"; + b->legacy_io->size = 0xffff; + b->legacy_io->attr.mode = S_IRUSR | S_IWUSR; + b->legacy_io->attr.owner = THIS_MODULE; + b->legacy_io->read = pci_read_legacy_io; + b->legacy_io->write = pci_write_legacy_io; + class_device_create_bin_file(&b->class_dev, b->legacy_io); + + /* Allocated above after the legacy_io struct */ + b->legacy_mem = b->legacy_io + 1; + b->legacy_mem->attr.name = "legacy_mem"; + b->legacy_mem->size = 1024*1024; + b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR; + b->legacy_mem->attr.owner = THIS_MODULE; + b->legacy_mem->mmap = pci_mmap_legacy_mem; + class_device_create_bin_file(&b->class_dev, b->legacy_mem); + } +} + +void pci_remove_legacy_files(struct pci_bus *b) +{ + class_device_remove_bin_file(&b->class_dev, b->legacy_io); + class_device_remove_bin_file(&b->class_dev, b->legacy_mem); + kfree(b->legacy_io); /* both are allocated here */ +} +#else /* !HAVE_PCI_LEGACY */ +static inline void pci_create_legacy_files(struct pci_bus *bus) { return; } +void pci_remove_legacy_files(struct pci_bus *bus) { return; } +#endif /* HAVE_PCI_LEGACY */ + +/* + * PCI Bus Class Devices + */ +static ssize_t pci_bus_show_cpuaffinity(struct class_device *class_dev, char *buf) +{ + cpumask_t cpumask = pcibus_to_cpumask((to_pci_bus(class_dev))->number); + int ret; + + ret = cpumask_scnprintf(buf, PAGE_SIZE, cpumask); + if (ret < PAGE_SIZE) + buf[ret++] = '\n'; + return ret; +} +CLASS_DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL); + /* * PCI Bus Class */ static void release_pcibus_dev(struct class_device *class_dev) { struct pci_bus *pci_bus = to_pci_bus(class_dev); + if (pci_bus->bridge) put_device(pci_bus->bridge); kfree(pci_bus); @@ -49,21 +111,6 @@ static int __init pcibus_class_init(void) } postcore_initcall(pcibus_class_init); -/* - * PCI Bus Class Devices - */ -static ssize_t pci_bus_show_cpuaffinity(struct class_device *class_dev, char *buf) -{ - cpumask_t cpumask = pcibus_to_cpumask((to_pci_bus(class_dev))->number); - int ret; - - ret = cpumask_scnprintf(buf, PAGE_SIZE, cpumask); - if (ret < PAGE_SIZE) - buf[ret++] = '\n'; - return ret; -} -static CLASS_DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL); - /* * Translate the low bits of the PCI base * to the resource type @@ -144,9 +191,11 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) 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); + sz = pci_size(l, sz, 0xffffffff); + if (sz) { + /* This BAR needs > 4GB? Wow. */ + res->end |= (unsigned long)sz<<32; + } #else if (l) { printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", pci_name(dev)); @@ -170,7 +219,7 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) if (sz && sz != 0xffffffff) { sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK); if (sz) { - res->flags = (l & PCI_ROM_ADDRESS_ENABLE) | + res->flags = (l & IORESOURCE_ROM_ENABLE) | IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_READONLY | IORESOURCE_CACHEABLE; res->start = l & PCI_ROM_ADDRESS_MASK; @@ -243,15 +292,23 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) 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); + + /* + * Some bridges set the base > limit by default, and some + * (broken) BIOSes do not initialize them. If we find + * this, just assume they are not being used. + */ + if (mem_base_hi <= mem_limit_hi) { #if BITS_PER_LONG == 64 - base |= ((long) mem_base_hi) << 32; - limit |= ((long) mem_limit_hi) << 32; + 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; - } + if (mem_base_hi || mem_limit_hi) { + printk(KERN_ERR "PCI: Unable to handle 64-bit address space for bridge %s\n", pci_name(dev)); + return; + } #endif + } } if (base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; @@ -326,6 +383,22 @@ struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_de return child; } +static void pci_enable_crs(struct pci_dev *dev) +{ + u16 cap, rpctl; + int rpcap = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!rpcap) + return; + + pci_read_config_word(dev, rpcap + PCI_CAP_FLAGS, &cap); + if (((cap & PCI_EXP_FLAGS_TYPE) >> 4) != PCI_EXP_TYPE_ROOT_PORT) + return; + + pci_read_config_word(dev, rpcap + PCI_EXP_RTCTL, &rpctl); + rpctl |= PCI_EXP_RTCTL_CRSSVE; + pci_write_config_word(dev, rpcap + PCI_EXP_RTCTL, rpctl); +} + unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus); /* @@ -356,6 +429,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT); + pci_enable_crs(dev); + if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) { unsigned int cmax, busnr; /* @@ -365,6 +440,17 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max if (pass) return max; busnr = (buses >> 8) & 0xFF; + + /* + * If we already got to this bus through a different bridge, + * ignore it. This can happen with the i450NX chipset. + */ + if (pci_find_bus(pci_domain_nr(bus), busnr)) { + printk(KERN_INFO "PCI: Bus %04x:%02x already known\n", + pci_domain_nr(bus), busnr); + return max; + } + child = pci_alloc_child_bus(bus, dev, busnr); if (!child) return max; @@ -478,6 +564,10 @@ static int pci_setup_device(struct pci_dev * dev) /* "Unknown power state" */ dev->current_state = 4; + /* Early fixups, before probing the BARs */ + pci_fixup_device(pci_fixup_early, dev); + class = dev->class >> 8; + switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) @@ -589,9 +679,7 @@ pci_scan_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; u32 l; u8 hdr_type; - - if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type)) - return NULL; + int delay = 1; if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) return NULL; @@ -601,6 +689,25 @@ pci_scan_device(struct pci_bus *bus, int devfn) l == 0x0000ffff || l == 0xffff0000) return NULL; + /* Configuration request Retry Status */ + while (l == 0xffff0001) { + msleep(delay); + delay *= 2; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) + return NULL; + /* Card hasn't responded in 60 seconds? Must be stuck. */ + if (delay > 60 * 1000) { + printk(KERN_WARNING "Device %04x:%02x:%02x.%d not " + "responding\n", pci_domain_nr(bus), + bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn)); + return NULL; + } + } + + if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type)) + return NULL; + dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); if (!dev) return NULL; @@ -753,6 +860,7 @@ unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus) struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata) { + int error; struct pci_bus *b; struct device *dev; @@ -771,10 +879,8 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, if (pci_find_bus(pci_domain_nr(b), bus)) { /* If we already got to this bus through a different bridge, ignore it */ - DBG("PCI: Bus %02x already known\n", bus); - kfree(dev); - kfree(b); - return NULL; + DBG("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus); + goto err_out; } list_add_tail(&b->node, &pci_root_buses); @@ -782,15 +888,26 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, dev->parent = parent; dev->release = pci_release_bus_bridge_dev; sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus); - device_register(dev); + error = device_register(dev); + if (error) + goto dev_reg_err; b->bridge = get_device(dev); b->class_dev.class = &pcibus_class; sprintf(b->class_dev.class_id, "%04x:%02x", pci_domain_nr(b), bus); - class_device_register(&b->class_dev); - class_device_create_file(&b->class_dev, &class_device_attr_cpuaffinity); + error = class_device_register(&b->class_dev); + if (error) + goto class_dev_reg_err; + error = class_device_create_file(&b->class_dev, &class_device_attr_cpuaffinity); + if (error) + goto class_dev_create_file_err; + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(b); - sysfs_create_link(&b->class_dev.kobj, &b->bridge->kobj, "bridge"); + error = sysfs_create_link(&b->class_dev.kobj, &b->bridge->kobj, "bridge"); + if (error) + goto sys_create_link_err; b->number = b->secondary = bus; b->resource[0] = &ioport_resource; @@ -801,6 +918,19 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, pci_bus_add_devices(b); return b; + +sys_create_link_err: + class_device_remove_file(&b->class_dev, &class_device_attr_cpuaffinity); +class_dev_create_file_err: + class_device_unregister(&b->class_dev); +class_dev_reg_err: + device_unregister(dev); +dev_reg_err: + list_del(&b->node); +err_out: + kfree(dev); + kfree(b); + return NULL; } EXPORT_SYMBOL(pci_scan_bus_parented);