diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 09:18:18 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 09:18:18 +0400 |
commit | 556f12f602ac0a18a82ca83e9f8e8547688fc633 (patch) | |
tree | d4051f6dd57968c8e8e660ad117c5bedc2aa7e8e /drivers/acpi | |
parent | fffddfd6c8e0c10c42c6e2cc54ba880fcc36ebbb (diff) | |
parent | 018ba0a6efada61b9bc17500101d81c3d35807c2 (diff) | |
download | linux-556f12f602ac0a18a82ca83e9f8e8547688fc633.tar.xz |
Merge tag 'pci-v3.9-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI changes from Bjorn Helgaas:
"Host bridge hotplug
- Major overhaul of ACPI host bridge add/start (Rafael Wysocki, Yinghai Lu)
- Major overhaul of PCI/ACPI binding (Rafael Wysocki, Yinghai Lu)
- Split out ACPI host bridge and ACPI PCI device hotplug (Yinghai Lu)
- Stop caching _PRT and make independent of bus numbers (Yinghai Lu)
PCI device hotplug
- Clean up cpqphp dead code (Sasha Levin)
- Disable ARI unless device and upstream bridge support it (Yijing Wang)
- Initialize all hot-added devices (not functions 0-7) (Yijing Wang)
Power management
- Don't touch ASPM if disabled (Joe Lawrence)
- Fix ASPM link state management (Myron Stowe)
Miscellaneous
- Fix PCI_EXP_FLAGS accessor (Alex Williamson)
- Disable Bus Master in pci_device_shutdown (Konstantin Khlebnikov)
- Document hotplug resource and MPS parameters (Yijing Wang)
- Add accessor for PCIe capabilities (Myron Stowe)
- Drop pciehp suspend/resume messages (Paul Bolle)
- Make pci_slot built-in only (not a module) (Jiang Liu)
- Remove unused PCI/ACPI bind ops (Jiang Liu)
- Removed used pci_root_bus (Bjorn Helgaas)"
* tag 'pci-v3.9-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (51 commits)
PCI/ACPI: Don't cache _PRT, and don't associate them with bus numbers
PCI: Fix PCI Express Capability accessors for PCI_EXP_FLAGS
ACPI / PCI: Make pci_slot built-in only, not a module
PCI/PM: Clear state_saved during suspend
PCI: Use atomic_inc_return() rather than atomic_add_return()
PCI: Catch attempts to disable already-disabled devices
PCI: Disable Bus Master unconditionally in pci_device_shutdown()
PCI: acpiphp: Remove dead code for PCI host bridge hotplug
PCI: acpiphp: Create companion ACPI devices before creating PCI devices
PCI: Remove unused "rc" in virtfn_add_bus()
PCI: pciehp: Drop suspend/resume ENTRY messages
PCI/ASPM: Don't touch ASPM if forcibly disabled
PCI/ASPM: Deallocate upstream link state even if device is not PCIe
PCI: Document MPS parameters pci=pcie_bus_safe, pci=pcie_bus_perf, etc
PCI: Document hpiosize= and hpmemsize= resource reservation parameters
PCI: Use PCI Express Capability accessor
PCI: Introduce accessor to retrieve PCIe Capabilities Register
PCI: Put pci_dev in device tree as early as possible
PCI: Skip attaching driver in device_add()
PCI: acpiphp: Keep driver loaded even if no slots found
...
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 5 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 24 | ||||
-rw-r--r-- | drivers/acpi/pci_irq.c | 102 | ||||
-rw-r--r-- | drivers/acpi/pci_root.c | 170 | ||||
-rw-r--r-- | drivers/acpi/pci_slot.c | 13 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 3 |
7 files changed, 200 insertions, 123 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 1a4ed64586a7..320006019e68 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -306,7 +306,7 @@ config ACPI_DEBUG_FUNC_TRACE is about half of the penalty and is rarely useful. config ACPI_PCI_SLOT - tristate "PCI slot detection driver" + bool "PCI slot detection driver" depends on SYSFS default n help @@ -315,9 +315,6 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N. - To compile this driver as a module, choose M here: - the module will be called pci_slot. - config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 79092328cf06..c8b70b5b2814 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -25,8 +25,14 @@ int init_acpi_device_notify(void); int acpi_scan_init(void); +#ifdef CONFIG_ACPI_PCI_SLOT +void acpi_pci_slot_init(void); +#else +static inline void acpi_pci_slot_init(void) { } +#endif void acpi_pci_root_init(void); void acpi_pci_link_init(void); +void acpi_pci_root_hp_init(void); void acpi_platform_init(void); int acpi_sysfs_init(void); void acpi_csrt_init(void); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 8080588f88cb..586e7e993d3d 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -84,8 +84,7 @@ static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpi_notify_wq; -struct workqueue_struct *kacpi_hotplug_wq; -EXPORT_SYMBOL(kacpi_hotplug_wq); +static struct workqueue_struct *kacpi_hotplug_wq; /* * This list of permanent mappings is for memory that may be accessed from @@ -1778,3 +1777,24 @@ void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, { __acpi_os_prepare_sleep = func; } + +void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, + void (*func)(struct work_struct *work)) +{ + struct acpi_hp_work *hp_work; + int ret; + + hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL); + if (!hp_work) + return; + + hp_work->handle = handle; + hp_work->type = type; + hp_work->context = context; + + INIT_WORK(&hp_work->work, func); + ret = queue_work(kacpi_hotplug_wq, &hp_work->work); + if (!ret) + kfree(hp_work); +} +EXPORT_SYMBOL_GPL(alloc_acpi_hp_work); diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 68a921d03247..41c5e1b799ef 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -53,9 +53,6 @@ struct acpi_prt_entry { u32 index; /* GSI, or link _CRS index */ }; -static LIST_HEAD(acpi_prt_list); -static DEFINE_SPINLOCK(acpi_prt_lock); - static inline char pin_name(int pin) { return 'A' + pin - 1; @@ -65,28 +62,6 @@ static inline char pin_name(int pin) PCI IRQ Routing Table (PRT) Support -------------------------------------------------------------------------- */ -static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(struct pci_dev *dev, - int pin) -{ - struct acpi_prt_entry *entry; - int segment = pci_domain_nr(dev->bus); - int bus = dev->bus->number; - int device = PCI_SLOT(dev->devfn); - - spin_lock(&acpi_prt_lock); - list_for_each_entry(entry, &acpi_prt_list, list) { - if ((segment == entry->id.segment) - && (bus == entry->id.bus) - && (device == entry->id.device) - && (pin == entry->pin)) { - spin_unlock(&acpi_prt_lock); - return entry; - } - } - spin_unlock(&acpi_prt_lock); - return NULL; -} - /* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ static const struct dmi_system_id medion_md9580[] = { { @@ -184,11 +159,19 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } } -static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, - struct acpi_pci_routing_table *prt) +static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, + int pin, struct acpi_pci_routing_table *prt, + struct acpi_prt_entry **entry_ptr) { + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); struct acpi_prt_entry *entry; + if (((prt->address >> 16) & 0xffff) != device || + prt->pin + 1 != pin) + return -ENODEV; + entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -237,43 +220,37 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, entry->id.device, pin_name(entry->pin), prt->source, entry->index)); - spin_lock(&acpi_prt_lock); - list_add_tail(&entry->list, &acpi_prt_list); - spin_unlock(&acpi_prt_lock); + *entry_ptr = entry; return 0; } -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) +static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin, struct acpi_prt_entry **entry_ptr) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_pci_routing_table *entry; + acpi_handle handle = NULL; - /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ - status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", - (char *) buffer.pointer); - - kfree(buffer.pointer); + if (dev->bus->bridge) + handle = ACPI_HANDLE(dev->bus->bridge); - buffer.length = ACPI_ALLOCATE_BUFFER; - buffer.pointer = NULL; + if (!handle) + return -ENODEV; + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ status = acpi_get_irq_routing_table(handle, &buffer); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]", - acpi_format_exception(status))); kfree(buffer.pointer); return -ENODEV; } entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + if (!acpi_pci_irq_check_entry(handle, dev, pin, + entry, entry_ptr)) + break; entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } @@ -282,23 +259,6 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) return 0; } -void acpi_pci_irq_del_prt(int segment, int bus) -{ - struct acpi_prt_entry *entry, *tmp; - - printk(KERN_DEBUG - "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", - segment, bus); - spin_lock(&acpi_prt_lock); - list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { - if (segment == entry->id.segment && bus == entry->id.bus) { - list_del(&entry->list); - kfree(entry); - } - } - spin_unlock(&acpi_prt_lock); -} - /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ @@ -359,12 +319,13 @@ static int acpi_reroute_boot_interrupt(struct pci_dev *dev, static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) { - struct acpi_prt_entry *entry; + struct acpi_prt_entry *entry = NULL; struct pci_dev *bridge; u8 bridge_pin, orig_pin = pin; + int ret; - entry = acpi_pci_irq_find_prt_entry(dev, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + if (!ret && entry) { #ifdef CONFIG_X86_IO_APIC acpi_reroute_boot_interrupt(dev, entry); #endif /* CONFIG_X86_IO_APIC */ @@ -373,7 +334,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) return entry; } - /* + /* * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ @@ -393,8 +354,8 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) pin = bridge_pin; } - entry = acpi_pci_irq_find_prt_entry(bridge, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + if (!ret && entry) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derived GSI for %s INT %c from %s\n", pci_name(dev), pin_name(orig_pin), @@ -470,6 +431,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) dev_warn(&dev->dev, "PCI INT %c: no GSI\n", pin_name(pin)); } + return 0; } @@ -477,6 +439,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) if (rc < 0) { dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", pin_name(pin)); + kfree(entry); return rc; } dev->irq = rc; @@ -491,6 +454,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); + kfree(entry); return 0; } @@ -513,6 +477,8 @@ void acpi_pci_irq_disable(struct pci_dev *dev) else gsi = entry->index; + kfree(entry); + /* * TBD: It might be worth clearing dev->irq by magic constant * (e.g. PCI_UNDEFINED_IRQ). diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index b3cc69c5caf1..0ac546d5e53f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -103,24 +103,6 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) } EXPORT_SYMBOL(acpi_pci_unregister_driver); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) -{ - struct acpi_pci_root *root; - acpi_handle handle = NULL; - - mutex_lock(&acpi_pci_root_lock); - list_for_each_entry(root, &acpi_pci_roots, node) - if ((root->segment == (u16) seg) && - (root->secondary.start == (u16) bus)) { - handle = root->device->handle; - break; - } - mutex_unlock(&acpi_pci_root_lock); - return handle; -} - -EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); - /** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge * @handle - the ACPI CA node in question. @@ -431,7 +413,6 @@ static int acpi_pci_root_add(struct acpi_device *device, acpi_status status; int result; struct acpi_pci_root *root; - acpi_handle handle; struct acpi_pci_driver *driver; u32 flags, base_flags; bool is_osc_granted = false; @@ -486,16 +467,6 @@ static int acpi_pci_root_add(struct acpi_device *device, acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); - /* - * PCI Routing Table - * ----------------- - * Evaluate and parse _PRT, if exists. - */ - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - result = acpi_pci_irq_add_prt(device->handle, root->segment, - root->secondary.start); - root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); /* @@ -597,8 +568,10 @@ static int acpi_pci_root_add(struct acpi_device *device, if (device->wakeup.flags.run_wake) device_set_run_wake(root->bus->bridge, true); - if (system_state != SYSTEM_BOOTING) + if (system_state != SYSTEM_BOOTING) { + pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_bus_resources(root->bus); + } mutex_lock(&acpi_pci_root_lock); list_for_each_entry(driver, &acpi_pci_drivers, node) @@ -618,7 +591,6 @@ out_del_root: list_del(&root->node); mutex_unlock(&acpi_pci_root_lock); - acpi_pci_irq_del_prt(root->segment, root->secondary.start); end: kfree(root); return result; @@ -626,8 +598,6 @@ end: static void acpi_pci_root_remove(struct acpi_device *device) { - acpi_status status; - acpi_handle handle; struct acpi_pci_root *root = acpi_driver_data(device); struct acpi_pci_driver *driver; @@ -642,10 +612,6 @@ static void acpi_pci_root_remove(struct acpi_device *device) device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device); - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - acpi_pci_irq_del_prt(root->segment, root->secondary.start); - pci_remove_root_bus(root->bus); mutex_lock(&acpi_pci_root_lock); @@ -663,3 +629,133 @@ void __init acpi_pci_root_init(void) acpi_scan_add_handler(&pci_root_handler); } } +/* Support root bridge hotplug */ + +static void handle_root_bridge_insertion(acpi_handle handle) +{ + struct acpi_device *device; + + if (!acpi_bus_get_device(handle, &device)) { + printk(KERN_DEBUG "acpi device exists...\n"); + return; + } + + if (acpi_bus_scan(handle)) + printk(KERN_ERR "cannot add bridge to acpi list\n"); +} + +static void handle_root_bridge_removal(struct acpi_device *device) +{ + struct acpi_eject_event *ej_event; + + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + /* Inform firmware the hot-remove operation has error */ + (void) acpi_evaluate_hotplug_ost(device->handle, + ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); + return; + } + + ej_event->device = device; + ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + + acpi_bus_hot_remove_device(ej_event); +} + +static void _handle_hotplug_event_root(struct work_struct *work) +{ + struct acpi_pci_root *root; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; + struct acpi_hp_work *hp_work; + acpi_handle handle; + u32 type; + + hp_work = container_of(work, struct acpi_hp_work, work); + handle = hp_work->handle; + type = hp_work->type; + + root = acpi_pci_find_root(handle); + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + /* bus enumerate */ + printk(KERN_DEBUG "%s: Bus check notify on %s\n", __func__, + (char *)buffer.pointer); + if (!root) + handle_root_bridge_insertion(handle); + + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + /* device check */ + printk(KERN_DEBUG "%s: Device check notify on %s\n", __func__, + (char *)buffer.pointer); + if (!root) + handle_root_bridge_insertion(handle); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + /* request device eject */ + printk(KERN_DEBUG "%s: Device eject notify on %s\n", __func__, + (char *)buffer.pointer); + if (root) + handle_root_bridge_removal(root->device); + break; + default: + printk(KERN_WARNING "notify_handler: unknown event type 0x%x for %s\n", + type, (char *)buffer.pointer); + break; + } + + kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ + kfree(buffer.pointer); +} + +static void handle_hotplug_event_root(acpi_handle handle, u32 type, + void *context) +{ + alloc_acpi_hp_work(handle, type, context, + _handle_hotplug_event_root); +} + +static acpi_status __init +find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + acpi_status status; + char objname[64]; + struct acpi_buffer buffer = { .length = sizeof(objname), + .pointer = objname }; + int *count = (int *)context; + + if (!acpi_is_root_bridge(handle)) + return AE_OK; + + (*count)++; + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + handle_hotplug_event_root, NULL); + if (ACPI_FAILURE(status)) + printk(KERN_DEBUG "acpi root: %s notify handler is not installed, exit status: %u\n", + objname, (unsigned int)status); + else + printk(KERN_DEBUG "acpi root: %s notify handler is installed\n", + objname); + + return AE_OK; +} + +void __init acpi_pci_root_hp_init(void) +{ + int num = 0; + + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); + + printk(KERN_DEBUG "Found %d acpi root devices\n", num); +} diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 2c630c006c2f..cd1434eb1de8 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -329,19 +329,8 @@ static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { {} }; -static int __init -acpi_pci_slot_init(void) +void __init acpi_pci_slot_init(void) { dmi_check_system(acpi_pci_slot_dmi_table); acpi_pci_register_driver(&acpi_pci_slot_driver); - return 0; } - -static void __exit -acpi_pci_slot_exit(void) -{ - acpi_pci_unregister_driver(&acpi_pci_slot_driver); -} - -module_init(acpi_pci_slot_init); -module_exit(acpi_pci_slot_exit); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index daee7497efd3..4be408756adc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1783,6 +1783,7 @@ int __init acpi_scan_init(void) acpi_platform_init(); acpi_csrt_init(); acpi_container_init(); + acpi_pci_slot_init(); mutex_lock(&acpi_scan_lock); /* @@ -1804,6 +1805,8 @@ int __init acpi_scan_init(void) acpi_update_all_gpes(); + acpi_pci_root_hp_init(); + out: mutex_unlock(&acpi_scan_lock); return result; |