diff options
Diffstat (limited to 'arch/x86/pci/acpi.c')
-rw-r--r-- | arch/x86/pci/acpi.c | 293 |
1 files changed, 91 insertions, 202 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index bb98afd0591e..6ac273832f28 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c | |||
@@ -10,9 +10,6 @@ | |||
10 | struct pci_root_info { | 10 | struct pci_root_info { |
11 | struct acpi_device *bridge; | 11 | struct acpi_device *bridge; |
12 | char name[16]; | 12 | char name[16]; |
13 | unsigned int res_num; | ||
14 | struct resource *res; | ||
15 | resource_size_t *res_offset; | ||
16 | struct pci_sysdata sd; | 13 | struct pci_sysdata sd; |
17 | #ifdef CONFIG_PCI_MMCONFIG | 14 | #ifdef CONFIG_PCI_MMCONFIG |
18 | bool mcfg_added; | 15 | bool mcfg_added; |
@@ -218,130 +215,41 @@ static void teardown_mcfg_map(struct pci_root_info *info) | |||
218 | } | 215 | } |
219 | #endif | 216 | #endif |
220 | 217 | ||
221 | static acpi_status resource_to_addr(struct acpi_resource *resource, | 218 | static void validate_resources(struct device *dev, struct list_head *crs_res, |
222 | struct acpi_resource_address64 *addr) | 219 | unsigned long type) |
223 | { | ||
224 | acpi_status status; | ||
225 | struct acpi_resource_memory24 *memory24; | ||
226 | struct acpi_resource_memory32 *memory32; | ||
227 | struct acpi_resource_fixed_memory32 *fixed_memory32; | ||
228 | |||
229 | memset(addr, 0, sizeof(*addr)); | ||
230 | switch (resource->type) { | ||
231 | case ACPI_RESOURCE_TYPE_MEMORY24: | ||
232 | memory24 = &resource->data.memory24; | ||
233 | addr->resource_type = ACPI_MEMORY_RANGE; | ||
234 | addr->address.minimum = memory24->minimum; | ||
235 | addr->address.address_length = memory24->address_length; | ||
236 | addr->address.maximum = addr->address.minimum + addr->address.address_length - 1; | ||
237 | return AE_OK; | ||
238 | case ACPI_RESOURCE_TYPE_MEMORY32: | ||
239 | memory32 = &resource->data.memory32; | ||
240 | addr->resource_type = ACPI_MEMORY_RANGE; | ||
241 | addr->address.minimum = memory32->minimum; | ||
242 | addr->address.address_length = memory32->address_length; | ||
243 | addr->address.maximum = addr->address.minimum + addr->address.address_length - 1; | ||
244 | return AE_OK; | ||
245 | case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: | ||
246 | fixed_memory32 = &resource->data.fixed_memory32; | ||
247 | addr->resource_type = ACPI_MEMORY_RANGE; | ||
248 | addr->address.minimum = fixed_memory32->address; | ||
249 | addr->address.address_length = fixed_memory32->address_length; | ||
250 | addr->address.maximum = addr->address.minimum + addr->address.address_length - 1; | ||
251 | return AE_OK; | ||
252 | case ACPI_RESOURCE_TYPE_ADDRESS16: | ||
253 | case ACPI_RESOURCE_TYPE_ADDRESS32: | ||
254 | case ACPI_RESOURCE_TYPE_ADDRESS64: | ||
255 | status = acpi_resource_to_address64(resource, addr); | ||
256 | if (ACPI_SUCCESS(status) && | ||
257 | (addr->resource_type == ACPI_MEMORY_RANGE || | ||
258 | addr->resource_type == ACPI_IO_RANGE) && | ||
259 | addr->address.address_length > 0) { | ||
260 | return AE_OK; | ||
261 | } | ||
262 | break; | ||
263 | } | ||
264 | return AE_ERROR; | ||
265 | } | ||
266 | |||
267 | static acpi_status count_resource(struct acpi_resource *acpi_res, void *data) | ||
268 | { | 220 | { |
269 | struct pci_root_info *info = data; | 221 | LIST_HEAD(list); |
270 | struct acpi_resource_address64 addr; | 222 | struct resource *res1, *res2, *root = NULL; |
271 | acpi_status status; | 223 | struct resource_entry *tmp, *entry, *entry2; |
272 | |||
273 | status = resource_to_addr(acpi_res, &addr); | ||
274 | if (ACPI_SUCCESS(status)) | ||
275 | info->res_num++; | ||
276 | return AE_OK; | ||
277 | } | ||
278 | |||
279 | static acpi_status setup_resource(struct acpi_resource *acpi_res, void *data) | ||
280 | { | ||
281 | struct pci_root_info *info = data; | ||
282 | struct resource *res; | ||
283 | struct acpi_resource_address64 addr; | ||
284 | acpi_status status; | ||
285 | unsigned long flags; | ||
286 | u64 start, orig_end, end; | ||
287 | |||
288 | status = resource_to_addr(acpi_res, &addr); | ||
289 | if (!ACPI_SUCCESS(status)) | ||
290 | return AE_OK; | ||
291 | |||
292 | if (addr.resource_type == ACPI_MEMORY_RANGE) { | ||
293 | flags = IORESOURCE_MEM; | ||
294 | if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) | ||
295 | flags |= IORESOURCE_PREFETCH; | ||
296 | } else if (addr.resource_type == ACPI_IO_RANGE) { | ||
297 | flags = IORESOURCE_IO; | ||
298 | } else | ||
299 | return AE_OK; | ||
300 | |||
301 | start = addr.address.minimum + addr.address.translation_offset; | ||
302 | orig_end = end = addr.address.maximum + addr.address.translation_offset; | ||
303 | |||
304 | /* Exclude non-addressable range or non-addressable portion of range */ | ||
305 | end = min(end, (u64)iomem_resource.end); | ||
306 | if (end <= start) { | ||
307 | dev_info(&info->bridge->dev, | ||
308 | "host bridge window [%#llx-%#llx] " | ||
309 | "(ignored, not CPU addressable)\n", start, orig_end); | ||
310 | return AE_OK; | ||
311 | } else if (orig_end != end) { | ||
312 | dev_info(&info->bridge->dev, | ||
313 | "host bridge window [%#llx-%#llx] " | ||
314 | "([%#llx-%#llx] ignored, not CPU addressable)\n", | ||
315 | start, orig_end, end + 1, orig_end); | ||
316 | } | ||
317 | 224 | ||
318 | res = &info->res[info->res_num]; | 225 | BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); |
319 | res->name = info->name; | 226 | root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; |
320 | res->flags = flags; | ||
321 | res->start = start; | ||
322 | res->end = end; | ||
323 | info->res_offset[info->res_num] = addr.address.translation_offset; | ||
324 | info->res_num++; | ||
325 | 227 | ||
326 | if (!pci_use_crs) | 228 | list_splice_init(crs_res, &list); |
327 | dev_printk(KERN_DEBUG, &info->bridge->dev, | 229 | resource_list_for_each_entry_safe(entry, tmp, &list) { |
328 | "host bridge window %pR (ignored)\n", res); | 230 | bool free = false; |
231 | resource_size_t end; | ||
329 | 232 | ||
330 | return AE_OK; | 233 | res1 = entry->res; |
331 | } | ||
332 | |||
333 | static void coalesce_windows(struct pci_root_info *info, unsigned long type) | ||
334 | { | ||
335 | int i, j; | ||
336 | struct resource *res1, *res2; | ||
337 | |||
338 | for (i = 0; i < info->res_num; i++) { | ||
339 | res1 = &info->res[i]; | ||
340 | if (!(res1->flags & type)) | 234 | if (!(res1->flags & type)) |
341 | continue; | 235 | goto next; |
236 | |||
237 | /* Exclude non-addressable range or non-addressable portion */ | ||
238 | end = min(res1->end, root->end); | ||
239 | if (end <= res1->start) { | ||
240 | dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", | ||
241 | res1); | ||
242 | free = true; | ||
243 | goto next; | ||
244 | } else if (res1->end != end) { | ||
245 | dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", | ||
246 | res1, (unsigned long long)end + 1, | ||
247 | (unsigned long long)res1->end); | ||
248 | res1->end = end; | ||
249 | } | ||
342 | 250 | ||
343 | for (j = i + 1; j < info->res_num; j++) { | 251 | resource_list_for_each_entry(entry2, crs_res) { |
344 | res2 = &info->res[j]; | 252 | res2 = entry2->res; |
345 | if (!(res2->flags & type)) | 253 | if (!(res2->flags & type)) |
346 | continue; | 254 | continue; |
347 | 255 | ||
@@ -353,118 +261,92 @@ static void coalesce_windows(struct pci_root_info *info, unsigned long type) | |||
353 | if (resource_overlaps(res1, res2)) { | 261 | if (resource_overlaps(res1, res2)) { |
354 | res2->start = min(res1->start, res2->start); | 262 | res2->start = min(res1->start, res2->start); |
355 | res2->end = max(res1->end, res2->end); | 263 | res2->end = max(res1->end, res2->end); |
356 | dev_info(&info->bridge->dev, | 264 | dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", |
357 | "host bridge window expanded to %pR; %pR ignored\n", | ||
358 | res2, res1); | 265 | res2, res1); |
359 | res1->flags = 0; | 266 | free = true; |
267 | goto next; | ||
360 | } | 268 | } |
361 | } | 269 | } |
270 | |||
271 | next: | ||
272 | resource_list_del(entry); | ||
273 | if (free) | ||
274 | resource_list_free_entry(entry); | ||
275 | else | ||
276 | resource_list_add_tail(entry, crs_res); | ||
362 | } | 277 | } |
363 | } | 278 | } |
364 | 279 | ||
365 | static void add_resources(struct pci_root_info *info, | 280 | static void add_resources(struct pci_root_info *info, |
366 | struct list_head *resources) | 281 | struct list_head *resources, |
282 | struct list_head *crs_res) | ||
367 | { | 283 | { |
368 | int i; | 284 | struct resource_entry *entry, *tmp; |
369 | struct resource *res, *root, *conflict; | 285 | struct resource *res, *conflict, *root = NULL; |
370 | |||
371 | coalesce_windows(info, IORESOURCE_MEM); | ||
372 | coalesce_windows(info, IORESOURCE_IO); | ||
373 | 286 | ||
374 | for (i = 0; i < info->res_num; i++) { | 287 | validate_resources(&info->bridge->dev, crs_res, IORESOURCE_MEM); |
375 | res = &info->res[i]; | 288 | validate_resources(&info->bridge->dev, crs_res, IORESOURCE_IO); |
376 | 289 | ||
290 | resource_list_for_each_entry_safe(entry, tmp, crs_res) { | ||
291 | res = entry->res; | ||
377 | if (res->flags & IORESOURCE_MEM) | 292 | if (res->flags & IORESOURCE_MEM) |
378 | root = &iomem_resource; | 293 | root = &iomem_resource; |
379 | else if (res->flags & IORESOURCE_IO) | 294 | else if (res->flags & IORESOURCE_IO) |
380 | root = &ioport_resource; | 295 | root = &ioport_resource; |
381 | else | 296 | else |
382 | continue; | 297 | BUG_ON(res); |
383 | 298 | ||
384 | conflict = insert_resource_conflict(root, res); | 299 | conflict = insert_resource_conflict(root, res); |
385 | if (conflict) | 300 | if (conflict) { |
386 | dev_info(&info->bridge->dev, | 301 | dev_info(&info->bridge->dev, |
387 | "ignoring host bridge window %pR (conflicts with %s %pR)\n", | 302 | "ignoring host bridge window %pR (conflicts with %s %pR)\n", |
388 | res, conflict->name, conflict); | 303 | res, conflict->name, conflict); |
389 | else | 304 | resource_list_destroy_entry(entry); |
390 | pci_add_resource_offset(resources, res, | 305 | } |
391 | info->res_offset[i]); | ||
392 | } | 306 | } |
393 | } | ||
394 | 307 | ||
395 | static void free_pci_root_info_res(struct pci_root_info *info) | 308 | list_splice_tail(crs_res, resources); |
396 | { | ||
397 | kfree(info->res); | ||
398 | info->res = NULL; | ||
399 | kfree(info->res_offset); | ||
400 | info->res_offset = NULL; | ||
401 | info->res_num = 0; | ||
402 | } | 309 | } |
403 | 310 | ||
404 | static void __release_pci_root_info(struct pci_root_info *info) | 311 | static void release_pci_root_info(struct pci_host_bridge *bridge) |
405 | { | 312 | { |
406 | int i; | ||
407 | struct resource *res; | 313 | struct resource *res; |
314 | struct resource_entry *entry; | ||
315 | struct pci_root_info *info = bridge->release_data; | ||
408 | 316 | ||
409 | for (i = 0; i < info->res_num; i++) { | 317 | resource_list_for_each_entry(entry, &bridge->windows) { |
410 | res = &info->res[i]; | 318 | res = entry->res; |
411 | 319 | if (res->parent && | |
412 | if (!res->parent) | 320 | (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) |
413 | continue; | 321 | release_resource(res); |
414 | |||
415 | if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) | ||
416 | continue; | ||
417 | |||
418 | release_resource(res); | ||
419 | } | 322 | } |
420 | 323 | ||
421 | free_pci_root_info_res(info); | ||
422 | |||
423 | teardown_mcfg_map(info); | 324 | teardown_mcfg_map(info); |
424 | |||
425 | kfree(info); | 325 | kfree(info); |
426 | } | 326 | } |
427 | 327 | ||
428 | static void release_pci_root_info(struct pci_host_bridge *bridge) | ||
429 | { | ||
430 | struct pci_root_info *info = bridge->release_data; | ||
431 | |||
432 | __release_pci_root_info(info); | ||
433 | } | ||
434 | |||
435 | static void probe_pci_root_info(struct pci_root_info *info, | 328 | static void probe_pci_root_info(struct pci_root_info *info, |
436 | struct acpi_device *device, | 329 | struct acpi_device *device, |
437 | int busnum, int domain) | 330 | int busnum, int domain, |
331 | struct list_head *list) | ||
438 | { | 332 | { |
439 | size_t size; | 333 | int ret; |
334 | struct resource_entry *entry; | ||
440 | 335 | ||
441 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); | 336 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); |
442 | info->bridge = device; | 337 | info->bridge = device; |
443 | 338 | ret = acpi_dev_get_resources(device, list, | |
444 | info->res_num = 0; | 339 | acpi_dev_filter_resource_type_cb, |
445 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource, | 340 | (void *)(IORESOURCE_IO | IORESOURCE_MEM)); |
446 | info); | 341 | if (ret < 0) |
447 | if (!info->res_num) | 342 | dev_warn(&device->dev, |
448 | return; | 343 | "failed to parse _CRS method, error code %d\n", ret); |
449 | 344 | else if (ret == 0) | |
450 | size = sizeof(*info->res) * info->res_num; | 345 | dev_dbg(&device->dev, |
451 | info->res = kzalloc_node(size, GFP_KERNEL, info->sd.node); | 346 | "no IO and memory resources present in _CRS\n"); |
452 | if (!info->res) { | 347 | else |
453 | info->res_num = 0; | 348 | resource_list_for_each_entry(entry, list) |
454 | return; | 349 | entry->res->name = info->name; |
455 | } | ||
456 | |||
457 | size = sizeof(*info->res_offset) * info->res_num; | ||
458 | info->res_num = 0; | ||
459 | info->res_offset = kzalloc_node(size, GFP_KERNEL, info->sd.node); | ||
460 | if (!info->res_offset) { | ||
461 | kfree(info->res); | ||
462 | info->res = NULL; | ||
463 | return; | ||
464 | } | ||
465 | |||
466 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, | ||
467 | info); | ||
468 | } | 350 | } |
469 | 351 | ||
470 | struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | 352 | struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) |
@@ -473,6 +355,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | |||
473 | struct pci_root_info *info; | 355 | struct pci_root_info *info; |
474 | int domain = root->segment; | 356 | int domain = root->segment; |
475 | int busnum = root->secondary.start; | 357 | int busnum = root->secondary.start; |
358 | struct resource_entry *res_entry; | ||
359 | LIST_HEAD(crs_res); | ||
476 | LIST_HEAD(resources); | 360 | LIST_HEAD(resources); |
477 | struct pci_bus *bus; | 361 | struct pci_bus *bus; |
478 | struct pci_sysdata *sd; | 362 | struct pci_sysdata *sd; |
@@ -520,18 +404,22 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | |||
520 | memcpy(bus->sysdata, sd, sizeof(*sd)); | 404 | memcpy(bus->sysdata, sd, sizeof(*sd)); |
521 | kfree(info); | 405 | kfree(info); |
522 | } else { | 406 | } else { |
523 | probe_pci_root_info(info, device, busnum, domain); | ||
524 | |||
525 | /* insert busn res at first */ | 407 | /* insert busn res at first */ |
526 | pci_add_resource(&resources, &root->secondary); | 408 | pci_add_resource(&resources, &root->secondary); |
409 | |||
527 | /* | 410 | /* |
528 | * _CRS with no apertures is normal, so only fall back to | 411 | * _CRS with no apertures is normal, so only fall back to |
529 | * defaults or native bridge info if we're ignoring _CRS. | 412 | * defaults or native bridge info if we're ignoring _CRS. |
530 | */ | 413 | */ |
531 | if (pci_use_crs) | 414 | probe_pci_root_info(info, device, busnum, domain, &crs_res); |
532 | add_resources(info, &resources); | 415 | if (pci_use_crs) { |
533 | else { | 416 | add_resources(info, &resources, &crs_res); |
534 | free_pci_root_info_res(info); | 417 | } else { |
418 | resource_list_for_each_entry(res_entry, &crs_res) | ||
419 | dev_printk(KERN_DEBUG, &device->dev, | ||
420 | "host bridge window %pR (ignored)\n", | ||
421 | res_entry->res); | ||
422 | resource_list_free(&crs_res); | ||
535 | x86_pci_root_bus_resources(busnum, &resources); | 423 | x86_pci_root_bus_resources(busnum, &resources); |
536 | } | 424 | } |
537 | 425 | ||
@@ -546,8 +434,9 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | |||
546 | to_pci_host_bridge(bus->bridge), | 434 | to_pci_host_bridge(bus->bridge), |
547 | release_pci_root_info, info); | 435 | release_pci_root_info, info); |
548 | } else { | 436 | } else { |
549 | pci_free_resource_list(&resources); | 437 | resource_list_free(&resources); |
550 | __release_pci_root_info(info); | 438 | teardown_mcfg_map(info); |
439 | kfree(info); | ||
551 | } | 440 | } |
552 | } | 441 | } |
553 | 442 | ||