Skip to content

Commit 37d6a0a

Browse files
arndbBjorn Helgaas
authored and
Bjorn Helgaas
committed
PCI: Add pci_register_host_bridge() interface
Make the existing pci_host_bridge structure a proper device that is usable by PCI host drivers in a more standard way. In addition to the existing pci_scan_bus(), pci_scan_root_bus(), pci_scan_root_bus_msi(), and pci_create_root_bus() interfaces, this unfortunately means having to add yet another interface doing basically the same thing, and add some extra code in the initial step. However, this time it's more likely to be extensible enough that we won't have to do another one again in the future, and we should be able to reduce code much more as a result. The main idea is to pull the allocation of 'struct pci_host_bridge' out of the registration, and let individual host drivers and architecture code fill the members before calling the registration function. There are a number of things we can do based on this: * Use a single memory allocation for the driver-specific structure and the generic PCI host bridge * consolidate the contents of driver-specific structures by moving them into pci_host_bridge * Add a consistent interface for removing a PCI host bridge again when unloading a host driver module * Replace the architecture specific __weak pcibios_*() functions with callbacks in a pci_host_bridge device * Move common boilerplate code from host drivers into the generic function, based on contents of the structure * Extend pci_host_bridge with additional members when needed without having to add arguments to pci_scan_*(). * Move members of struct pci_bus into pci_host_bridge to avoid having lots of identical copies. Signed-off-by: Arnd Bergmann <[email protected]> Signed-off-by: Thierry Reding <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 1001354 commit 37d6a0a

File tree

2 files changed

+145
-97
lines changed

2 files changed

+145
-97
lines changed

drivers/pci/probe.c

Lines changed: 141 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ static void pci_release_host_bridge_dev(struct device *dev)
521521
kfree(bridge);
522522
}
523523

524-
static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
524+
static struct pci_host_bridge *pci_alloc_host_bridge(void)
525525
{
526526
struct pci_host_bridge *bridge;
527527

@@ -530,7 +530,7 @@ static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
530530
return NULL;
531531

532532
INIT_LIST_HEAD(&bridge->windows);
533-
bridge->bus = b;
533+
534534
return bridge;
535535
}
536536

@@ -717,6 +717,122 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus)
717717
dev_set_msi_domain(&bus->dev, d);
718718
}
719719

