diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/pci_irq.c | 85 | ||||
-rw-r--r-- | drivers/acpi/pci_link.c | 103 |
2 files changed, 146 insertions, 42 deletions
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 8093f2e00321..c536ccfc5413 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c | |||
@@ -269,7 +269,51 @@ acpi_pci_irq_del_prt (int segment, int bus) | |||
269 | /* -------------------------------------------------------------------------- | 269 | /* -------------------------------------------------------------------------- |
270 | PCI Interrupt Routing Support | 270 | PCI Interrupt Routing Support |
271 | -------------------------------------------------------------------------- */ | 271 | -------------------------------------------------------------------------- */ |
272 | typedef int (*irq_lookup_func)(struct acpi_prt_entry *, int *, int *, char **); | ||
272 | 273 | ||
274 | static int | ||
275 | acpi_pci_allocate_irq(struct acpi_prt_entry *entry, | ||
276 | int *edge_level, | ||
277 | int *active_high_low, | ||
278 | char **link) | ||
279 | { | ||
280 | int irq; | ||
281 | |||
282 | ACPI_FUNCTION_TRACE("acpi_pci_allocate_irq"); | ||
283 | |||
284 | if (entry->link.handle) { | ||
285 | irq = acpi_pci_link_allocate_irq(entry->link.handle, | ||
286 | entry->link.index, edge_level, active_high_low, link); | ||
287 | if (irq < 0) { | ||
288 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); | ||
289 | return_VALUE(-1); | ||
290 | } | ||
291 | } else { | ||
292 | irq = entry->link.index; | ||
293 | *edge_level = ACPI_LEVEL_SENSITIVE; | ||
294 | *active_high_low = ACPI_ACTIVE_LOW; | ||
295 | } | ||
296 | |||
297 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); | ||
298 | return_VALUE(irq); | ||
299 | } | ||
300 | |||
301 | static int | ||
302 | acpi_pci_free_irq(struct acpi_prt_entry *entry, | ||
303 | int *edge_level, | ||
304 | int *active_high_low, | ||
305 | char **link) | ||
306 | { | ||
307 | int irq; | ||
308 | |||
309 | ACPI_FUNCTION_TRACE("acpi_pci_free_irq"); | ||
310 | if (entry->link.handle) { | ||
311 | irq = acpi_pci_link_free_irq(entry->link.handle); | ||
312 | } else { | ||
313 | irq = entry->link.index; | ||
314 | } | ||
315 | return_VALUE(irq); | ||
316 | } | ||
273 | /* | 317 | /* |
274 | * acpi_pci_irq_lookup | 318 | * acpi_pci_irq_lookup |
275 | * success: return IRQ >= 0 | 319 | * success: return IRQ >= 0 |
@@ -282,12 +326,13 @@ acpi_pci_irq_lookup ( | |||
282 | int pin, | 326 | int pin, |
283 | int *edge_level, | 327 | int *edge_level, |
284 | int *active_high_low, | 328 | int *active_high_low, |
285 | char **link) | 329 | char **link, |
330 | irq_lookup_func func) | ||
286 | { | 331 | { |
287 | struct acpi_prt_entry *entry = NULL; | 332 | struct acpi_prt_entry *entry = NULL; |
288 | int segment = pci_domain_nr(bus); | 333 | int segment = pci_domain_nr(bus); |
289 | int bus_nr = bus->number; | 334 | int bus_nr = bus->number; |
290 | int irq; | 335 | int ret; |
291 | 336 | ||
292 | ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); | 337 | ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); |
293 | 338 | ||
@@ -301,22 +346,8 @@ acpi_pci_irq_lookup ( | |||
301 | return_VALUE(-1); | 346 | return_VALUE(-1); |
302 | } | 347 | } |
303 | 348 | ||
304 | if (entry->link.handle) { | 349 | ret = func(entry, edge_level, active_high_low, link); |
305 | irq = acpi_pci_link_get_irq(entry->link.handle, | 350 | return_VALUE(ret); |
306 | entry->link.index, edge_level, active_high_low, link); | ||
307 | if (irq < 0) { | ||
308 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); | ||
309 | return_VALUE(-1); | ||
310 | } | ||
311 | } else { | ||
312 | irq = entry->link.index; | ||
313 | *edge_level = ACPI_LEVEL_SENSITIVE; | ||
314 | *active_high_low = ACPI_ACTIVE_LOW; | ||
315 | } | ||
316 | |||
317 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); | ||
318 | |||
319 | return_VALUE(irq); | ||
320 | } | 351 | } |
321 | 352 | ||
322 | /* | 353 | /* |
@@ -330,7 +361,8 @@ acpi_pci_irq_derive ( | |||
330 | int pin, | 361 | int pin, |
331 | int *edge_level, | 362 | int *edge_level, |
332 | int *active_high_low, | 363 | int *active_high_low, |
333 | char **link) | 364 | char **link, |
365 | irq_lookup_func func) | ||
334 | { | 366 | { |
335 | struct pci_dev *bridge = dev; | 367 | struct pci_dev *bridge = dev; |
336 | int irq = -1; | 368 | int irq = -1; |
@@ -363,7 +395,7 @@ acpi_pci_irq_derive ( | |||
363 | } | 395 | } |
364 | 396 | ||
365 | irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), | 397 | irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), |
366 | pin, edge_level, active_high_low, link); | 398 | pin, edge_level, active_high_low, link, func); |
367 | } | 399 | } |
368 | 400 | ||
369 | if (irq < 0) { | 401 | if (irq < 0) { |
@@ -415,7 +447,7 @@ acpi_pci_irq_enable ( | |||
415 | * values override any BIOS-assigned IRQs set during boot. | 447 | * values override any BIOS-assigned IRQs set during boot. |
416 | */ | 448 | */ |
417 | irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, | 449 | irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, |
418 | &edge_level, &active_high_low, &link); | 450 | &edge_level, &active_high_low, &link, acpi_pci_allocate_irq); |
419 | 451 | ||
420 | /* | 452 | /* |
421 | * If no PRT entry was found, we'll try to derive an IRQ from the | 453 | * If no PRT entry was found, we'll try to derive an IRQ from the |
@@ -423,7 +455,7 @@ acpi_pci_irq_enable ( | |||
423 | */ | 455 | */ |
424 | if (irq < 0) | 456 | if (irq < 0) |
425 | irq = acpi_pci_irq_derive(dev, pin, &edge_level, | 457 | irq = acpi_pci_irq_derive(dev, pin, &edge_level, |
426 | &active_high_low, &link); | 458 | &active_high_low, &link, acpi_pci_allocate_irq); |
427 | 459 | ||
428 | /* | 460 | /* |
429 | * No IRQ known to the ACPI subsystem - maybe the BIOS / | 461 | * No IRQ known to the ACPI subsystem - maybe the BIOS / |
@@ -461,7 +493,9 @@ acpi_pci_irq_enable ( | |||
461 | EXPORT_SYMBOL(acpi_pci_irq_enable); | 493 | EXPORT_SYMBOL(acpi_pci_irq_enable); |
462 | 494 | ||
463 | 495 | ||
464 | #ifdef CONFIG_ACPI_DEALLOCATE_IRQ | 496 | /* FIXME: implement x86/x86_64 version */ |
497 | void __attribute__((weak)) acpi_unregister_gsi(u32 i) {} | ||
498 | |||
465 | void | 499 | void |
466 | acpi_pci_irq_disable ( | 500 | acpi_pci_irq_disable ( |
467 | struct pci_dev *dev) | 501 | struct pci_dev *dev) |
@@ -488,14 +522,14 @@ acpi_pci_irq_disable ( | |||
488 | * First we check the PCI IRQ routing table (PRT) for an IRQ. | 522 | * First we check the PCI IRQ routing table (PRT) for an IRQ. |
489 | */ | 523 | */ |
490 | gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, | 524 | gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, |
491 | &edge_level, &active_high_low, NULL); | 525 | &edge_level, &active_high_low, NULL, acpi_pci_free_irq); |
492 | /* | 526 | /* |
493 | * If no PRT entry was found, we'll try to derive an IRQ from the | 527 | * If no PRT entry was found, we'll try to derive an IRQ from the |
494 | * device's parent bridge. | 528 | * device's parent bridge. |
495 | */ | 529 | */ |
496 | if (gsi < 0) | 530 | if (gsi < 0) |
497 | gsi = acpi_pci_irq_derive(dev, pin, | 531 | gsi = acpi_pci_irq_derive(dev, pin, |
498 | &edge_level, &active_high_low, NULL); | 532 | &edge_level, &active_high_low, NULL, acpi_pci_free_irq); |
499 | if (gsi < 0) | 533 | if (gsi < 0) |
500 | return_VOID; | 534 | return_VOID; |
501 | 535 | ||
@@ -511,4 +545,3 @@ acpi_pci_irq_disable ( | |||
511 | 545 | ||
512 | return_VOID; | 546 | return_VOID; |
513 | } | 547 | } |
514 | #endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ | ||
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 6ad0e77df9b3..6a29610edc11 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c | |||
@@ -68,6 +68,10 @@ static struct acpi_driver acpi_pci_link_driver = { | |||
68 | }, | 68 | }, |
69 | }; | 69 | }; |
70 | 70 | ||
71 | /* | ||
72 | * If a link is initialized, we never change its active and initialized | ||
73 | * later even the link is disable. Instead, we just repick the active irq | ||
74 | */ | ||
71 | struct acpi_pci_link_irq { | 75 | struct acpi_pci_link_irq { |
72 | u8 active; /* Current IRQ */ | 76 | u8 active; /* Current IRQ */ |
73 | u8 edge_level; /* All IRQs */ | 77 | u8 edge_level; /* All IRQs */ |
@@ -76,8 +80,7 @@ struct acpi_pci_link_irq { | |||
76 | u8 possible_count; | 80 | u8 possible_count; |
77 | u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; | 81 | u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; |
78 | u8 initialized:1; | 82 | u8 initialized:1; |
79 | u8 suspend_resume:1; | 83 | u8 reserved:7; |
80 | u8 reserved:6; | ||
81 | }; | 84 | }; |
82 | 85 | ||
83 | struct acpi_pci_link { | 86 | struct acpi_pci_link { |
@@ -85,12 +88,14 @@ struct acpi_pci_link { | |||
85 | struct acpi_device *device; | 88 | struct acpi_device *device; |
86 | acpi_handle handle; | 89 | acpi_handle handle; |
87 | struct acpi_pci_link_irq irq; | 90 | struct acpi_pci_link_irq irq; |
91 | int refcnt; | ||
88 | }; | 92 | }; |
89 | 93 | ||
90 | static struct { | 94 | static struct { |
91 | int count; | 95 | int count; |
92 | struct list_head entries; | 96 | struct list_head entries; |
93 | } acpi_link; | 97 | } acpi_link; |
98 | DECLARE_MUTEX(acpi_link_lock); | ||
94 | 99 | ||
95 | 100 | ||
96 | /* -------------------------------------------------------------------------- | 101 | /* -------------------------------------------------------------------------- |
@@ -532,12 +537,12 @@ static int acpi_pci_link_allocate( | |||
532 | 537 | ||
533 | ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); | 538 | ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); |
534 | 539 | ||
535 | if (link->irq.suspend_resume) { | 540 | if (link->irq.initialized) { |
536 | acpi_pci_link_set(link, link->irq.active); | 541 | if (link->refcnt == 0) |
537 | link->irq.suspend_resume = 0; | 542 | /* This means the link is disabled but initialized */ |
538 | } | 543 | acpi_pci_link_set(link, link->irq.active); |
539 | if (link->irq.initialized) | ||
540 | return_VALUE(0); | 544 | return_VALUE(0); |
545 | } | ||
541 | 546 | ||
542 | /* | 547 | /* |
543 | * search for active IRQ in list of possible IRQs. | 548 | * search for active IRQ in list of possible IRQs. |
@@ -596,13 +601,13 @@ static int acpi_pci_link_allocate( | |||
596 | } | 601 | } |
597 | 602 | ||
598 | /* | 603 | /* |
599 | * acpi_pci_link_get_irq | 604 | * acpi_pci_link_allocate_irq |
600 | * success: return IRQ >= 0 | 605 | * success: return IRQ >= 0 |
601 | * failure: return -1 | 606 | * failure: return -1 |
602 | */ | 607 | */ |
603 | 608 | ||
604 | int | 609 | int |
605 | acpi_pci_link_get_irq ( | 610 | acpi_pci_link_allocate_irq ( |
606 | acpi_handle handle, | 611 | acpi_handle handle, |
607 | int index, | 612 | int index, |
608 | int *edge_level, | 613 | int *edge_level, |
@@ -613,7 +618,7 @@ acpi_pci_link_get_irq ( | |||
613 | struct acpi_device *device = NULL; | 618 | struct acpi_device *device = NULL; |
614 | struct acpi_pci_link *link = NULL; | 619 | struct acpi_pci_link *link = NULL; |
615 | 620 | ||
616 | ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq"); | 621 | ACPI_FUNCTION_TRACE("acpi_pci_link_allocate_irq"); |
617 | 622 | ||
618 | result = acpi_bus_get_device(handle, &device); | 623 | result = acpi_bus_get_device(handle, &device); |
619 | if (result) { | 624 | if (result) { |
@@ -633,21 +638,70 @@ acpi_pci_link_get_irq ( | |||
633 | return_VALUE(-1); | 638 | return_VALUE(-1); |
634 | } | 639 | } |
635 | 640 | ||
636 | if (acpi_pci_link_allocate(link)) | 641 | down(&acpi_link_lock); |
642 | if (acpi_pci_link_allocate(link)) { | ||
643 | up(&acpi_link_lock); | ||
637 | return_VALUE(-1); | 644 | return_VALUE(-1); |
645 | } | ||
638 | 646 | ||
639 | if (!link->irq.active) { | 647 | if (!link->irq.active) { |
648 | up(&acpi_link_lock); | ||
640 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); | 649 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); |
641 | return_VALUE(-1); | 650 | return_VALUE(-1); |
642 | } | 651 | } |
652 | link->refcnt ++; | ||
653 | up(&acpi_link_lock); | ||
643 | 654 | ||
644 | if (edge_level) *edge_level = link->irq.edge_level; | 655 | if (edge_level) *edge_level = link->irq.edge_level; |
645 | if (active_high_low) *active_high_low = link->irq.active_high_low; | 656 | if (active_high_low) *active_high_low = link->irq.active_high_low; |
646 | if (name) *name = acpi_device_bid(link->device); | 657 | if (name) *name = acpi_device_bid(link->device); |
658 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
659 | "Link %s is referenced\n", acpi_device_bid(link->device))); | ||
647 | return_VALUE(link->irq.active); | 660 | return_VALUE(link->irq.active); |
648 | } | 661 | } |
649 | 662 | ||
663 | /* | ||
664 | * We don't change link's irq information here. After it is reenabled, we | ||
665 | * continue use the info | ||
666 | */ | ||
667 | int | ||
668 | acpi_pci_link_free_irq(acpi_handle handle) | ||
669 | { | ||
670 | struct acpi_device *device = NULL; | ||
671 | struct acpi_pci_link *link = NULL; | ||
672 | acpi_status result; | ||
673 | |||
674 | ACPI_FUNCTION_TRACE("acpi_pci_link_free_irq"); | ||
675 | |||
676 | result = acpi_bus_get_device(handle, &device); | ||
677 | if (result) { | ||
678 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n")); | ||
679 | return_VALUE(-1); | ||
680 | } | ||
650 | 681 | ||
682 | link = (struct acpi_pci_link *) acpi_driver_data(device); | ||
683 | if (!link) { | ||
684 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); | ||
685 | return_VALUE(-1); | ||
686 | } | ||
687 | |||
688 | down(&acpi_link_lock); | ||
689 | if (!link->irq.initialized) { | ||
690 | up(&acpi_link_lock); | ||
691 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link isn't initialized\n")); | ||
692 | return_VALUE(-1); | ||
693 | } | ||
694 | |||
695 | link->refcnt --; | ||
696 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
697 | "Link %s is dereferenced\n", acpi_device_bid(link->device))); | ||
698 | |||
699 | if (link->refcnt == 0) { | ||
700 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); | ||
701 | } | ||
702 | up(&acpi_link_lock); | ||
703 | return_VALUE(link->irq.active); | ||
704 | } | ||
651 | /* -------------------------------------------------------------------------- | 705 | /* -------------------------------------------------------------------------- |
652 | Driver Interface | 706 | Driver Interface |
653 | -------------------------------------------------------------------------- */ | 707 | -------------------------------------------------------------------------- */ |
@@ -677,6 +731,7 @@ acpi_pci_link_add ( | |||
677 | strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); | 731 | strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); |
678 | acpi_driver_data(device) = link; | 732 | acpi_driver_data(device) = link; |
679 | 733 | ||
734 | down(&acpi_link_lock); | ||
680 | result = acpi_pci_link_get_possible(link); | 735 | result = acpi_pci_link_get_possible(link); |
681 | if (result) | 736 | if (result) |
682 | goto end; | 737 | goto end; |
@@ -712,6 +767,7 @@ acpi_pci_link_add ( | |||
712 | end: | 767 | end: |
713 | /* disable all links -- to be activated on use */ | 768 | /* disable all links -- to be activated on use */ |
714 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); | 769 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); |
770 | up(&acpi_link_lock); | ||
715 | 771 | ||
716 | if (result) | 772 | if (result) |
717 | kfree(link); | 773 | kfree(link); |
@@ -726,19 +782,32 @@ irqrouter_suspend( | |||
726 | { | 782 | { |
727 | struct list_head *node = NULL; | 783 | struct list_head *node = NULL; |
728 | struct acpi_pci_link *link = NULL; | 784 | struct acpi_pci_link *link = NULL; |
785 | int ret = 0; | ||
729 | 786 | ||
730 | ACPI_FUNCTION_TRACE("irqrouter_suspend"); | 787 | ACPI_FUNCTION_TRACE("irqrouter_suspend"); |
731 | 788 | ||
732 | list_for_each(node, &acpi_link.entries) { | 789 | list_for_each(node, &acpi_link.entries) { |
733 | link = list_entry(node, struct acpi_pci_link, node); | 790 | link = list_entry(node, struct acpi_pci_link, node); |
734 | if (!link) { | 791 | if (!link) { |
735 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); | 792 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
793 | "Invalid link context\n")); | ||
736 | continue; | 794 | continue; |
737 | } | 795 | } |
738 | if (link->irq.active && link->irq.initialized) | 796 | if (link->irq.initialized && link->refcnt != 0 |
739 | link->irq.suspend_resume = 1; | 797 | /* We ignore legacy IDE device irq */ |
798 | && link->irq.active != 14 && link->irq.active !=15) { | ||
799 | printk(KERN_WARNING PREFIX | ||
800 | "%d drivers with interrupt %d neglected to call" | ||
801 | " pci_disable_device at .suspend\n", | ||
802 | link->refcnt, | ||
803 | link->irq.active); | ||
804 | printk(KERN_WARNING PREFIX | ||
805 | "Fix the driver, or rmmod before suspend\n"); | ||
806 | link->refcnt = 0; | ||
807 | ret = -EINVAL; | ||
808 | } | ||
740 | } | 809 | } |
741 | return_VALUE(0); | 810 | return_VALUE(ret); |
742 | } | 811 | } |
743 | 812 | ||
744 | 813 | ||
@@ -756,8 +825,9 @@ acpi_pci_link_remove ( | |||
756 | 825 | ||
757 | link = (struct acpi_pci_link *) acpi_driver_data(device); | 826 | link = (struct acpi_pci_link *) acpi_driver_data(device); |
758 | 827 | ||
759 | /* TBD: Acquire/release lock */ | 828 | down(&acpi_link_lock); |
760 | list_del(&link->node); | 829 | list_del(&link->node); |
830 | up(&acpi_link_lock); | ||
761 | 831 | ||
762 | kfree(link); | 832 | kfree(link); |
763 | 833 | ||
@@ -849,6 +919,7 @@ int __init acpi_irq_balance_set(char *str) | |||
849 | __setup("acpi_irq_balance", acpi_irq_balance_set); | 919 | __setup("acpi_irq_balance", acpi_irq_balance_set); |
850 | 920 | ||
851 | 921 | ||
922 | /* FIXME: we will remove this interface after all drivers call pci_disable_device */ | ||
852 | static struct sysdev_class irqrouter_sysdev_class = { | 923 | static struct sysdev_class irqrouter_sysdev_class = { |
853 | set_kset_name("irqrouter"), | 924 | set_kset_name("irqrouter"), |
854 | .suspend = irqrouter_suspend, | 925 | .suspend = irqrouter_suspend, |