|
@@ -521,7 +521,7 @@ static void pci_release_host_bridge_dev(struct device *dev)
|
|
kfree(bridge);
|
|
kfree(bridge);
|
|
}
|
|
}
|
|
|
|
|
|
-static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
|
|
|
|
|
|
+static struct pci_host_bridge *pci_alloc_host_bridge(void)
|
|
{
|
|
{
|
|
struct pci_host_bridge *bridge;
|
|
struct pci_host_bridge *bridge;
|
|
|
|
|
|
@@ -530,7 +530,7 @@ static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
INIT_LIST_HEAD(&bridge->windows);
|
|
INIT_LIST_HEAD(&bridge->windows);
|
|
- bridge->bus = b;
|
|
|
|
|
|
+
|
|
return bridge;
|
|
return bridge;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -717,6 +717,122 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus)
|
|
dev_set_msi_domain(&bus->dev, d);
|
|
dev_set_msi_domain(&bus->dev, d);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
|
|
|
+{
|
|
|
|
+ struct device *parent = bridge->dev.parent;
|
|
|
|
+ struct resource_entry *window, *n;
|
|
|
|
+ struct pci_bus *bus, *b;
|
|
|
|
+ resource_size_t offset;
|
|
|
|
+ LIST_HEAD(resources);
|
|
|
|
+ struct resource *res;
|
|
|
|
+ char addr[64], *fmt;
|
|
|
|
+ const char *name;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ bus = pci_alloc_bus(NULL);
|
|
|
|
+ if (!bus)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ bridge->bus = bus;
|
|
|
|
+
|
|
|
|
+ /* temporarily move resources off the list */
|
|
|
|
+ list_splice_init(&bridge->windows, &resources);
|
|
|
|
+ bus->sysdata = bridge->sysdata;
|
|
|
|
+ bus->msi = bridge->msi;
|
|
|
|
+ bus->ops = bridge->ops;
|
|
|
|
+ bus->number = bus->busn_res.start = bridge->busnr;
|
|
|
|
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
|
|
|
+ bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
|
|
|
|
+ if (b) {
|
|
|
|
+ /* If we already got to this bus through a different bridge, ignore it */
|
|
|
|
+ dev_dbg(&b->dev, "bus already known\n");
|
|
|
|
+ err = -EEXIST;
|
|
|
|
+ goto free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus),
|
|
|
|
+ bridge->busnr);
|
|
|
|
+
|
|
|
|
+ err = pcibios_root_bridge_prepare(bridge);
|
|
|
|
+ if (err)
|
|
|
|
+ goto free;
|
|
|
|
+
|
|
|
|
+ err = device_register(&bridge->dev);
|
|
|
|
+ if (err)
|
|
|
|
+ put_device(&bridge->dev);
|
|
|
|
+
|
|
|
|
+ bus->bridge = get_device(&bridge->dev);
|
|
|
|
+ device_enable_async_suspend(bus->bridge);
|
|
|
|
+ pci_set_bus_of_node(bus);
|
|
|
|
+ pci_set_bus_msi_domain(bus);
|
|
|
|
+
|
|
|
|
+ if (!parent)
|
|
|
|
+ set_dev_node(bus->bridge, pcibus_to_node(bus));
|
|
|
|
+
|
|
|
|
+ bus->dev.class = &pcibus_class;
|
|
|
|
+ bus->dev.parent = bus->bridge;
|
|
|
|
+
|
|
|
|
+ dev_set_name(&bus->dev, "%04x:%02x", pci_domain_nr(bus), bus->number);
|
|
|
|
+ name = dev_name(&bus->dev);
|
|
|
|
+
|
|
|
|
+ err = device_register(&bus->dev);
|
|
|
|
+ if (err)
|
|
|
|
+ goto unregister;
|
|
|
|
+
|
|
|
|
+ pcibios_add_bus(bus);
|
|
|
|
+
|
|
|
|
+ /* Create legacy_io and legacy_mem files for this bus */
|
|
|
|
+ pci_create_legacy_files(bus);
|
|
|
|
+
|
|
|
|
+ if (parent)
|
|
|
|
+ dev_info(parent, "PCI host bridge to bus %s\n", name);
|
|
|
|
+ else
|
|
|
|
+ pr_info("PCI host bridge to bus %s\n", name);
|
|
|
|
+
|
|
|
|
+ /* Add initial resources to the bus */
|
|
|
|
+ resource_list_for_each_entry_safe(window, n, &resources) {
|
|
|
|
+ list_move_tail(&window->node, &bridge->windows);
|
|
|
|
+ offset = window->offset;
|
|
|
|
+ res = window->res;
|
|
|
|
+
|
|
|
|
+ if (res->flags & IORESOURCE_BUS)
|
|
|
|
+ pci_bus_insert_busn_res(bus, bus->number, res->end);
|
|
|
|
+ else
|
|
|
|
+ pci_bus_add_resource(bus, res, 0);
|
|
|
|
+
|
|
|
|
+ if (offset) {
|
|
|
|
+ if (resource_type(res) == IORESOURCE_IO)
|
|
|
|
+ fmt = " (bus address [%#06llx-%#06llx])";
|
|
|
|
+ else
|
|
|
|
+ fmt = " (bus address [%#010llx-%#010llx])";
|
|
|
|
+
|
|
|
|
+ snprintf(addr, sizeof(addr), fmt,
|
|
|
|
+ (unsigned long long)(res->start - offset),
|
|
|
|
+ (unsigned long long)(res->end - offset));
|
|
|
|
+ } else
|
|
|
|
+ addr[0] = '\0';
|
|
|
|
+
|
|
|
|
+ dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ down_write(&pci_bus_sem);
|
|
|
|
+ list_add_tail(&bus->node, &pci_root_buses);
|
|
|
|
+ up_write(&pci_bus_sem);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+unregister:
|
|
|
|
+ put_device(&bridge->dev);
|
|
|
|
+ device_unregister(&bridge->dev);
|
|
|
|
+
|
|
|
|
+free:
|
|
|
|
+ kfree(bus);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
|
static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
|
struct pci_dev *bridge, int busnr)
|
|
struct pci_dev *bridge, int busnr)
|
|
{
|
|
{
|
|
@@ -2130,113 +2246,43 @@ void __weak pcibios_remove_bus(struct pci_bus *bus)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
-struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
|
|
|
|
- struct pci_ops *ops, void *sysdata, struct list_head *resources)
|
|
|
|
|
|
+static struct pci_bus *pci_create_root_bus_msi(struct device *parent,
|
|
|
|
+ int bus, struct pci_ops *ops, void *sysdata,
|
|
|
|
+ struct list_head *resources, struct msi_controller *msi)
|
|
{
|
|
{
|
|
int error;
|
|
int error;
|
|
struct pci_host_bridge *bridge;
|
|
struct pci_host_bridge *bridge;
|
|
- struct pci_bus *b, *b2;
|
|
|
|
- struct resource_entry *window, *n;
|
|
|
|
- struct resource *res;
|
|
|
|
- resource_size_t offset;
|
|
|
|
- char bus_addr[64];
|
|
|
|
- char *fmt;
|
|
|
|
-
|
|
|
|
- b = pci_alloc_bus(NULL);
|
|
|
|
- if (!b)
|
|
|
|
- return NULL;
|
|
|
|
|
|
|
|
- b->sysdata = sysdata;
|
|
|
|
- b->ops = ops;
|
|
|
|
- b->number = b->busn_res.start = bus;
|
|
|
|
-#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
|
|
|
- b->domain_nr = pci_bus_find_domain_nr(b, parent);
|
|
|
|
-#endif
|
|
|
|
- b2 = pci_find_bus(pci_domain_nr(b), bus);
|
|
|
|
- if (b2) {
|
|
|
|
- /* If we already got to this bus through a different bridge, ignore it */
|
|
|
|
- dev_dbg(&b2->dev, "bus already known\n");
|
|
|
|
- goto err_out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- bridge = pci_alloc_host_bridge(b);
|
|
|
|
|
|
+ bridge = pci_alloc_host_bridge();
|
|
if (!bridge)
|
|
if (!bridge)
|
|
- goto err_out;
|
|
|
|
|
|
+ return NULL;
|
|
|
|
|
|
bridge->dev.parent = parent;
|
|
bridge->dev.parent = parent;
|
|
bridge->dev.release = pci_release_host_bridge_dev;
|
|
bridge->dev.release = pci_release_host_bridge_dev;
|
|
- dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
|
|
|
|
- error = pcibios_root_bridge_prepare(bridge);
|
|
|
|
- if (error) {
|
|
|
|
- kfree(bridge);
|
|
|
|
- goto err_out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- error = device_register(&bridge->dev);
|
|
|
|
- if (error) {
|
|
|
|
- put_device(&bridge->dev);
|
|
|
|
- goto err_out;
|
|
|
|
- }
|
|
|
|
- b->bridge = get_device(&bridge->dev);
|
|
|
|
- device_enable_async_suspend(b->bridge);
|
|
|
|
- pci_set_bus_of_node(b);
|
|
|
|
- pci_set_bus_msi_domain(b);
|
|
|
|
|
|
|
|
- if (!parent)
|
|
|
|
- set_dev_node(b->bridge, pcibus_to_node(b));
|
|
|
|
-
|
|
|
|
- b->dev.class = &pcibus_class;
|
|
|
|
- b->dev.parent = b->bridge;
|
|
|
|
- dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
|
|
|
|
- error = device_register(&b->dev);
|
|
|
|
- if (error)
|
|
|
|
- goto class_dev_reg_err;
|
|
|
|
|
|
+ list_splice_init(resources, &bridge->windows);
|
|
|
|
+ bridge->sysdata = sysdata;
|
|
|
|
+ bridge->busnr = bus;
|
|
|
|
+ bridge->ops = ops;
|
|
|
|
+ bridge->msi = msi;
|
|
|
|
|
|
- pcibios_add_bus(b);
|
|
|
|
-
|
|
|
|
- /* Create legacy_io and legacy_mem files for this bus */
|
|
|
|
- pci_create_legacy_files(b);
|
|
|
|
-
|
|
|
|
- if (parent)
|
|
|
|
- dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
|
|
|
|
- else
|
|
|
|
- printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev));
|
|
|
|
-
|
|
|
|
- /* Add initial resources to the bus */
|
|
|
|
- resource_list_for_each_entry_safe(window, n, resources) {
|
|
|
|
- list_move_tail(&window->node, &bridge->windows);
|
|
|
|
- res = window->res;
|
|
|
|
- offset = window->offset;
|
|
|
|
- if (res->flags & IORESOURCE_BUS)
|
|
|
|
- pci_bus_insert_busn_res(b, bus, res->end);
|
|
|
|
- else
|
|
|
|
- pci_bus_add_resource(b, res, 0);
|
|
|
|
- if (offset) {
|
|
|
|
- if (resource_type(res) == IORESOURCE_IO)
|
|
|
|
- fmt = " (bus address [%#06llx-%#06llx])";
|
|
|
|
- else
|
|
|
|
- fmt = " (bus address [%#010llx-%#010llx])";
|
|
|
|
- snprintf(bus_addr, sizeof(bus_addr), fmt,
|
|
|
|
- (unsigned long long) (res->start - offset),
|
|
|
|
- (unsigned long long) (res->end - offset));
|
|
|
|
- } else
|
|
|
|
- bus_addr[0] = '\0';
|
|
|
|
- dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
|
|
|
|
- }
|
|
|
|
|
|
+ error = pci_register_host_bridge(bridge);
|
|
|
|
+ if (error < 0)
|
|
|
|
+ goto err_out;
|
|
|
|
|
|
- down_write(&pci_bus_sem);
|
|
|
|
- list_add_tail(&b->node, &pci_root_buses);
|
|
|
|
- up_write(&pci_bus_sem);
|
|
|
|
|
|
+ return bridge->bus;
|
|
|
|
|
|
- return b;
|
|
|
|
-
|
|
|
|
-class_dev_reg_err:
|
|
|
|
- put_device(&bridge->dev);
|
|
|
|
- device_unregister(&bridge->dev);
|
|
|
|
err_out:
|
|
err_out:
|
|
- kfree(b);
|
|
|
|
|
|
+ kfree(bridge);
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
|
|
|
|
+ struct pci_ops *ops, void *sysdata, struct list_head *resources)
|
|
|
|
+{
|
|
|
|
+ return pci_create_root_bus_msi(parent, bus, ops, sysdata, resources,
|
|
|
|
+ NULL);
|
|
|
|
+}
|
|
EXPORT_SYMBOL_GPL(pci_create_root_bus);
|
|
EXPORT_SYMBOL_GPL(pci_create_root_bus);
|
|
|
|
|
|
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
|
|
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
|
|
@@ -2317,12 +2363,10 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus,
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- b = pci_create_root_bus(parent, bus, ops, sysdata, resources);
|
|
|
|
|
|
+ b = pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, msi);
|
|
if (!b)
|
|
if (!b)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
- b->msi = msi;
|
|
|
|
-
|
|
|
|
if (!found) {
|
|
if (!found) {
|
|
dev_info(&b->dev,
|
|
dev_info(&b->dev,
|
|
"No busn resource found for root bus, will use [bus %02x-ff]\n",
|
|
"No busn resource found for root bus, will use [bus %02x-ff]\n",
|