720+
static int pci_register_host_bridge(struct pci_host_bridge *bridge)
721+
{
722+
struct device *parent = bridge->dev.parent;
723+
struct resource_entry *window, *n;
724+
struct pci_bus *bus, *b;
725+
resource_size_t offset;
726+
LIST_HEAD(resources);
727+
struct resource *res;
728+
char addr[64], *fmt;
729+
const char *name;
730+
int err;
731+
732+
bus = pci_alloc_bus(NULL);
733+
if (!bus)
734+
return -ENOMEM;
735+
736+
bridge->bus = bus;
737+
738+
/* temporarily move resources off the list */
739+
list_splice_init(&bridge->windows, &resources);
740+
bus->sysdata = bridge->sysdata;
741+
bus->msi = bridge->msi;
742+
bus->ops = bridge->ops;
743+
bus->number = bus->busn_res.start = bridge->busnr;
744+
#ifdef CONFIG_PCI_DOMAINS_GENERIC
745+
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
746+
#endif
747+
748+
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
749+
if (b) {
750+
/* If we already got to this bus through a different bridge, ignore it */
751+
dev_dbg(&b->dev, "bus already known\n");
752+
err = -EEXIST;
753+
goto free;
754+
}
755+
756+
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus),
757+
bridge->busnr);
758+
759+
err = pcibios_root_bridge_prepare(bridge);
760+
if (err)
761+
goto free;
762+
763+
err = device_register(&bridge->dev);
764+
if (err)
765+
put_device(&bridge->dev);
766+
767+
bus->bridge = get_device(&bridge->dev);
768+
device_enable_async_suspend(bus->bridge);
769+
pci_set_bus_of_node(bus);
770+
pci_set_bus_msi_domain(bus);
771+
772+
if (!parent)
773+
set_dev_node(bus->bridge, pcibus_to_node(bus));
774+
775+
bus->dev.class = &pcibus_class;
776+
bus->dev.parent = bus->bridge;
777+
778+
dev_set_name(&bus->dev, "%04x:%02x", pci_domain_nr(bus), bus->number);
779+
name = dev_name(&bus->dev);
780+
781+
err = device_register(&bus->dev);
782+
if (err)
783+
goto unregister;
784+
785+
pcibios_add_bus(bus);
786+
787+
/* Create legacy_io and legacy_mem files for this bus */
788+
pci_create_legacy_files(bus);
789+
790+
if (parent)
791+
dev_info(parent, "PCI host bridge to bus %s\n", name);
792+
else
793+
pr_info("PCI host bridge to bus %s\n", name);
794+
795+
/* Add initial resources to the bus */
796+
resource_list_for_each_entry_safe(window, n, &resources) {
797+
list_move_tail(&window->node, &bridge->windows);
798+
offset = window->offset;
799+
res = window->res;
800+
801+
if (res->flags & IORESOURCE_BUS)
802+
pci_bus_insert_busn_res(bus, bus->number, res->end);
803+
else
804+
pci_bus_add_resource(bus, res, 0);
805+
806+
if (offset) {
807+
if (resource_type(res) == IORESOURCE_IO)
808+
fmt = " (bus address [%#06llx-%#06llx])";
809+
else
810+
fmt = " (bus address [%#010llx-%#010llx])";
811+
812+
snprintf(addr, sizeof(addr), fmt,
813+
(unsigned long long)(res->start - offset),
814+
(unsigned long long)(res->end - offset));
815+
} else
816+
addr[0] = '\0';
817+
818+
dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
819+
}
820+
821+
down_write(&pci_bus_sem);
822+
list_add_tail(&bus->node, &pci_root_buses);
823+
up_write(&pci_bus_sem);
824+
825+
return 0;
826+
827+
unregister:
828+
put_device(&bridge->dev);
829+
device_unregister(&bridge->dev);
830+
831+
free:
832+
kfree(bus);
833+
return err;
834+
}
835+
720836
static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
721837
struct pci_dev *bridge, int busnr)
722838
{
@@ -2130,113 +2246,43 @@ void __weak pcibios_remove_bus(struct pci_bus *bus)
21302246
{
21312247
}
21322248

2133-
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
2134-
struct pci_ops *ops, void *sysdata, struct list_head *resources)
2249+
static struct pci_bus *pci_create_root_bus_msi(struct device *parent,
2250+
int bus, struct pci_ops *ops, void *sysdata,
2251+
struct list_head *resources, struct msi_controller *msi)
21352252
{
21362253
int error;
21372254
struct pci_host_bridge *bridge;
2138-
struct pci_bus *b, *b2;
2139-
struct resource_entry *window, *n;
2140-
struct resource *res;
2141-
resource_size_t offset;
2142-
char bus_addr[64];
2143-
char *fmt;
2144-
2145-
b = pci_alloc_bus(NULL);
2146-
if (!b)
2147-
return NULL;
21482255

2149-
b->sysdata = sysdata;
2150-
b->ops = ops;
2151-
b->number = b->busn_res.start = bus;
2152-
#ifdef CONFIG_PCI_DOMAINS_GENERIC
2153-
b->domain_nr = pci_bus_find_domain_nr(b, parent);
2154-
#endif
2155-
b2 = pci_find_bus(pci_domain_nr(b), bus);
2156-
if (b2) {
2157-
/* If we already got to this bus through a different bridge, ignore it */
2158-
dev_dbg(&b2->dev, "bus already known\n");
2159-
goto err_out;
2160-
}
2161-
2162-
bridge = pci_alloc_host_bridge(b);
2256+
bridge = pci_alloc_host_bridge();
21632257
if (!bridge)
2164-
goto err_out;
2258+
return NULL;
21652259

21662260
bridge->dev.parent = parent;
21672261
bridge->dev.release = pci_release_host_bridge_dev;
2168-
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
2169-
error = pcibios_root_bridge_prepare(bridge);
2170-
if (error) {
2171-
kfree(bridge);
2172-
goto err_out;
2173-
}
2174-
2175-
error = device_register(&bridge->dev);
2176-
if (error) {
2177-
put_device(&bridge->dev);
2178-
goto err_out;
2179-
}
2180-
b->bridge = get_device(&bridge->dev);
2181-
device_enable_async_suspend(b->bridge);
2182-
pci_set_bus_of_node(b);
2183-
pci_set_bus_msi_domain(b);
21842262

2185-
if (!parent)
2186-
set_dev_node(b->bridge, pcibus_to_node(b));
2187-
2188-
b->dev.class = &pcibus_class;
2189-
b->dev.parent = b->bridge;
2190-
dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
2191-
error = device_register(&b->dev);
2192-
if (error)
2193-
goto class_dev_reg_err;
2263+
list_splice_init(resources, &bridge->windows);
2264+
bridge->sysdata = sysdata;
2265+
bridge->busnr = bus;
2266+
bridge->ops = ops;
2267+
bridge->msi = msi;
21942268

2195-
pcibios_add_bus(b);
2196-
2197-
/* Create legacy_io and legacy_mem files for this bus */
2198-
pci_create_legacy_files(b);
2199-
2200-
if (parent)
2201-
dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
2202-
else
2203-
printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev));
2204-
2205-
/* Add initial resources to the bus */
2206-
resource_list_for_each_entry_safe(window, n, resources) {
2207-
list_move_tail(&window->node, &bridge->windows);
2208-
res = window->res;
2209-
offset = window->offset;
2210-
if (res->flags & IORESOURCE_BUS)
2211-
pci_bus_insert_busn_res(b, bus, res->end);
2212-
else
2213-
pci_bus_add_resource(b, res, 0);
2214-
if (offset) {
2215-
if (resource_type(res) == IORESOURCE_IO)
2216-
fmt = " (bus address [%#06llx-%#06llx])";
2217-
else
2218-
fmt = " (bus address [%#010llx-%#010llx])";
2219-
snprintf(bus_addr, sizeof(bus_addr), fmt,
2220-
(unsigned long long) (res->start - offset),
2221-
(unsigned long long) (res->end - offset));
2222-
} else
2223-
bus_addr[0] = '\0';
2224-
dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
2225-
}
2269+
error = pci_register_host_bridge(bridge);
2270+
if (error < 0)
2271+
goto err_out;
22262272

