diff options
Diffstat (limited to 'drivers/pci/probe.c')
-rw-r--r-- | drivers/pci/probe.c | 154 |
1 files changed, 42 insertions, 112 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5e1ca3c58a7d..658ac977cb56 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -10,18 +10,16 @@ #include <linux/module.h> #include <linux/cpumask.h> #include <linux/pci-aspm.h> +#include <asm-generic/pci-bridge.h> #include "pci.h" #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 -static LIST_HEAD(pci_host_bridges); - /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); - static int find_anything(struct device *dev, void *data) { return 1; @@ -44,82 +42,6 @@ int no_pci_devices(void) } EXPORT_SYMBOL(no_pci_devices); -static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) -{ - struct pci_bus *bus; - struct pci_host_bridge *bridge; - - bus = dev->bus; - while (bus->parent) - bus = bus->parent; - - list_for_each_entry(bridge, &pci_host_bridges, list) { - if (bridge->bus == bus) - return bridge; - } - - return NULL; -} - -static bool resource_contains(struct resource *res1, struct resource *res2) -{ - return res1->start <= res2->start && res1->end >= res2->end; -} - -void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - struct pci_host_bridge *bridge = pci_host_bridge(dev); - struct pci_host_bridge_window *window; - resource_size_t offset = 0; - - list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - - if (resource_contains(window->res, res)) { - offset = window->offset; - break; - } - } - - region->start = res->start - offset; - region->end = res->end - offset; -} -EXPORT_SYMBOL(pcibios_resource_to_bus); - -static bool region_contains(struct pci_bus_region *region1, - struct pci_bus_region *region2) -{ - return region1->start <= region2->start && region1->end >= region2->end; -} - -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) -{ - struct pci_host_bridge *bridge = pci_host_bridge(dev); - struct pci_host_bridge_window *window; - struct pci_bus_region bus_region; - resource_size_t offset = 0; - - list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - - bus_region.start = window->res->start - window->offset; - bus_region.end = window->res->end - window->offset; - - if (region_contains(&bus_region, region)) { - offset = window->offset; - break; - } - } - - res->start = region->start + offset; - res->end = region->end + offset; -} -EXPORT_SYMBOL(pcibios_bus_to_resource); - /* * PCI Bus Class */ @@ -501,6 +423,19 @@ static struct pci_bus * pci_alloc_bus(void) return b; } +static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b) +{ + struct pci_host_bridge *bridge; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (bridge) { + INIT_LIST_HEAD(&bridge->windows); + bridge->bus = b; + } + + return bridge; +} + static unsigned char pcix_bus_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCI_SPEED_66MHz_PCIX, /* 1 */ @@ -1201,7 +1136,14 @@ int pci_cfg_space_size(struct pci_dev *dev) static void pci_release_bus_bridge_dev(struct device *dev) { - kfree(dev); + struct pci_host_bridge *bridge = to_pci_host_bridge(dev); + + if (bridge->release_fn) + bridge->release_fn(bridge); + + pci_free_resource_list(&bridge->windows); + + kfree(bridge); } struct pci_dev *alloc_pci_dev(void) @@ -1395,10 +1337,13 @@ static unsigned no_next_fn(struct pci_dev *dev, unsigned fn) static int only_one_child(struct pci_bus *bus) { struct pci_dev *parent = bus->self; + if (!parent || !pci_is_pcie(parent)) return 0; - if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT || - parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) + if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT) + return 1; + if (parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && + !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS)) return 1; return 0; } @@ -1650,28 +1595,19 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, int error; struct pci_host_bridge *bridge; struct pci_bus *b, *b2; - struct device *dev; struct pci_host_bridge_window *window, *n; struct resource *res; resource_size_t offset; char bus_addr[64]; char *fmt; - bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return NULL; b = pci_alloc_bus(); if (!b) - goto err_bus; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - goto err_dev; + return NULL; b->sysdata = sysdata; b->ops = ops; - b2 = pci_find_bus(pci_domain_nr(b), bus); if (b2) { /* If we already got to this bus through a different bridge, ignore it */ @@ -1679,13 +1615,17 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; } - dev->parent = parent; - dev->release = pci_release_bus_bridge_dev; - dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); - error = device_register(dev); + bridge = pci_alloc_host_bridge(b); + if (!bridge) + goto err_out; + + 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 = device_register(&bridge->dev); if (error) - goto dev_reg_err; - b->bridge = get_device(dev); + goto bridge_dev_reg_err; + b->bridge = get_device(&bridge->dev); device_enable_async_suspend(b->bridge); pci_set_bus_of_node(b); @@ -1704,9 +1644,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; - bridge->bus = b; - INIT_LIST_HEAD(&bridge->windows); - if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else @@ -1732,25 +1669,18 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, } down_write(&pci_bus_sem); - list_add_tail(&bridge->list, &pci_host_bridges); list_add_tail(&b->node, &pci_root_buses); up_write(&pci_bus_sem); return b; class_dev_reg_err: - device_unregister(dev); -dev_reg_err: - down_write(&pci_bus_sem); - list_del(&bridge->list); - list_del(&b->node); - up_write(&pci_bus_sem); + put_device(&bridge->dev); + device_unregister(&bridge->dev); +bridge_dev_reg_err: + kfree(bridge); err_out: - kfree(dev); -err_dev: kfree(b); -err_bus: - kfree(bridge); return NULL; } |