diff options
Diffstat (limited to 'drivers/pci/bus.c')
-rw-r--r-- | drivers/pci/bus.c | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 5624db8c9ad0..003170ea2e39 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c | |||
@@ -64,17 +64,57 @@ void pci_bus_remove_resources(struct pci_bus *bus) | |||
64 | } | 64 | } |
65 | } | 65 | } |
66 | 66 | ||
67 | static bool pci_bus_resource_better(struct resource *res1, bool pos1, | ||
68 | struct resource *res2, bool pos2) | ||
69 | { | ||
70 | /* If exactly one is positive decode, always prefer that one */ | ||
71 | if (pos1 != pos2) | ||
72 | return pos1 ? true : false; | ||
73 | |||
74 | /* Prefer the one that contains the highest address */ | ||
75 | if (res1->end != res2->end) | ||
76 | return (res1->end > res2->end) ? true : false; | ||
77 | |||
78 | /* Otherwise, prefer the one with highest "center of gravity" */ | ||
79 | if (res1->start != res2->start) | ||
80 | return (res1->start > res2->start) ? true : false; | ||
81 | |||
82 | /* Otherwise, choose one arbitrarily (but consistently) */ | ||
83 | return (res1 > res2) ? true : false; | ||
84 | } | ||
85 | |||
86 | static bool pci_bus_resource_positive(struct pci_bus *bus, struct resource *res) | ||
87 | { | ||
88 | struct pci_bus_resource *bus_res; | ||
89 | |||
90 | /* | ||
91 | * This relies on the fact that pci_bus.resource[] refers to P2P or | ||
92 | * CardBus bridge base/limit registers, which are always positively | ||
93 | * decoded. The pci_bus.resources list contains host bridge or | ||
94 | * subtractively decoded resources. | ||
95 | */ | ||
96 | list_for_each_entry(bus_res, &bus->resources, list) { | ||
97 | if (bus_res->res == res) | ||
98 | return (bus_res->flags & PCI_SUBTRACTIVE_DECODE) ? | ||
99 | false : true; | ||
100 | } | ||
101 | return true; | ||
102 | } | ||
103 | |||
67 | /* | 104 | /* |
68 | * Find the highest-address bus resource below the cursor "res". If the | 105 | * Find the next-best bus resource after the cursor "res". If the cursor is |
69 | * cursor is NULL, return the highest resource. | 106 | * NULL, return the best resource. "Best" means that we prefer positive |
107 | * decode regions over subtractive decode, then those at higher addresses. | ||
70 | */ | 108 | */ |
71 | static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, | 109 | static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, |
72 | unsigned int type, | 110 | unsigned int type, |
73 | struct resource *res) | 111 | struct resource *res) |
74 | { | 112 | { |
113 | bool res_pos, r_pos, prev_pos = false; | ||
75 | struct resource *r, *prev = NULL; | 114 | struct resource *r, *prev = NULL; |
76 | int i; | 115 | int i; |
77 | 116 | ||
117 | res_pos = pci_bus_resource_positive(bus, res); | ||
78 | pci_bus_for_each_resource(bus, r, i) { | 118 | pci_bus_for_each_resource(bus, r, i) { |
79 | if (!r) | 119 | if (!r) |
80 | continue; | 120 | continue; |
@@ -82,26 +122,14 @@ static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, | |||
82 | if ((r->flags & IORESOURCE_TYPE_BITS) != type) | 122 | if ((r->flags & IORESOURCE_TYPE_BITS) != type) |
83 | continue; | 123 | continue; |
84 | 124 | ||
85 | /* If this resource is at or past the cursor, skip it */ | 125 | r_pos = pci_bus_resource_positive(bus, r); |
86 | if (res) { | 126 | if (!res || pci_bus_resource_better(res, res_pos, r, r_pos)) { |
87 | if (r == res) | 127 | if (!prev || pci_bus_resource_better(r, r_pos, |
88 | continue; | 128 | prev, prev_pos)) { |
89 | if (r->end > res->end) | 129 | prev = r; |
90 | continue; | 130 | prev_pos = r_pos; |
91 | if (r->end == res->end && r->start > res->start) | 131 | } |
92 | continue; | ||
93 | } | 132 | } |
94 | |||
95 | if (!prev) | ||
96 | prev = r; | ||
97 | |||
98 | /* | ||
99 | * A small resource is higher than a large one that ends at | ||
100 | * the same address. | ||
101 | */ | ||
102 | if (r->end > prev->end || | ||
103 | (r->end == prev->end && r->start > prev->start)) | ||
104 | prev = r; | ||
105 | } | 133 | } |
106 | 134 | ||
107 | return prev; | 135 | return prev; |