drivers/acpi/acpica/aclocal.h | 7 +-- drivers/acpi/acpica/evgpe.c | 75 +++++++++++++++----------------- drivers/acpi/acpica/evgpeinit.c | 11 +---- drivers/acpi/acpica/evgpeutil.c | 5 +- drivers/acpi/acpica/evxface.c | 23 +++++----- drivers/acpi/ec.c | 2 +- drivers/acpi/pci_bind.c | 86 ++++++++++++++++++++++++++++++++++++++ drivers/acpi/sleep.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 2 +- include/acpi/acpixf.h | 3 +- 10 files changed, 143 insertions(+), 73 deletions(-) diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2ceb0c0..3663362 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -406,17 +406,16 @@ struct acpi_predefined_data { * ****************************************************************************/ -/* Dispatch info for each GPE -- either a method or handler, cannot be both */ +/* Dispatch info for each GPE */ struct acpi_handler_info { acpi_event_handler address; /* Address of handler, if any */ void *context; /* Context to be passed to handler */ - struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ u8 orig_flags; /* Original misc info about this GPE */ u8 orig_enabled; /* Set if the GPE was originally enabled */ }; -union acpi_gpe_dispatch_info { +struct acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; /* Method node for this GPE level */ struct acpi_handler_info *handler; }; @@ -426,7 +425,7 @@ union acpi_gpe_dispatch_info { * NOTE: Important to keep this struct as small as possible. */ struct acpi_gpe_event_info { - union acpi_gpe_dispatch_info dispatch; /* Either Method or Handler */ + struct acpi_gpe_dispatch_info dispatch; struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ u8 flags; /* Misc info about this GPE */ u8 gpe_number; /* This GPE */ diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index f226eac..c4b1c4c 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -474,9 +474,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * Must check for control method type dispatch one more time to avoid a * race with ev_gpe_install_handler */ - if ((local_gpe_event_info.flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_METHOD) { - + if (local_gpe_event_info.flags & ACPI_GPE_DISPATCH_METHOD) { /* Allocate the evaluation information block */ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); @@ -575,41 +573,15 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) } /* - * Dispatch the GPE to either an installed handler, or the control method - * associated with this GPE (_Lxx or _Exx). If a handler exists, we invoke - * it and do not attempt to run the method. If there is neither a handler - * nor a method, we disable this GPE to prevent further such pointless - * events from firing. + * Dispatch the GPE to either any installed handler or control + * method associated with this GPE (_Lxx or _Exx). We invoke + * the method first in case it has side effects that would be + * interfered with if the handler has already altered hardware + * state. If there is neither a handler nor a method, we + * disable this GPE to prevent further such pointless events + * from firing. */ - switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { - case ACPI_GPE_DISPATCH_HANDLER: - - /* - * Invoke the installed handler (at interrupt level) - * Ignore return status for now. - * TBD: leave GPE disabled on error? - */ - (void)gpe_event_info->dispatch.handler->address(gpe_event_info-> - dispatch. - handler-> - context); - - /* It is now safe to clear level-triggered events. */ - - if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == - ACPI_GPE_LEVEL_TRIGGERED) { - status = acpi_hw_clear_gpe(gpe_event_info); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to clear GPE[0x%2X]", - gpe_number)); - return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); - } - } - break; - - case ACPI_GPE_DISPATCH_METHOD: - + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) { /* * Disable the GPE, so it doesn't keep firing before the method has a * chance to run (it runs asynchronously with interrupts enabled). @@ -634,10 +606,34 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) "Unable to queue handler for GPE[0x%2X] - event disabled", gpe_number)); } - break; + } - default: + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER) { + /* + * Invoke the installed handler (at interrupt level) + * Ignore return status for now. + * TBD: leave GPE disabled on error? + */ + (void)gpe_event_info->dispatch.handler->address(gpe_event_info-> + dispatch. + handler-> + context); + /* It is now safe to clear level-triggered events. */ + + if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == + ACPI_GPE_LEVEL_TRIGGERED) { + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to clear GPE[0x%2X]", + gpe_number)); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); + } + } + } + + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { /* * No handler or method to run! * 03/2010: This case should no longer be possible. We will not allow @@ -658,7 +654,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) gpe_number)); return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } - break; } return_UINT32(ACPI_INTERRUPT_HANDLED); diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 2c7def9..9915b52 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -386,16 +386,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, return_ACPI_STATUS(AE_OK); } - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { - - /* If there is already a handler, ignore this GPE method */ - - return_ACPI_STATUS(AE_OK); - } - - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_METHOD) { + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD) { /* * If there is already a method, ignore this method. But check * for a type mismatch (if both the _Lxx AND _Exx exist) diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 19a0e51..434ad1b 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -323,12 +323,11 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, ACPI_GPE_REGISTER_WIDTH) + j]; - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER) { ACPI_FREE(gpe_event_info->dispatch.handler); gpe_event_info->dispatch.handler = NULL; gpe_event_info->flags &= - ~ACPI_GPE_DISPATCH_MASK; + ~ACPI_GPE_DISPATCH_HANDLER; } } } diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 36af222..b097136 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -662,6 +662,8 @@ ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler) * edge- or level-triggered interrupt. * Address - Address of the handler * Context - Value passed to the handler on each GPE + * keep_method - Whether the existing method should be + * displaced or kept * * RETURN: Status * @@ -671,7 +673,8 @@ ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler) acpi_status acpi_install_gpe_handler(acpi_handle gpe_device, u32 gpe_number, - u32 type, acpi_event_handler address, void *context) + u32 type, acpi_event_handler address, void *context, + bool keep_method) { struct acpi_gpe_event_info *gpe_event_info; struct acpi_handler_info *handler; @@ -711,8 +714,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, /* Make sure that there isn't a handler there already */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER) { status = AE_ALREADY_EXISTS; goto free_and_exit; } @@ -721,7 +723,6 @@ acpi_install_gpe_handler(acpi_handle gpe_device, handler->address = address; handler->context = context; - handler->method_node = gpe_event_info->dispatch.method_node; handler->orig_flags = gpe_event_info->flags & (ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); @@ -732,7 +733,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, */ if ((handler->orig_flags & ACPI_GPE_DISPATCH_METHOD) - && gpe_event_info->runtime_count) { + && gpe_event_info->runtime_count && !keep_method) { handler->orig_enabled = 1; (void)acpi_raw_disable_gpe(gpe_event_info); } @@ -741,10 +742,10 @@ acpi_install_gpe_handler(acpi_handle gpe_device, gpe_event_info->dispatch.handler = handler; - /* Setup up dispatch flags to indicate handler (vs. method) */ + if (!keep_method) + gpe_event_info->flags &= + ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); - gpe_event_info->flags &= - ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); gpe_event_info->flags |= (u8) (type | ACPI_GPE_DISPATCH_HANDLER); acpi_os_release_lock(acpi_gbl_gpe_lock, flags); @@ -813,8 +814,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, /* Make sure that a handler is indeed installed */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != - ACPI_GPE_DISPATCH_HANDLER) { + if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_HANDLER)) { status = AE_NOT_EXIST; goto unlock_and_exit; } @@ -830,9 +830,8 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, handler = gpe_event_info->dispatch.handler; - /* Restore Method node (if any), set dispatch flags */ + /* Set dispatch flags */ - gpe_event_info->dispatch.method_node = handler->method_node; gpe_event_info->flags &= ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); gpe_event_info->flags |= handler->orig_flags; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 372ff80..9a39f82 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -740,7 +740,7 @@ static int ec_install_handlers(struct acpi_ec *ec) return 0; status = acpi_install_gpe_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec); + &acpi_ec_gpe_handler, ec, false); if (ACPI_FAILURE(status)) return -ENODEV; diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 2ef0409..8b3cc6a 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,43 @@ #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_bind"); +static LIST_HEAD(acpi_pci_gpe_devs); + +struct pci_gpe_dev { + struct list_head node; + struct pci_dev *dev; + acpi_handle gpe_device; + int gpe_number; + struct work_struct work; +}; + +static void acpi_pci_wake_handler_work(struct work_struct *work) +{ + struct pci_gpe_dev *gpe_dev = container_of(work, struct pci_gpe_dev, + work); + + pci_check_pme_status(gpe_dev->dev); + pm_runtime_resume(&gpe_dev->dev->dev); + pci_wakeup_event(gpe_dev->dev); + if (gpe_dev->dev->subordinate) + pci_pme_wakeup_bus(gpe_dev->dev->subordinate); +} + +static u32 acpi_pci_wake_handler(void *data) +{ + long gpe_number = (long) data; + struct pci_gpe_dev *gpe_dev; + + list_for_each_entry(gpe_dev, &acpi_pci_gpe_devs, node) { + if (gpe_number != gpe_dev->gpe_number) + continue; + + schedule_work(&gpe_dev->work); + } + + return ACPI_INTERRUPT_HANDLED; +} + static int acpi_pci_unbind(struct acpi_device *device) { struct pci_dev *dev; @@ -43,6 +81,30 @@ static int acpi_pci_unbind(struct acpi_device *device) if (!dev) goto out; + if (device->wakeup.flags.valid) { + struct pci_gpe_dev *gpe_dev; + struct pci_gpe_dev *tmp; + int gpe_count = 0; + int gpe_number = device->wakeup.gpe_number; + acpi_handle gpe_device = device->wakeup.gpe_device; + + list_for_each_entry_safe(gpe_dev, tmp, &acpi_pci_gpe_devs, node) { + if (gpe_dev->dev == dev) { + flush_work(&gpe_dev->work); + list_del(&gpe_dev->node); + kfree(gpe_dev); + } else if (gpe_dev->gpe_number == gpe_number && + gpe_dev->gpe_device == gpe_device) { + gpe_count++; + } + } + + if (gpe_count == 0) { + acpi_remove_gpe_handler(gpe_device, gpe_number, + &acpi_pci_wake_handler); + } + } + device_set_run_wake(&dev->dev, false); pci_acpi_remove_pm_notifier(device); @@ -71,6 +133,30 @@ static int acpi_pci_bind(struct acpi_device *device) return 0; pci_acpi_add_pm_notifier(device, dev); + if (device->wakeup.flags.valid) { + struct pci_gpe_dev *gpe_dev; + acpi_handle gpe_device = device->wakeup.gpe_device; + long gpe_number = device->wakeup.gpe_number; + + gpe_dev = kmalloc(sizeof(struct pci_gpe_dev), GFP_KERNEL); + if (gpe_dev) { + gpe_dev->dev = dev; + gpe_dev->gpe_device = gpe_device; + gpe_dev->gpe_number = gpe_number; + INIT_WORK(&gpe_dev->work, acpi_pci_wake_handler_work); + + acpi_install_gpe_handler(gpe_device, gpe_number, + ACPI_GPE_LEVEL_TRIGGERED, + &acpi_pci_wake_handler, + (void *)gpe_number, + true); + acpi_gpe_can_wake(device->wakeup.gpe_device, + device->wakeup.gpe_number); + device->wakeup.flags.run_wake = 1; + list_add_tail(&gpe_dev->node, &acpi_pci_gpe_devs); + } + } + if (device->wakeup.flags.run_wake) device_set_run_wake(&dev->dev, true); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 721d93b..b0ddef6 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -643,7 +643,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) if (acpi_target_sleep_state != ACPI_STATE_S0 || status != AE_NOT_FOUND) d_max = d_min; - } else if (d_max < d_min) { + } else if (ACPI_SUCCESS(status) && d_max < d_min) { /* Warn the user of the broken DSDT */ printk(KERN_WARNING "ACPI: Wrong value from %s\n", acpi_method); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 035da9e..62a48b2 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1970,7 +1970,7 @@ static int acpi_gpe_irq_setup(struct smi_info *info) info->irq, ACPI_GPE_LEVEL_TRIGGERED, &ipmi_acpi_gpe, - info); + info, false); if (status != AE_OK) { dev_warn(info->dev, "%s unable to claim ACPI GPE %d," " running polled\n", DEVICE_NAME, info->irq); diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 53b7cfd..740eb7e 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -258,7 +258,8 @@ acpi_remove_address_space_handler(acpi_handle device, acpi_status acpi_install_gpe_handler(acpi_handle gpe_device, u32 gpe_number, - u32 type, acpi_event_handler address, void *context); + u32 type, acpi_event_handler address, void *context, + bool keep_method); acpi_status acpi_remove_gpe_handler(acpi_handle gpe_device,