|
@@ -604,6 +604,88 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus)
|
|
|
}
|
|
|
EXPORT_SYMBOL(pci_bus_assign_resources);
|
|
|
|
|
|
+static void pci_bridge_release_resources(struct pci_bus *bus,
|
|
|
+ unsigned long type)
|
|
|
+{
|
|
|
+ int idx;
|
|
|
+ bool changed = false;
|
|
|
+ struct pci_dev *dev;
|
|
|
+ struct resource *r;
|
|
|
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
|
|
|
+ IORESOURCE_PREFETCH;
|
|
|
+
|
|
|
+ dev = bus->self;
|
|
|
+ for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END;
|
|
|
+ idx++) {
|
|
|
+ r = &dev->resource[idx];
|
|
|
+ if ((r->flags & type_mask) != type)
|
|
|
+ continue;
|
|
|
+ if (!r->parent)
|
|
|
+ continue;
|
|
|
+ /*
|
|
|
+ * if there are children under that, we should release them
|
|
|
+ * all
|
|
|
+ */
|
|
|
+ release_child_resources(r);
|
|
|
+ if (!release_resource(r)) {
|
|
|
+ dev_printk(KERN_DEBUG, &dev->dev,
|
|
|
+ "resource %d %pR released\n", idx, r);
|
|
|
+ /* keep the old size */
|
|
|
+ r->end = resource_size(r) - 1;
|
|
|
+ r->start = 0;
|
|
|
+ r->flags = 0;
|
|
|
+ changed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (changed) {
|
|
|
+ /* avoiding touch the one without PREF */
|
|
|
+ if (type & IORESOURCE_PREFETCH)
|
|
|
+ type = IORESOURCE_PREFETCH;
|
|
|
+ __pci_setup_bridge(bus, type);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+enum release_type {
|
|
|
+ leaf_only,
|
|
|
+ whole_subtree,
|
|
|
+};
|
|
|
+/*
|
|
|
+ * try to release pci bridge resources that is from leaf bridge,
|
|
|
+ * so we can allocate big new one later
|
|
|
+ */
|
|
|
+static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus,
|
|
|
+ unsigned long type,
|
|
|
+ enum release_type rel_type)
|
|
|
+{
|
|
|
+ struct pci_dev *dev;
|
|
|
+ bool is_leaf_bridge = true;
|
|
|
+
|
|
|
+ list_for_each_entry(dev, &bus->devices, bus_list) {
|
|
|
+ struct pci_bus *b = dev->subordinate;
|
|
|
+ if (!b)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ is_leaf_bridge = false;
|
|
|
+
|
|
|
+ if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (rel_type == whole_subtree)
|
|
|
+ pci_bus_release_bridge_resources(b, type,
|
|
|
+ whole_subtree);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pci_is_root_bus(bus))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if ((rel_type == whole_subtree) || is_leaf_bridge)
|
|
|
+ pci_bridge_release_resources(bus, type);
|
|
|
+}
|
|
|
+
|
|
|
static void pci_bus_dump_res(struct pci_bus *bus)
|
|
|
{
|
|
|
int i;
|