diff options
Diffstat (limited to 'drivers/pci/probe.c')
-rw-r--r-- | drivers/pci/probe.c | 103 |
1 files changed, 67 insertions, 36 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2dcd22d9c816..b494066ef32f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -623,6 +623,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, { struct pci_bus *child; int i; + int ret; /* * Allocate a new bus, and inherit stuff from the parent.. @@ -637,8 +638,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->bus_flags = parent->bus_flags; /* initialize some portions of the bus device, but don't register it - * now as the parent is not properly set up yet. This device will get - * registered later in pci_bus_add_devices() + * now as the parent is not properly set up yet. */ child->dev.class = &pcibus_class; dev_set_name(&child->dev, "%04x:%02x", pci_domain_nr(child), busnr); @@ -651,11 +651,14 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->primary = parent->busn_res.start; child->busn_res.end = 0xff; - if (!bridge) - return child; + if (!bridge) { + child->dev.parent = parent->bridge; + goto add_dev; + } child->self = bridge; child->bridge = get_device(&bridge->dev); + child->dev.parent = child->bridge; pci_set_bus_of_node(child); pci_set_bus_speed(child); @@ -666,6 +669,13 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, } bridge->subordinate = child; +add_dev: + ret = device_register(&child->dev); + WARN_ON(ret < 0); + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(child); + return child; } @@ -1285,7 +1295,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_vpd_pci22_init(dev); /* Alternative Routing-ID Forwarding */ - pci_enable_ari(dev); + pci_configure_ari(dev); /* Single Root I/O Virtualization */ pci_iov_init(dev); @@ -1296,10 +1306,12 @@ static void pci_init_capabilities(struct pci_dev *dev) void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { + int ret; + device_initialize(&dev->dev); dev->dev.release = pci_release_dev; - pci_dev_get(dev); + set_dev_node(&dev->dev, pcibus_to_node(bus)); dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; @@ -1326,6 +1338,17 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) down_write(&pci_bus_sem); list_add_tail(&dev->bus_list, &bus->devices); up_write(&pci_bus_sem); + + pci_fixup_device(pci_fixup_final, dev); + ret = pcibios_add_device(dev); + WARN_ON(ret < 0); + + /* Notifier could use PCI capabilities */ + dev->match_driver = false; + ret = device_add(&dev->dev); + WARN_ON(ret < 0); + + pci_proc_attach_device(dev); } struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) @@ -1348,31 +1371,31 @@ struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) } EXPORT_SYMBOL(pci_scan_single_device); -static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn) +static unsigned next_fn(struct pci_bus *bus, struct pci_dev *dev, unsigned fn) { - u16 cap; - unsigned pos, next_fn; + int pos; + u16 cap = 0; + unsigned next_fn; - if (!dev) - return 0; + if (pci_ari_enabled(bus)) { + if (!dev) + return 0; + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); + if (!pos) + return 0; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); - if (!pos) - return 0; - pci_read_config_word(dev, pos + 4, &cap); - next_fn = cap >> 8; - if (next_fn <= fn) - return 0; - return next_fn; -} + pci_read_config_word(dev, pos + PCI_ARI_CAP, &cap); + next_fn = PCI_ARI_CAP_NFN(cap); + if (next_fn <= fn) + return 0; /* protect against malformed list */ -static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn) -{ - return (fn + 1) % 8; -} + return next_fn; + } + + /* dev may be NULL for non-contiguous multifunction devices */ + if (!dev || dev->multifunction) + return (fn + 1) % 8; -static unsigned no_next_fn(struct pci_dev *dev, unsigned fn) -{ return 0; } @@ -1405,7 +1428,6 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) { unsigned fn, nr = 0; struct pci_dev *dev; - unsigned (*next_fn)(struct pci_dev *, unsigned) = no_next_fn; if (only_one_child(bus) && (devfn > 0)) return 0; /* Already scanned the entire slot */ @@ -1416,12 +1438,7 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) if (!dev->is_added) nr++; - if (pci_ari_enabled(bus)) - next_fn = next_ari_fn; - else if (dev->multifunction) - next_fn = next_trad_fn; - - for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) { + for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) { dev = pci_scan_single_device(bus, devfn + fn); if (dev) { if (!dev->is_added) @@ -1632,6 +1649,18 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) return max; } +/** + * pcibios_root_bridge_prepare - Platform-specific host bridge setup. + * @bridge: Host bridge to set up. + * + * Default empty implementation. Replace with an architecture-specific setup + * routine, if necessary. + */ +int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + return 0; +} + struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { @@ -1644,13 +1673,13 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, char bus_addr[64]; char *fmt; - b = pci_alloc_bus(); if (!b) return NULL; b->sysdata = sysdata; b->ops = ops; + b->number = b->busn_res.start = bus; b2 = pci_find_bus(pci_domain_nr(b), bus); if (b2) { /* If we already got to this bus through a different bridge, ignore it */ @@ -1665,6 +1694,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_bus_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + error = pcibios_root_bridge_prepare(bridge); + if (error) + goto bridge_dev_reg_err; + error = device_register(&bridge->dev); if (error) goto bridge_dev_reg_err; @@ -1685,8 +1718,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(b); - b->number = b->busn_res.start = bus; - if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else |