diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-02-05 00:44:46 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-02-05 09:09:26 -0500 |
commit | 593669c2ac0fe18baee04a3cd5539a148aa48574 (patch) | |
tree | 67dfe1062d3c565c8fa0ab93e6b7cc6d7fbd5e64 | |
parent | 812dbd9994f122629db73205a7f7f46b430a6360 (diff) |
x86/PCI/ACPI: Use common ACPI resource interfaces to simplify implementation
Use common ACPI resource discovery interfaces to simplify PCI host bridge
resource enumeration.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | arch/x86/pci/acpi.c | 295 |
1 files changed, 91 insertions, 204 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 1d725d99963d..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,132 +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, res_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 | res_end = (u64)iomem_resource.end; | ||
297 | } else if (addr.resource_type == ACPI_IO_RANGE) { | ||
298 | flags = IORESOURCE_IO; | ||
299 | res_end = (u64)ioport_resource.end; | ||
300 | } else | ||
301 | return AE_OK; | ||
302 | |||
303 | start = addr.address.minimum + addr.address.translation_offset; | ||
304 | orig_end = end = addr.address.maximum + addr.address.translation_offset; | ||
305 | |||
306 | /* Exclude non-addressable range or non-addressable portion of range */ | ||
307 | end = min(end, res_end); | ||
308 | if (end <= start) { | ||
309 | dev_info(&info->bridge->dev, | ||
310 | "host bridge window [%#llx-%#llx] " | ||
311 | "(ignored, not CPU addressable)\n", start, orig_end); | ||
312 | return AE_OK; | ||
313 | } else if (orig_end != end) { | ||
314 | dev_info(&info->bridge->dev, | ||
315 | "host bridge window [%#llx-%#llx] " | ||
316 | "([%#llx-%#llx] ignored, not CPU addressable)\n", | ||
317 | start, orig_end, end + 1, orig_end); | ||
318 | } | ||
319 | 224 | ||
320 | res = &info->res[info->res_num]; | 225 | BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); |
321 | res->name = info->name; | 226 | root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; |
322 | res->flags = flags; | ||
323 | res->start = start; | ||
324 | res->end = end; | ||
325 | info->res_offset[info->res_num] = addr.address.translation_offset; | ||
326 | info->res_num++; | ||
327 | 227 | ||
328 | if (!pci_use_crs) | 228 | list_splice_init(crs_res, &list); |
329 | dev_printk(KERN_DEBUG, &info->bridge->dev, | 229 | resource_list_for_each_entry_safe(entry, tmp, &list) { |
330 | "host bridge window %pR (ignored)\n", res); | 230 | bool free = false; |
231 | resource_size_t end; | ||
331 | 232 | ||
332 | return AE_OK; | 233 | res1 = entry->res; |
333 | } | ||
334 | |||
335 | static void coalesce_windows(struct pci_root_info *info, unsigned long type) | ||
336 | { | ||
337 | int i, j; | ||
338 | struct resource *res1, *res2; | ||
339 | |||
340 | for (i = 0; i < info->res_num; i++) { | ||
341 | res1 = &info->res[i]; | ||
342 | if (!(res1->flags & type)) | 234 | if (!(res1->flags & type)) |
343 | 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 | } | ||
344 | 250 | ||
345 | for (j = i + 1; j < info->res_num; j++) { | 251 | resource_list_for_each_entry(entry2, crs_res) { |
346 | res2 = &info->res[j]; | 252 | res2 = entry2->res; |
347 | if (!(res2->flags & type)) | 253 | if (!(res2->flags & type)) |
348 | continue; | 254 | continue; |
349 | 255 | ||
@@ -355,118 +261,92 @@ static void coalesce_windows(struct pci_root_info *info, unsigned long type) | |||
355 | if (resource_overlaps(res1, res2)) { | 261 | if (resource_overlaps(res1, res2)) { |
356 | res2->start = min(res1->start, res2->start); | 262 | res2->start = min(res1->start, res2->start); |
357 | res2->end = max(res1->end, res2->end); | 263 | res2->end = max(res1->end, res2->end); |
358 | dev_info(&info->bridge->dev, | 264 | dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", |
359 | "host bridge window expanded to %pR; %pR ignored\n", | ||
360 | res2, res1); | 265 | res2, res1); |
361 | res1->flags = 0; | 266 | free = true; |
267 | goto next; | ||
362 | } | 268 | } |
363 | } | 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); | ||
364 | } | 277 | } |
365 | } | 278 | } |
366 | 279 | ||
367 | static void add_resources(struct pci_root_info *info, | 280 | static void add_resources(struct pci_root_info *info, |
368 | struct list_head *resources) | 281 | struct list_head *resources, |
282 | struct list_head *crs_res) | ||
369 | { | 283 | { |
370 | int i; | 284 | struct resource_entry *entry, *tmp; |
371 | struct resource *res, *root, *conflict; | 285 | struct resource *res, *conflict, *root = NULL; |
372 | |||
373 | coalesce_windows(info, IORESOURCE_MEM); | ||
374 | coalesce_windows(info, IORESOURCE_IO); | ||
375 | 286 | ||
376 | for (i = 0; i < info->res_num; i++) { | 287 | validate_resources(&info->bridge->dev, crs_res, IORESOURCE_MEM); |
377 | res = &info->res[i]; | 288 | validate_resources(&info->bridge->dev, crs_res, IORESOURCE_IO); |
378 | 289 | ||
290 | resource_list_for_each_entry_safe(entry, tmp, crs_res) { | ||
291 | res = entry->res; | ||
379 | if (res->flags & IORESOURCE_MEM) | 292 | if (res->flags & IORESOURCE_MEM) |
380 | root = &iomem_resource; | 293 | root = &iomem_resource; |
381 | else if (res->flags & IORESOURCE_IO) | 294 | else if (res->flags & IORESOURCE_IO) |
382 | root = &ioport_resource; | 295 | root = &ioport_resource; |
383 | else | 296 | else |
384 | continue; | 297 | BUG_ON(res); |
385 | 298 | ||
386 | conflict = insert_resource_conflict(root, res); | 299 | conflict = insert_resource_conflict(root, res); |
387 | if (conflict) | 300 | if (conflict) { |
388 | dev_info(&info->bridge->dev, | 301 | dev_info(&info->bridge->dev, |
389 | "ignoring host bridge window %pR (conflicts with %s %pR)\n", | 302 | "ignoring host bridge window %pR (conflicts with %s %pR)\n", |
390 | res, conflict->name, conflict); | 303 | res, conflict->name, conflict); |
391 | else | 304 | resource_list_destroy_entry(entry); |
392 | pci_add_resource_offset(resources, res, | 305 | } |
393 | info->res_offset[i]); | ||
394 | } | 306 | } |
395 | } | ||
396 | 307 | ||
397 | static void free_pci_root_info_res(struct pci_root_info *info) | 308 | list_splice_tail(crs_res, resources); |
398 | { | ||
399 | kfree(info->res); | ||
400 | info->res = NULL; | ||
401 | kfree(info->res_offset); | ||
402 | info->res_offset = NULL; | ||
403 | info->res_num = 0; | ||
404 | } | 309 | } |
405 | 310 | ||
406 | static void __release_pci_root_info(struct pci_root_info *info) | 311 | static void release_pci_root_info(struct pci_host_bridge *bridge) |
407 | { | 312 | { |
408 | int i; | ||
409 | struct resource *res; | 313 | struct resource *res; |
314 | struct resource_entry *entry; | ||
315 | struct pci_root_info *info = bridge->release_data; | ||
410 | 316 | ||
411 | for (i = 0; i < info->res_num; i++) { | 317 | resource_list_for_each_entry(entry, &bridge->windows) { |
412 | res = &info->res[i]; | 318 | res = entry->res; |
413 | 319 | if (res->parent && | |
414 | if (!res->parent) | 320 | (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) |
415 | continue; | 321 | release_resource(res); |
416 | |||
417 | if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) | ||
418 | continue; | ||
419 | |||
420 | release_resource(res); | ||
421 | } | 322 | } |
422 | 323 | ||
423 | free_pci_root_info_res(info); | ||
424 | |||
425 | teardown_mcfg_map(info); | 324 | teardown_mcfg_map(info); |
426 | |||
427 | kfree(info); | 325 | kfree(info); |
428 | } | 326 | } |
429 | 327 | ||
430 | static void release_pci_root_info(struct pci_host_bridge *bridge) | ||
431 | { | ||
432 | struct pci_root_info *info = bridge->release_data; | ||
433 | |||
434 | __release_pci_root_info(info); | ||
435 | } | ||
436 | |||
437 | static void probe_pci_root_info(struct pci_root_info *info, | 328 | static void probe_pci_root_info(struct pci_root_info *info, |
438 | struct acpi_device *device, | 329 | struct acpi_device *device, |
439 | int busnum, int domain) | 330 | int busnum, int domain, |
331 | struct list_head *list) | ||
440 | { | 332 | { |
441 | size_t size; | 333 | int ret; |
334 | struct resource_entry *entry; | ||
442 | 335 | ||
443 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); | 336 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); |
444 | info->bridge = device; | 337 | info->bridge = device; |
445 | 338 | ret = acpi_dev_get_resources(device, list, | |
446 | info->res_num = 0; | 339 | acpi_dev_filter_resource_type_cb, |
447 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource, | 340 | (void *)(IORESOURCE_IO | IORESOURCE_MEM)); |
448 | info); | 341 | if (ret < 0) |
449 | if (!info->res_num) | 342 | dev_warn(&device->dev, |
450 | return; | 343 | "failed to parse _CRS method, error code %d\n", ret); |
451 | 344 | else if (ret == 0) | |
452 | size = sizeof(*info->res) * info->res_num; | 345 | dev_dbg(&device->dev, |
453 | info->res = kzalloc_node(size, GFP_KERNEL, info->sd.node); | 346 | "no IO and memory resources present in _CRS\n"); |
454 | if (!info->res) { | 347 | else |
455 | info->res_num = 0; | 348 | resource_list_for_each_entry(entry, list) |
456 | return; | 349 | entry->res->name = info->name; |
457 | } | ||
458 | |||
459 | size = sizeof(*info->res_offset) * info->res_num; | ||
460 | info->res_num = 0; | ||
461 | info->res_offset = kzalloc_node(size, GFP_KERNEL, info->sd.node); | ||
462 | if (!info->res_offset) { | ||
463 | kfree(info->res); | ||
464 | info->res = NULL; | ||
465 | return; | ||
466 | } | ||
467 | |||
468 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, | ||
469 | info); | ||
470 | } | 350 | } |
471 | 351 | ||
472 | 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) |
@@ -475,6 +355,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | |||
475 | struct pci_root_info *info; | 355 | struct pci_root_info *info; |
476 | int domain = root->segment; | 356 | int domain = root->segment; |
477 | int busnum = root->secondary.start; | 357 | int busnum = root->secondary.start; |
358 | struct resource_entry *res_entry; | ||
359 | LIST_HEAD(crs_res); | ||
478 | LIST_HEAD(resources); | 360 | LIST_HEAD(resources); |
479 | struct pci_bus *bus; | 361 | struct pci_bus *bus; |
480 | struct pci_sysdata *sd; | 362 | struct pci_sysdata *sd; |
@@ -522,18 +404,22 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | |||
522 | memcpy(bus->sysdata, sd, sizeof(*sd)); | 404 | memcpy(bus->sysdata, sd, sizeof(*sd)); |
523 | kfree(info); | 405 | kfree(info); |
524 | } else { | 406 | } else { |
525 | probe_pci_root_info(info, device, busnum, domain); | ||
526 | |||
527 | /* insert busn res at first */ | 407 | /* insert busn res at first */ |
528 | pci_add_resource(&resources, &root->secondary); | 408 | pci_add_resource(&resources, &root->secondary); |
409 | |||
529 | /* | 410 | /* |
530 | * _CRS with no apertures is normal, so only fall back to | 411 | * _CRS with no apertures is normal, so only fall back to |
531 | * defaults or native bridge info if we're ignoring _CRS. | 412 | * defaults or native bridge info if we're ignoring _CRS. |
532 | */ | 413 | */ |
533 | if (pci_use_crs) | 414 | probe_pci_root_info(info, device, busnum, domain, &crs_res); |
534 | add_resources(info, &resources); | 415 | if (pci_use_crs) { |
535 | else { | 416 | add_resources(info, &resources, &crs_res); |
536 | 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); | ||
537 | x86_pci_root_bus_resources(busnum, &resources); | 423 | x86_pci_root_bus_resources(busnum, &resources); |
538 | } | 424 | } |
539 | 425 | ||
@@ -548,8 +434,9 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | |||
548 | to_pci_host_bridge(bus->bridge), | 434 | to_pci_host_bridge(bus->bridge), |
549 | release_pci_root_info, info); | 435 | release_pci_root_info, info); |
550 | } else { | 436 | } else { |
551 | pci_free_resource_list(&resources); | 437 | resource_list_free(&resources); |
552 | __release_pci_root_info(info); | 438 | teardown_mcfg_map(info); |
439 | kfree(info); | ||
553 | } | 440 | } |
554 | } | 441 | } |
555 | 442 | ||