diff options
| -rw-r--r-- | arch/x86/pci/acpi.c | 103 |
1 files changed, 83 insertions, 20 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 15466c096ba5..0972315c3860 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c | |||
| @@ -138,7 +138,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
| 138 | struct acpi_resource_address64 addr; | 138 | struct acpi_resource_address64 addr; |
| 139 | acpi_status status; | 139 | acpi_status status; |
| 140 | unsigned long flags; | 140 | unsigned long flags; |
| 141 | struct resource *root, *conflict; | ||
| 142 | u64 start, end; | 141 | u64 start, end; |
| 143 | 142 | ||
| 144 | status = resource_to_addr(acpi_res, &addr); | 143 | status = resource_to_addr(acpi_res, &addr); |
| @@ -146,12 +145,10 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
| 146 | return AE_OK; | 145 | return AE_OK; |
| 147 | 146 | ||
| 148 | if (addr.resource_type == ACPI_MEMORY_RANGE) { | 147 | if (addr.resource_type == ACPI_MEMORY_RANGE) { |
| 149 | root = &iomem_resource; | ||
| 150 | flags = IORESOURCE_MEM; | 148 | flags = IORESOURCE_MEM; |
| 151 | if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) | 149 | if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) |
| 152 | flags |= IORESOURCE_PREFETCH; | 150 | flags |= IORESOURCE_PREFETCH; |
| 153 | } else if (addr.resource_type == ACPI_IO_RANGE) { | 151 | } else if (addr.resource_type == ACPI_IO_RANGE) { |
| 154 | root = &ioport_resource; | ||
| 155 | flags = IORESOURCE_IO; | 152 | flags = IORESOURCE_IO; |
| 156 | } else | 153 | } else |
| 157 | return AE_OK; | 154 | return AE_OK; |
| @@ -172,25 +169,90 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
| 172 | return AE_OK; | 169 | return AE_OK; |
| 173 | } | 170 | } |
| 174 | 171 | ||
| 175 | conflict = insert_resource_conflict(root, res); | 172 | info->res_num++; |
| 176 | if (conflict) { | 173 | if (addr.translation_offset) |
| 177 | dev_err(&info->bridge->dev, | 174 | dev_info(&info->bridge->dev, "host bridge window %pR " |
| 178 | "address space collision: host bridge window %pR " | 175 | "(PCI address [%#llx-%#llx])\n", |
| 179 | "conflicts with %s %pR\n", | 176 | res, res->start - addr.translation_offset, |
| 180 | res, conflict->name, conflict); | 177 | res->end - addr.translation_offset); |
| 181 | } else { | 178 | else |
| 182 | pci_bus_add_resource(info->bus, res, 0); | 179 | dev_info(&info->bridge->dev, "host bridge window %pR\n", res); |
| 183 | info->res_num++; | 180 | |
| 184 | if (addr.translation_offset) | 181 | return AE_OK; |
| 185 | dev_info(&info->bridge->dev, "host bridge window %pR " | 182 | } |
| 186 | "(PCI address [%#llx-%#llx])\n", | 183 | |
| 187 | res, res->start - addr.translation_offset, | 184 | static bool resource_contains(struct resource *res, resource_size_t point) |
| 188 | res->end - addr.translation_offset); | 185 | { |
| 186 | if (res->start <= point && point <= res->end) | ||
| 187 | return true; | ||
| 188 | return false; | ||
| 189 | } | ||
| 190 | |||
| 191 | static void coalesce_windows(struct pci_root_info *info, int type) | ||
| 192 | { | ||
| 193 | int i, j; | ||
| 194 | struct resource *res1, *res2; | ||
| 195 | |||
| 196 | for (i = 0; i < info->res_num; i++) { | ||
| 197 | res1 = &info->res[i]; | ||
| 198 | if (!(res1->flags & type)) | ||
| 199 | continue; | ||
| 200 | |||
| 201 | for (j = i + 1; j < info->res_num; j++) { | ||
| 202 | res2 = &info->res[j]; | ||
| 203 | if (!(res2->flags & type)) | ||
| 204 | continue; | ||
| 205 | |||
| 206 | /* | ||
| 207 | * I don't like throwing away windows because then | ||
| 208 | * our resources no longer match the ACPI _CRS, but | ||
| 209 | * the kernel resource tree doesn't allow overlaps. | ||
| 210 | */ | ||
| 211 | if (resource_contains(res1, res2->start) || | ||
| 212 | resource_contains(res1, res2->end) || | ||
| 213 | resource_contains(res2, res1->start) || | ||
| 214 | resource_contains(res2, res1->end)) { | ||
| 215 | res1->start = min(res1->start, res2->start); | ||
| 216 | res1->end = max(res1->end, res2->end); | ||
| 217 | dev_info(&info->bridge->dev, | ||
| 218 | "host bridge window expanded to %pR; %pR ignored\n", | ||
| 219 | res1, res2); | ||
| 220 | res2->flags = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | static void add_resources(struct pci_root_info *info) | ||
| 227 | { | ||
| 228 | int i; | ||
| 229 | struct resource *res, *root, *conflict; | ||
| 230 | |||
| 231 | if (!pci_use_crs) | ||
| 232 | return; | ||
| 233 | |||
| 234 | coalesce_windows(info, IORESOURCE_MEM); | ||
| 235 | coalesce_windows(info, IORESOURCE_IO); | ||
| 236 | |||
| 237 | for (i = 0; i < info->res_num; i++) { | ||
| 238 | res = &info->res[i]; | ||
| 239 | |||
| 240 | if (res->flags & IORESOURCE_MEM) | ||
| 241 | root = &iomem_resource; | ||
| 242 | else if (res->flags & IORESOURCE_IO) | ||
| 243 | root = &ioport_resource; | ||
| 189 | else | 244 | else |
| 190 | dev_info(&info->bridge->dev, | 245 | continue; |
| 191 | "host bridge window %pR\n", res); | 246 | |
| 247 | conflict = insert_resource_conflict(root, res); | ||
| 248 | if (conflict) | ||
| 249 | dev_err(&info->bridge->dev, | ||
| 250 | "address space collision: host bridge window %pR " | ||
| 251 | "conflicts with %s %pR\n", | ||
| 252 | res, conflict->name, conflict); | ||
| 253 | else | ||
| 254 | pci_bus_add_resource(info->bus, res, 0); | ||
| 192 | } | 255 | } |
| 193 | return AE_OK; | ||
| 194 | } | 256 | } |
| 195 | 257 | ||
| 196 | static void | 258 | static void |
| @@ -224,6 +286,7 @@ get_current_resources(struct acpi_device *device, int busnum, | |||
| 224 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, | 286 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, |
| 225 | &info); | 287 | &info); |
| 226 | 288 | ||
| 289 | add_resources(&info); | ||
| 227 | return; | 290 | return; |
| 228 | 291 | ||
| 229 | name_alloc_fail: | 292 | name_alloc_fail: |
