|
@@ -16,10 +16,47 @@
|
|
|
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
|
|
|
#define CARDBUS_RESERVE_BUSNR 3
|
|
|
|
|
|
+struct resource busn_resource = {
|
|
|
+ .name = "PCI busn",
|
|
|
+ .start = 0,
|
|
|
+ .end = 255,
|
|
|
+ .flags = IORESOURCE_BUS,
|
|
|
+};
|
|
|
+
|
|
|
/* Ugh. Need to stop exporting this to modules. */
|
|
|
LIST_HEAD(pci_root_buses);
|
|
|
EXPORT_SYMBOL(pci_root_buses);
|
|
|
|
|
|
+static LIST_HEAD(pci_domain_busn_res_list);
|
|
|
+
|
|
|
+struct pci_domain_busn_res {
|
|
|
+ struct list_head list;
|
|
|
+ struct resource res;
|
|
|
+ int domain_nr;
|
|
|
+};
|
|
|
+
|
|
|
+static struct resource *get_pci_domain_busn_res(int domain_nr)
|
|
|
+{
|
|
|
+ struct pci_domain_busn_res *r;
|
|
|
+
|
|
|
+ list_for_each_entry(r, &pci_domain_busn_res_list, list)
|
|
|
+ if (r->domain_nr == domain_nr)
|
|
|
+ return &r->res;
|
|
|
+
|
|
|
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
|
|
|
+ if (!r)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ r->domain_nr = domain_nr;
|
|
|
+ r->res.start = 0;
|
|
|
+ r->res.end = 0xff;
|
|
|
+ r->res.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED;
|
|
|
+
|
|
|
+ list_add_tail(&r->list, &pci_domain_busn_res_list);
|
|
|
+
|
|
|
+ return &r->res;
|
|
|
+}
|
|
|
+
|
|
|
static int find_anything(struct device *dev, void *data)
|
|
|
{
|
|
|
return 1;
|
|
@@ -381,8 +418,8 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
|
|
|
if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */
|
|
|
return;
|
|
|
|
|
|
- dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n",
|
|
|
- child->secondary, child->subordinate,
|
|
|
+ dev_info(&dev->dev, "PCI bridge to %pR%s\n",
|
|
|
+ &child->busn_res,
|
|
|
dev->transparent ? " (subtractive decode)" : "");
|
|
|
|
|
|
pci_bus_remove_resources(child);
|
|
@@ -599,9 +636,9 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
|
|
* Set up the primary, secondary and subordinate
|
|
|
* bus numbers.
|
|
|
*/
|
|
|
- child->number = child->secondary = busnr;
|
|
|
- child->primary = parent->secondary;
|
|
|
- child->subordinate = 0xff;
|
|
|
+ child->number = child->busn_res.start = busnr;
|
|
|
+ child->primary = parent->busn_res.start;
|
|
|
+ child->busn_res.end = 0xff;
|
|
|
|
|
|
if (!bridge)
|
|
|
return child;
|
|
@@ -643,8 +680,8 @@ static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max)
|
|
|
if (!pcibios_assign_all_busses())
|
|
|
return;
|
|
|
|
|
|
- while (parent->parent && parent->subordinate < max) {
|
|
|
- parent->subordinate = max;
|
|
|
+ while (parent->parent && parent->busn_res.end < max) {
|
|
|
+ parent->busn_res.end = max;
|
|
|
pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max);
|
|
|
parent = parent->parent;
|
|
|
}
|
|
@@ -718,15 +755,15 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
|
|
|
if (!child)
|
|
|
goto out;
|
|
|
child->primary = primary;
|
|
|
- child->subordinate = subordinate;
|
|
|
+ pci_bus_insert_busn_res(child, secondary, subordinate);
|
|
|
child->bridge_ctl = bctl;
|
|
|
}
|
|
|
|
|
|
cmax = pci_scan_child_bus(child);
|
|
|
if (cmax > max)
|
|
|
max = cmax;
|
|
|
- if (child->subordinate > max)
|
|
|
- max = child->subordinate;
|
|
|
+ if (child->busn_res.end > max)
|
|
|
+ max = child->busn_res.end;
|
|
|
} else {
|
|
|
/*
|
|
|
* We need to assign a number to this bus which we always
|
|
@@ -756,11 +793,12 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
|
|
|
child = pci_add_new_bus(bus, dev, ++max);
|
|
|
if (!child)
|
|
|
goto out;
|
|
|
+ pci_bus_insert_busn_res(child, max, 0xff);
|
|
|
}
|
|
|
buses = (buses & 0xff000000)
|
|
|
| ((unsigned int)(child->primary) << 0)
|
|
|
- | ((unsigned int)(child->secondary) << 8)
|
|
|
- | ((unsigned int)(child->subordinate) << 16);
|
|
|
+ | ((unsigned int)(child->busn_res.start) << 8)
|
|
|
+ | ((unsigned int)(child->busn_res.end) << 16);
|
|
|
|
|
|
/*
|
|
|
* yenta.c forces a secondary latency timer of 176.
|
|
@@ -805,8 +843,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
|
|
|
break;
|
|
|
while (parent->parent) {
|
|
|
if ((!pcibios_assign_all_busses()) &&
|
|
|
- (parent->subordinate > max) &&
|
|
|
- (parent->subordinate <= max+i)) {
|
|
|
+ (parent->busn_res.end > max) &&
|
|
|
+ (parent->busn_res.end <= max+i)) {
|
|
|
j = 1;
|
|
|
}
|
|
|
parent = parent->parent;
|
|
@@ -827,7 +865,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
|
|
|
/*
|
|
|
* Set the subordinate bus number to its real value.
|
|
|
*/
|
|
|
- child->subordinate = max;
|
|
|
+ pci_bus_update_busn_res_end(child, max);
|
|
|
pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
|
|
|
}
|
|
|
|
|
@@ -837,19 +875,19 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
|
|
|
|
|
|
/* Has only triggered on CardBus, fixup is in yenta_socket */
|
|
|
while (bus->parent) {
|
|
|
- if ((child->subordinate > bus->subordinate) ||
|
|
|
- (child->number > bus->subordinate) ||
|
|
|
+ if ((child->busn_res.end > bus->busn_res.end) ||
|
|
|
+ (child->number > bus->busn_res.end) ||
|
|
|
(child->number < bus->number) ||
|
|
|
- (child->subordinate < bus->number)) {
|
|
|
- dev_info(&child->dev, "[bus %02x-%02x] %s "
|
|
|
- "hidden behind%s bridge %s [bus %02x-%02x]\n",
|
|
|
- child->number, child->subordinate,
|
|
|
- (bus->number > child->subordinate &&
|
|
|
- bus->subordinate < child->number) ?
|
|
|
+ (child->busn_res.end < bus->number)) {
|
|
|
+ dev_info(&child->dev, "%pR %s "
|
|
|
+ "hidden behind%s bridge %s %pR\n",
|
|
|
+ &child->busn_res,
|
|
|
+ (bus->number > child->busn_res.end &&
|
|
|
+ bus->busn_res.end < child->number) ?
|
|
|
"wholly" : "partially",
|
|
|
bus->self->transparent ? " transparent" : "",
|
|
|
dev_name(&bus->dev),
|
|
|
- bus->number, bus->subordinate);
|
|
|
+ &bus->busn_res);
|
|
|
}
|
|
|
bus = bus->parent;
|
|
|
}
|
|
@@ -1548,7 +1586,7 @@ EXPORT_SYMBOL_GPL(pcie_bus_configure_settings);
|
|
|
|
|
|
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
|
|
|
{
|
|
|
- unsigned int devfn, pass, max = bus->secondary;
|
|
|
+ unsigned int devfn, pass, max = bus->busn_res.start;
|
|
|
struct pci_dev *dev;
|
|
|
|
|
|
dev_dbg(&bus->dev, "scanning bus\n");
|
|
@@ -1642,7 +1680,7 @@ 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->secondary = bus;
|
|
|
+ b->number = b->busn_res.start = bus;
|
|
|
|
|
|
if (parent)
|
|
|
dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
|
|
@@ -1654,7 +1692,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
|
|
|
list_move_tail(&window->list, &bridge->windows);
|
|
|
res = window->res;
|
|
|
offset = window->offset;
|
|
|
- pci_bus_add_resource(b, res, 0);
|
|
|
+ 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])";
|
|
@@ -1684,16 +1725,104 @@ err_out:
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
|
|
|
+{
|
|
|
+ struct resource *res = &b->busn_res;
|
|
|
+ struct resource *parent_res, *conflict;
|
|
|
+
|
|
|
+ res->start = bus;
|
|
|
+ res->end = bus_max;
|
|
|
+ res->flags = IORESOURCE_BUS;
|
|
|
+
|
|
|
+ if (!pci_is_root_bus(b))
|
|
|
+ parent_res = &b->parent->busn_res;
|
|
|
+ else {
|
|
|
+ parent_res = get_pci_domain_busn_res(pci_domain_nr(b));
|
|
|
+ res->flags |= IORESOURCE_PCI_FIXED;
|
|
|
+ }
|
|
|
+
|
|
|
+ conflict = insert_resource_conflict(parent_res, res);
|
|
|
+
|
|
|
+ if (conflict)
|
|
|
+ dev_printk(KERN_DEBUG, &b->dev,
|
|
|
+ "busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n",
|
|
|
+ res, pci_is_root_bus(b) ? "domain " : "",
|
|
|
+ parent_res, conflict->name, conflict);
|
|
|
+ else
|
|
|
+ dev_printk(KERN_DEBUG, &b->dev,
|
|
|
+ "busn_res: %pR is inserted under %s%pR\n",
|
|
|
+ res, pci_is_root_bus(b) ? "domain " : "",
|
|
|
+ parent_res);
|
|
|
+
|
|
|
+ return conflict == NULL;
|
|
|
+}
|
|
|
+
|
|
|
+int pci_bus_update_busn_res_end(struct pci_bus *b, int bus_max)
|
|
|
+{
|
|
|
+ struct resource *res = &b->busn_res;
|
|
|
+ struct resource old_res = *res;
|
|
|
+ resource_size_t size;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (res->start > bus_max)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ size = bus_max - res->start + 1;
|
|
|
+ ret = adjust_resource(res, res->start, size);
|
|
|
+ dev_printk(KERN_DEBUG, &b->dev,
|
|
|
+ "busn_res: %pR end %s updated to %02x\n",
|
|
|
+ &old_res, ret ? "can not be" : "is", bus_max);
|
|
|
+
|
|
|
+ if (!ret && !res->parent)
|
|
|
+ pci_bus_insert_busn_res(b, res->start, res->end);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void pci_bus_release_busn_res(struct pci_bus *b)
|
|
|
+{
|
|
|
+ struct resource *res = &b->busn_res;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!res->flags || !res->parent)
|
|
|
+ return;
|
|
|
+
|
|
|
+ ret = release_resource(res);
|
|
|
+ dev_printk(KERN_DEBUG, &b->dev,
|
|
|
+ "busn_res: %pR %s released\n",
|
|
|
+ res, ret ? "can not be" : "is");
|
|
|
+}
|
|
|
+
|
|
|
struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus,
|
|
|
struct pci_ops *ops, void *sysdata, struct list_head *resources)
|
|
|
{
|
|
|
+ struct pci_host_bridge_window *window;
|
|
|
+ bool found = false;
|
|
|
struct pci_bus *b;
|
|
|
+ int max;
|
|
|
+
|
|
|
+ list_for_each_entry(window, resources, list)
|
|
|
+ if (window->res->flags & IORESOURCE_BUS) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
b = pci_create_root_bus(parent, bus, ops, sysdata, resources);
|
|
|
if (!b)
|
|
|
return NULL;
|
|
|
|
|
|
- b->subordinate = pci_scan_child_bus(b);
|
|
|
+ if (!found) {
|
|
|
+ dev_info(&b->dev,
|
|
|
+ "No busn resource found for root bus, will use [bus %02x-ff]\n",
|
|
|
+ bus);
|
|
|
+ pci_bus_insert_busn_res(b, bus, 255);
|
|
|
+ }
|
|
|
+
|
|
|
+ max = pci_scan_child_bus(b);
|
|
|
+
|
|
|
+ if (!found)
|
|
|
+ pci_bus_update_busn_res_end(b, max);
|
|
|
+
|
|
|
pci_bus_add_devices(b);
|
|
|
return b;
|
|
|
}
|
|
@@ -1708,9 +1837,10 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,
|
|
|
|
|
|
pci_add_resource(&resources, &ioport_resource);
|
|
|
pci_add_resource(&resources, &iomem_resource);
|
|
|
+ pci_add_resource(&resources, &busn_resource);
|
|
|
b = pci_create_root_bus(parent, bus, ops, sysdata, &resources);
|
|
|
if (b)
|
|
|
- b->subordinate = pci_scan_child_bus(b);
|
|
|
+ pci_scan_child_bus(b);
|
|
|
else
|
|
|
pci_free_resource_list(&resources);
|
|
|
return b;
|
|
@@ -1725,9 +1855,10 @@ struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops,
|
|
|
|
|
|
pci_add_resource(&resources, &ioport_resource);
|
|
|
pci_add_resource(&resources, &iomem_resource);
|
|
|
+ pci_add_resource(&resources, &busn_resource);
|
|
|
b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources);
|
|
|
if (b) {
|
|
|
- b->subordinate = pci_scan_child_bus(b);
|
|
|
+ pci_scan_child_bus(b);
|
|
|
pci_bus_add_devices(b);
|
|
|
} else {
|
|
|
pci_free_resource_list(&resources);
|