2227-
down_write(&pci_bus_sem);
2228-
list_add_tail(&b->node, &pci_root_buses);
2229-
up_write(&pci_bus_sem);
2273+
return bridge->bus;
22302274

2231-
return b;
2232-
2233-
class_dev_reg_err:
2234-
put_device(&bridge->dev);
2235-
device_unregister(&bridge->dev);
22362275
err_out:
2237-
kfree(b);
2276+
kfree(bridge);
22382277
return NULL;
22392278
}
2279+
2280+
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
2281+
struct pci_ops *ops, void *sysdata, struct list_head *resources)
2282+
{
2283+
return pci_create_root_bus_msi(parent, bus, ops, sysdata, resources,
2284+
NULL);
2285+
}
22402286
EXPORT_SYMBOL_GPL(pci_create_root_bus);
22412287

22422288
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,
23172363
break;
23182364
}
23192365

2320-
b = pci_create_root_bus(parent, bus, ops, sysdata, resources);
2366+
b = pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, msi);
23212367
if (!b)
23222368
return NULL;
23232369

2324-
b->msi = msi;
2325-
23262370
if (!found) {
23272371
dev_info(&b->dev,
23282372
"No busn resource found for root bus, will use [bus %02x-ff]\n",

include/linux/pci.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,13 @@ static inline int pci_channel_offline(struct pci_dev *pdev)
420420
struct pci_host_bridge {
421421
struct device dev;
422422
struct pci_bus *bus; /* root bus */
423+
struct pci_ops *ops;
424+
void *sysdata;
425+
int busnr;
423426
struct list_head windows; /* resource_entry */
424427
void (*release_fn)(struct pci_host_bridge *);
425428
void *release_data;
429+
struct msi_controller *msi;
426430
unsigned int ignore_reset_delay:1; /* for entire hierarchy */
427431
/* Resource alignment requirements */
428432
resource_size_t (*align_resource)(struct pci_dev *dev,

0 commit comments

Comments
 (0)