Skip to content

Commit

Permalink
PCI: add struct pci_host_bridge_window with CPU/bus address offset
Browse files Browse the repository at this point in the history
Some PCI host bridges apply an address offset, so bus addresses on PCI are
different from CPU addresses.  This patch adds a way for architectures to
tell the PCI core about this offset.  For example:

    LIST_HEAD(resources);
    pci_add_resource_offset(&resources, host->io_space, host->io_offset);
    pci_add_resource_offset(&resources, host->mem_space, host->mem_offset);
    pci_scan_root_bus(parent, bus, ops, sysdata, &resources);

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
  • Loading branch information
Bjorn Helgaas committed Feb 24, 2012
1 parent 5a21d70 commit 0efd5aa
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 20 deletions.
30 changes: 19 additions & 11 deletions drivers/pci/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,36 @@

#include "pci.h"

void pci_add_resource(struct list_head *resources, struct resource *res)
void pci_add_resource_offset(struct list_head *resources, struct resource *res,
resource_size_t offset)
{
struct pci_bus_resource *bus_res;
struct pci_host_bridge_window *window;

bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
if (!bus_res) {
printk(KERN_ERR "PCI: can't add bus resource %pR\n", res);
window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL);
if (!window) {
printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
return;
}

bus_res->res = res;
list_add_tail(&bus_res->list, resources);
window->res = res;
window->offset = offset;
list_add_tail(&window->list, resources);
}
EXPORT_SYMBOL(pci_add_resource_offset);

void pci_add_resource(struct list_head *resources, struct resource *res)
{
pci_add_resource_offset(resources, res, 0);
}
EXPORT_SYMBOL(pci_add_resource);

void pci_free_resource_list(struct list_head *resources)
{
struct pci_bus_resource *bus_res, *tmp;
struct pci_host_bridge_window *window, *tmp;

list_for_each_entry_safe(bus_res, tmp, resources, list) {
list_del(&bus_res->list);
kfree(bus_res);
list_for_each_entry_safe(window, tmp, resources, list) {
list_del(&window->list);
kfree(window);
}
}
EXPORT_SYMBOL(pci_free_resource_list);
Expand Down
32 changes: 23 additions & 9 deletions drivers/pci/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -1562,12 +1562,15 @@ unsigned int __devinit pci_scan_child_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)
{
int error, i;
int error;
struct pci_host_bridge *bridge;
struct pci_bus *b, *b2;
struct device *dev;
struct pci_bus_resource *bus_res, *n;
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)
Expand Down Expand Up @@ -1617,19 +1620,30 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
b->number = b->secondary = bus;

bridge->bus = b;

/* Add initial resources to the bus */
list_for_each_entry_safe(bus_res, n, resources, list)
list_move_tail(&bus_res->list, &b->resources);
INIT_LIST_HEAD(&bridge->windows);

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));

pci_bus_for_each_resource(b, res, i) {
if (res)
dev_info(&b->dev, "root bus resource %pR\n", res);
/* Add initial resources to the bus */
list_for_each_entry_safe(window, n, resources, list) {
list_move_tail(&window->list, &bridge->windows);
res = window->res;
offset = window->offset;
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);
}

down_write(&pci_bus_sem);
Expand Down
9 changes: 9 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,16 @@ static inline int pci_channel_offline(struct pci_dev *pdev)
return (pdev->error_state != pci_channel_io_normal);
}

struct pci_host_bridge_window {
struct list_head list;
struct resource *res; /* host bridge aperture (CPU address) */
resource_size_t offset; /* bus address + offset = CPU address */
};

struct pci_host_bridge {
struct list_head list;
struct pci_bus *bus; /* root bus */
struct list_head windows; /* pci_host_bridge_windows */
};

/*
Expand Down Expand Up @@ -901,6 +908,8 @@ void pci_release_selected_regions(struct pci_dev *, int);

/* drivers/pci/bus.c */
void pci_add_resource(struct list_head *resources, struct resource *res);
void pci_add_resource_offset(struct list_head *resources, struct resource *res,
resource_size_t offset);
void pci_free_resource_list(struct list_head *resources);
void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, unsigned int flags);
struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n);
Expand Down

0 comments on commit 0efd5aa

Please sign in to comment.