|
@@ -1518,13 +1518,16 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#define PCI_RES_TYPE_MASK \
|
|
|
+ (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\
|
|
|
+ IORESOURCE_MEM_64)
|
|
|
+
|
|
|
static void pci_bridge_release_resources(struct pci_bus *bus,
|
|
|
unsigned long type)
|
|
|
{
|
|
|
struct pci_dev *dev = bus->self;
|
|
|
struct resource *r;
|
|
|
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
|
|
|
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
|
|
|
unsigned old_flags = 0;
|
|
|
struct resource *b_res;
|
|
|
int idx = 1;
|
|
@@ -1567,7 +1570,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus,
|
|
|
*/
|
|
|
release_child_resources(r);
|
|
|
if (!release_resource(r)) {
|
|
|
- type = old_flags = r->flags & type_mask;
|
|
|
+ type = old_flags = r->flags & PCI_RES_TYPE_MASK;
|
|
|
dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n",
|
|
|
PCI_BRIDGE_RESOURCES + idx, r);
|
|
|
/* keep the old size */
|
|
@@ -1758,8 +1761,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
|
|
|
enum release_type rel_type = leaf_only;
|
|
|
LIST_HEAD(fail_head);
|
|
|
struct pci_dev_resource *fail_res;
|
|
|
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
|
|
|
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
|
|
|
int pci_try_num = 1;
|
|
|
enum enable_type enable_local;
|
|
|
|
|
@@ -1818,7 +1819,7 @@ again:
|
|
|
*/
|
|
|
list_for_each_entry(fail_res, &fail_head, list)
|
|
|
pci_bus_release_bridge_resources(fail_res->dev->bus,
|
|
|
- fail_res->flags & type_mask,
|
|
|
+ fail_res->flags & PCI_RES_TYPE_MASK,
|
|
|
rel_type);
|
|
|
|
|
|
/* restore size and flags */
|
|
@@ -2031,8 +2032,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
|
|
|
LIST_HEAD(fail_head);
|
|
|
struct pci_dev_resource *fail_res;
|
|
|
int retval;
|
|
|
- unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
|
|
|
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
|
|
|
|
|
|
again:
|
|
|
__pci_bus_size_bridges(parent, &add_list);
|
|
@@ -2066,7 +2065,7 @@ again:
|
|
|
*/
|
|
|
list_for_each_entry(fail_res, &fail_head, list)
|
|
|
pci_bus_release_bridge_resources(fail_res->dev->bus,
|
|
|
- fail_res->flags & type_mask,
|
|
|
+ fail_res->flags & PCI_RES_TYPE_MASK,
|
|
|
whole_subtree);
|
|
|
|
|
|
/* restore size and flags */
|
|
@@ -2091,6 +2090,104 @@ enable_all:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
|
|
|
|
|
|
+int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
|
|
|
+{
|
|
|
+ struct pci_dev_resource *dev_res;
|
|
|
+ struct pci_dev *next;
|
|
|
+ LIST_HEAD(saved);
|
|
|
+ LIST_HEAD(added);
|
|
|
+ LIST_HEAD(failed);
|
|
|
+ unsigned int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Walk to the root hub, releasing bridge BARs when possible */
|
|
|
+ next = bridge;
|
|
|
+ do {
|
|
|
+ bridge = next;
|
|
|
+ for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END;
|
|
|
+ i++) {
|
|
|
+ struct resource *res = &bridge->resource[i];
|
|
|
+
|
|
|
+ if ((res->flags ^ type) & PCI_RES_TYPE_MASK)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Ignore BARs which are still in use */
|
|
|
+ if (res->child)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = add_to_list(&saved, bridge, res, 0, 0);
|
|
|
+ if (ret)
|
|
|
+ goto cleanup;
|
|
|
+
|
|
|
+ dev_info(&bridge->dev, "BAR %d: releasing %pR\n",
|
|
|
+ i, res);
|
|
|
+
|
|
|
+ if (res->parent)
|
|
|
+ release_resource(res);
|
|
|
+ res->start = 0;
|
|
|
+ res->end = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i == PCI_BRIDGE_RESOURCE_END)
|
|
|
+ break;
|
|
|
+
|
|
|
+ next = bridge->bus ? bridge->bus->self : NULL;
|
|
|
+ } while (next);
|
|
|
+
|
|
|
+ if (list_empty(&saved))
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ __pci_bus_size_bridges(bridge->subordinate, &added);
|
|
|
+ __pci_bridge_assign_resources(bridge, &added, &failed);
|
|
|
+ BUG_ON(!list_empty(&added));
|
|
|
+
|
|
|
+ if (!list_empty(&failed)) {
|
|
|
+ ret = -ENOSPC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ list_for_each_entry(dev_res, &saved, list) {
|
|
|
+ /* Skip the bridge we just assigned resources for. */
|
|
|
+ if (bridge == dev_res->dev)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ bridge = dev_res->dev;
|
|
|
+ pci_setup_bridge(bridge->subordinate);
|
|
|
+ }
|
|
|
+
|
|
|
+ free_list(&saved);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+cleanup:
|
|
|
+ /* restore size and flags */
|
|
|
+ list_for_each_entry(dev_res, &failed, list) {
|
|
|
+ struct resource *res = dev_res->res;
|
|
|
+
|
|
|
+ res->start = dev_res->start;
|
|
|
+ res->end = dev_res->end;
|
|
|
+ res->flags = dev_res->flags;
|
|
|
+ }
|
|
|
+ free_list(&failed);
|
|
|
+
|
|
|
+ /* Revert to the old configuration */
|
|
|
+ list_for_each_entry(dev_res, &saved, list) {
|
|
|
+ struct resource *res = dev_res->res;
|
|
|
+
|
|
|
+ bridge = dev_res->dev;
|
|
|
+ i = res - bridge->resource;
|
|
|
+
|
|
|
+ res->start = dev_res->start;
|
|
|
+ res->end = dev_res->end;
|
|
|
+ res->flags = dev_res->flags;
|
|
|
+
|
|
|
+ pci_claim_resource(bridge, i);
|
|
|
+ pci_setup_bridge(bridge->subordinate);
|
|
|
+ }
|
|
|
+ free_list(&saved);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
|
|
|
{
|
|
|
struct pci_dev *dev;
|