Merge branch 'acpi-pm'
* acpi-pm: PM / core: Drop run_wake flag from struct dev_pm_info PCI / PM: Simplify device wakeup settings code PCI / PM: Drop pme_interrupt flag from struct pci_dev ACPI / PM: Consolidate device wakeup settings code ACPI / PM: Drop run_wake from struct acpi_device_wakeup_flags ACPI / sleep: EC-based wakeup from suspend-to-idle on recent systems platform: x86: intel-hid: Wake up the system from suspend-to-idle platform: x86: intel-vbtn: Wake up the system from suspend-to-idle ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle platform/x86: Add driver for ACPI INT0002 Virtual GPIO device PCI / PM: Restore PME Enable if skipping wakeup setup PM / sleep: Print timing information if debug is enabled ACPI / PM: Clean up device wakeup enable/disable code ACPI / PM: Change log level of wakeup-related message USB / PCI / PM: Allow the PCI core to do the resume cleanup ACPI / PM: Run wakeup notify handlers synchronously Conflicts: drivers/base/power/main.c
This commit is contained in:
commit
8f8e5c3e27
|
@ -105,9 +105,9 @@ knows what to do to handle the device).
|
|||
|
||||
In particular, if the driver requires remote wakeup capability (i.e. hardware
|
||||
mechanism allowing the device to request a change of its power state, such as
|
||||
PCI PME) for proper functioning and device_run_wake() returns 'false' for the
|
||||
PCI PME) for proper functioning and device_can_wakeup() returns 'false' for the
|
||||
device, then ->runtime_suspend() should return -EBUSY. On the other hand, if
|
||||
device_run_wake() returns 'true' for the device and the device is put into a
|
||||
device_can_wakeup() returns 'true' for the device and the device is put into a
|
||||
low-power state during the execution of the suspend callback, it is expected
|
||||
that remote wakeup will be enabled for the device. Generally, remote wakeup
|
||||
should be enabled for all input devices put into low-power states at run time.
|
||||
|
@ -253,9 +253,6 @@ defined in include/linux/pm.h:
|
|||
being executed for that device and it is not practical to wait for the
|
||||
suspend to complete; means "start a resume as soon as you've suspended"
|
||||
|
||||
unsigned int run_wake;
|
||||
- set if the device is capable of generating runtime wake-up events
|
||||
|
||||
enum rpm_status runtime_status;
|
||||
- the runtime PM status of the device; this field's initial value is
|
||||
RPM_SUSPENDED, which means that each device is initially regarded by the
|
||||
|
|
|
@ -782,7 +782,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
|
|||
if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) ||
|
||||
(test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) &&
|
||||
(battery->capacity_now <= battery->alarm)))
|
||||
pm_wakeup_event(&battery->device->dev, 0);
|
||||
acpi_pm_wakeup_event(&battery->device->dev);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -217,7 +217,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
|
|||
}
|
||||
|
||||
if (state)
|
||||
pm_wakeup_event(&device->dev, 0);
|
||||
acpi_pm_wakeup_event(&device->dev);
|
||||
|
||||
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
|
||||
if (ret == NOTIFY_DONE)
|
||||
|
@ -402,7 +402,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event)
|
|||
} else {
|
||||
int keycode;
|
||||
|
||||
pm_wakeup_event(&device->dev, 0);
|
||||
acpi_pm_wakeup_event(&device->dev);
|
||||
if (button->suspended)
|
||||
break;
|
||||
|
||||
|
@ -534,6 +534,7 @@ static int acpi_button_add(struct acpi_device *device)
|
|||
lid_device = device;
|
||||
}
|
||||
|
||||
device_init_wakeup(&device->dev, true);
|
||||
printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/pm_qos.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
@ -385,6 +386,12 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
|
|||
#ifdef CONFIG_PM
|
||||
static DEFINE_MUTEX(acpi_pm_notifier_lock);
|
||||
|
||||
void acpi_pm_wakeup_event(struct device *dev)
|
||||
{
|
||||
pm_wakeup_dev_event(dev, 0, acpi_s2idle_wakeup());
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_pm_wakeup_event);
|
||||
|
||||
static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
@ -399,9 +406,9 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
|
|||
mutex_lock(&acpi_pm_notifier_lock);
|
||||
|
||||
if (adev->wakeup.flags.notifier_present) {
|
||||
__pm_wakeup_event(adev->wakeup.ws, 0);
|
||||
if (adev->wakeup.context.work.func)
|
||||
queue_pm_work(&adev->wakeup.context.work);
|
||||
pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup());
|
||||
if (adev->wakeup.context.func)
|
||||
adev->wakeup.context.func(&adev->wakeup.context);
|
||||
}
|
||||
|
||||
mutex_unlock(&acpi_pm_notifier_lock);
|
||||
|
@ -413,7 +420,7 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
|
|||
* acpi_add_pm_notifier - Register PM notify handler for given ACPI device.
|
||||
* @adev: ACPI device to add the notify handler for.
|
||||
* @dev: Device to generate a wakeup event for while handling the notification.
|
||||
* @work_func: Work function to execute when handling the notification.
|
||||
* @func: Work function to execute when handling the notification.
|
||||
*
|
||||
* NOTE: @adev need not be a run-wake or wakeup device to be a valid source of
|
||||
* PM wakeup events. For example, wakeup events may be generated for bridges
|
||||
|
@ -421,11 +428,11 @@ static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
|
|||
* bridge itself doesn't have a wakeup GPE associated with it.
|
||||
*/
|
||||
acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
|
||||
void (*work_func)(struct work_struct *work))
|
||||
void (*func)(struct acpi_device_wakeup_context *context))
|
||||
{
|
||||
acpi_status status = AE_ALREADY_EXISTS;
|
||||
|
||||
if (!dev && !work_func)
|
||||
if (!dev && !func)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
mutex_lock(&acpi_pm_notifier_lock);
|
||||
|
@ -435,8 +442,7 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
|
|||
|
||||
adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
|
||||
adev->wakeup.context.dev = dev;
|
||||
if (work_func)
|
||||
INIT_WORK(&adev->wakeup.context.work, work_func);
|
||||
adev->wakeup.context.func = func;
|
||||
|
||||
status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY,
|
||||
acpi_pm_notify_handler, NULL);
|
||||
|
@ -469,10 +475,7 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
|
|||
if (ACPI_FAILURE(status))
|
||||
goto out;
|
||||
|
||||
if (adev->wakeup.context.work.func) {
|
||||
cancel_work_sync(&adev->wakeup.context.work);
|
||||
adev->wakeup.context.work.func = NULL;
|
||||
}
|
||||
adev->wakeup.context.func = NULL;
|
||||
adev->wakeup.context.dev = NULL;
|
||||
wakeup_source_unregister(adev->wakeup.ws);
|
||||
|
||||
|
@ -493,6 +496,13 @@ bool acpi_bus_can_wakeup(acpi_handle handle)
|
|||
}
|
||||
EXPORT_SYMBOL(acpi_bus_can_wakeup);
|
||||
|
||||
bool acpi_pm_device_can_wakeup(struct device *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
|
||||
return adev ? acpi_device_can_wakeup(adev) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_pm_get_state - Get preferred power state of ACPI device.
|
||||
* @dev: Device whose preferred target power state to return.
|
||||
|
@ -658,16 +668,15 @@ EXPORT_SYMBOL(acpi_pm_device_sleep_state);
|
|||
|
||||
/**
|
||||
* acpi_pm_notify_work_func - ACPI devices wakeup notification work function.
|
||||
* @work: Work item to handle.
|
||||
* @context: Device wakeup context.
|
||||
*/
|
||||
static void acpi_pm_notify_work_func(struct work_struct *work)
|
||||
static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context)
|
||||
{
|
||||
struct device *dev;
|
||||
struct device *dev = context->dev;
|
||||
|
||||
dev = container_of(work, struct acpi_device_wakeup_context, work)->dev;
|
||||
if (dev) {
|
||||
pm_wakeup_event(dev, 0);
|
||||
pm_runtime_resume(dev);
|
||||
pm_request_resume(dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -693,80 +702,53 @@ static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state,
|
|||
acpi_status res;
|
||||
int error;
|
||||
|
||||
if (adev->wakeup.flags.enabled)
|
||||
return 0;
|
||||
|
||||
error = acpi_enable_wakeup_device_power(adev, target_state);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (adev->wakeup.flags.enabled)
|
||||
return 0;
|
||||
|
||||
res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
|
||||
if (ACPI_SUCCESS(res)) {
|
||||
adev->wakeup.flags.enabled = 1;
|
||||
} else {
|
||||
if (ACPI_FAILURE(res)) {
|
||||
acpi_disable_wakeup_device_power(adev);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
if (adev->wakeup.flags.enabled) {
|
||||
acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
|
||||
adev->wakeup.flags.enabled = 0;
|
||||
}
|
||||
adev->wakeup.flags.enabled = 1;
|
||||
} else if (adev->wakeup.flags.enabled) {
|
||||
acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
|
||||
acpi_disable_wakeup_device_power(adev);
|
||||
adev->wakeup.flags.enabled = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_pm_device_run_wake - Enable/disable remote wakeup for given device.
|
||||
* @dev: Device to enable/disable the platform to wake up.
|
||||
* acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
|
||||
* @dev: Device to enable/disable to generate wakeup events.
|
||||
* @enable: Whether to enable or disable the wakeup functionality.
|
||||
*/
|
||||
int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
||||
if (!device_run_wake(phys_dev))
|
||||
return -EINVAL;
|
||||
|
||||
adev = ACPI_COMPANION(phys_dev);
|
||||
if (!adev) {
|
||||
dev_dbg(phys_dev, "ACPI companion missing in %s!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return acpi_device_wakeup(adev, ACPI_STATE_S0, enable);
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_pm_device_run_wake);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* acpi_pm_device_sleep_wake - Enable or disable device to wake up the system.
|
||||
* @dev: Device to enable/desible to wake up the system from sleep states.
|
||||
* @enable: Whether to enable or disable @dev to wake up the system.
|
||||
*/
|
||||
int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
|
||||
int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
int error;
|
||||
|
||||
if (!device_can_wakeup(dev))
|
||||
return -EINVAL;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (!adev) {
|
||||
dev_dbg(dev, "ACPI companion missing in %s!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!acpi_device_can_wakeup(adev))
|
||||
return -EINVAL;
|
||||
|
||||
error = acpi_device_wakeup(adev, acpi_target_system_state(), enable);
|
||||
if (!error)
|
||||
dev_info(dev, "System wakeup %s by ACPI\n",
|
||||
enable ? "enabled" : "disabled");
|
||||
dev_dbg(dev, "Wakeup %s by ACPI\n", enable ? "enabled" : "disabled");
|
||||
|
||||
return error;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
EXPORT_SYMBOL(acpi_pm_set_device_wakeup);
|
||||
|
||||
/**
|
||||
* acpi_dev_pm_low_power - Put ACPI device into a low-power state.
|
||||
|
|
|
@ -1835,7 +1835,7 @@ static int acpi_ec_suspend(struct device *dev)
|
|||
struct acpi_ec *ec =
|
||||
acpi_driver_data(to_acpi_device(dev));
|
||||
|
||||
if (ec_freeze_events)
|
||||
if (acpi_sleep_no_ec_events() && ec_freeze_events)
|
||||
acpi_ec_disable_event(ec);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -198,8 +198,12 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
|
|||
Suspend/Resume
|
||||
-------------------------------------------------------------------------- */
|
||||
#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
|
||||
extern bool acpi_s2idle_wakeup(void);
|
||||
extern bool acpi_sleep_no_ec_events(void);
|
||||
extern int acpi_sleep_init(void);
|
||||
#else
|
||||
static inline bool acpi_s2idle_wakeup(void) { return false; }
|
||||
static inline bool acpi_sleep_no_ec_events(void) { return true; }
|
||||
static inline int acpi_sleep_init(void) { return -ENXIO; }
|
||||
#endif
|
||||
|
||||
|
|
|
@ -608,8 +608,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
|
|||
pcie_no_aspm();
|
||||
|
||||
pci_acpi_add_bus_pm_notifier(device);
|
||||
if (device->wakeup.flags.run_wake)
|
||||
device_set_run_wake(root->bus->bridge, true);
|
||||
device_set_wakeup_capable(root->bus->bridge, device->wakeup.flags.valid);
|
||||
|
||||
if (hotadd) {
|
||||
pcibios_resource_survey_bus(root->bus);
|
||||
|
@ -649,7 +648,7 @@ static void acpi_pci_root_remove(struct acpi_device *device)
|
|||
pci_stop_root_bus(root->bus);
|
||||
|
||||
pci_ioapic_remove(root);
|
||||
device_set_run_wake(root->bus->bridge, false);
|
||||
device_set_wakeup_capable(root->bus->bridge, false);
|
||||
pci_acpi_remove_bus_pm_notifier(device);
|
||||
|
||||
pci_remove_root_bus(root->bus);
|
||||
|
|
|
@ -42,7 +42,7 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
|
|||
|
||||
if (!dev->physical_node_count) {
|
||||
seq_printf(seq, "%c%-8s\n",
|
||||
dev->wakeup.flags.run_wake ? '*' : ' ',
|
||||
dev->wakeup.flags.valid ? '*' : ' ',
|
||||
device_may_wakeup(&dev->dev) ?
|
||||
"enabled" : "disabled");
|
||||
} else {
|
||||
|
@ -58,7 +58,7 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
|
|||
seq_printf(seq, "\t\t");
|
||||
|
||||
seq_printf(seq, "%c%-8s %s:%s\n",
|
||||
dev->wakeup.flags.run_wake ? '*' : ' ',
|
||||
dev->wakeup.flags.valid ? '*' : ' ',
|
||||
(device_may_wakeup(&dev->dev) ||
|
||||
device_may_wakeup(ldev)) ?
|
||||
"enabled" : "disabled",
|
||||
|
|
|
@ -835,7 +835,7 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
|
|||
return err;
|
||||
}
|
||||
|
||||
static void acpi_wakeup_gpe_init(struct acpi_device *device)
|
||||
static bool acpi_wakeup_gpe_init(struct acpi_device *device)
|
||||
{
|
||||
static const struct acpi_device_id button_device_ids[] = {
|
||||
{"PNP0C0C", 0},
|
||||
|
@ -845,13 +845,11 @@ static void acpi_wakeup_gpe_init(struct acpi_device *device)
|
|||
};
|
||||
struct acpi_device_wakeup *wakeup = &device->wakeup;
|
||||
acpi_status status;
|
||||
acpi_event_status event_status;
|
||||
|
||||
wakeup->flags.notifier_present = 0;
|
||||
|
||||
/* Power button, Lid switch always enable wakeup */
|
||||
if (!acpi_match_device_ids(device, button_device_ids)) {
|
||||
wakeup->flags.run_wake = 1;
|
||||
if (!acpi_match_device_ids(device, &button_device_ids[1])) {
|
||||
/* Do not use Lid/sleep button for S5 wakeup */
|
||||
if (wakeup->sleep_state == ACPI_STATE_S5)
|
||||
|
@ -859,17 +857,12 @@ static void acpi_wakeup_gpe_init(struct acpi_device *device)
|
|||
}
|
||||
acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number);
|
||||
device_set_wakeup_capable(&device->dev, true);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
acpi_setup_gpe_for_wake(device->handle, wakeup->gpe_device,
|
||||
wakeup->gpe_number);
|
||||
status = acpi_get_gpe_status(wakeup->gpe_device, wakeup->gpe_number,
|
||||
&event_status);
|
||||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
wakeup->flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HAS_HANDLER);
|
||||
status = acpi_setup_gpe_for_wake(device->handle, wakeup->gpe_device,
|
||||
wakeup->gpe_number);
|
||||
return ACPI_SUCCESS(status);
|
||||
}
|
||||
|
||||
static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
|
||||
|
@ -887,10 +880,10 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
|
|||
return;
|
||||
}
|
||||
|
||||
device->wakeup.flags.valid = 1;
|
||||
device->wakeup.flags.valid = acpi_wakeup_gpe_init(device);
|
||||
device->wakeup.prepare_count = 0;
|
||||
acpi_wakeup_gpe_init(device);
|
||||
/* Call _PSW/_DSW object to disable its ability to wake the sleeping
|
||||
/*
|
||||
* Call _PSW/_DSW object to disable its ability to wake the sleeping
|
||||
* system for the ACPI device with the _PRW object.
|
||||
* The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW.
|
||||
* So it is necessary to call _DSW object first. Only when it is not
|
||||
|
|
|
@ -650,38 +650,165 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
|
|||
.recover = acpi_pm_finish,
|
||||
};
|
||||
|
||||
static bool s2idle_in_progress;
|
||||
static bool s2idle_wakeup;
|
||||
|
||||
/*
|
||||
* On platforms supporting the Low Power S0 Idle interface there is an ACPI
|
||||
* device object with the PNP0D80 compatible device ID (System Power Management
|
||||
* Controller) and a specific _DSM method under it. That method, if present,
|
||||
* can be used to indicate to the platform that the OS is transitioning into a
|
||||
* low-power state in which certain types of activity are not desirable or that
|
||||
* it is leaving such a state, which allows the platform to adjust its operation
|
||||
* mode accordingly.
|
||||
*/
|
||||
static const struct acpi_device_id lps0_device_ids[] = {
|
||||
{"PNP0D80", },
|
||||
{"", },
|
||||
};
|
||||
|
||||
#define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
|
||||
|
||||
#define ACPI_LPS0_SCREEN_OFF 3
|
||||
#define ACPI_LPS0_SCREEN_ON 4
|
||||
#define ACPI_LPS0_ENTRY 5
|
||||
#define ACPI_LPS0_EXIT 6
|
||||
|
||||
#define ACPI_S2IDLE_FUNC_MASK ((1 << ACPI_LPS0_ENTRY) | (1 << ACPI_LPS0_EXIT))
|
||||
|
||||
static acpi_handle lps0_device_handle;
|
||||
static guid_t lps0_dsm_guid;
|
||||
static char lps0_dsm_func_mask;
|
||||
|
||||
static void acpi_sleep_run_lps0_dsm(unsigned int func)
|
||||
{
|
||||
union acpi_object *out_obj;
|
||||
|
||||
if (!(lps0_dsm_func_mask & (1 << func)))
|
||||
return;
|
||||
|
||||
out_obj = acpi_evaluate_dsm(lps0_device_handle, &lps0_dsm_guid, 1, func, NULL);
|
||||
ACPI_FREE(out_obj);
|
||||
|
||||
acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n",
|
||||
func, out_obj ? "successful" : "failed");
|
||||
}
|
||||
|
||||
static int lps0_device_attach(struct acpi_device *adev,
|
||||
const struct acpi_device_id *not_used)
|
||||
{
|
||||
union acpi_object *out_obj;
|
||||
|
||||
if (lps0_device_handle)
|
||||
return 0;
|
||||
|
||||
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
|
||||
return 0;
|
||||
|
||||
guid_parse(ACPI_LPS0_DSM_UUID, &lps0_dsm_guid);
|
||||
/* Check if the _DSM is present and as expected. */
|
||||
out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 1, 0, NULL);
|
||||
if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
|
||||
char bitmask = *(char *)out_obj->buffer.pointer;
|
||||
|
||||
if ((bitmask & ACPI_S2IDLE_FUNC_MASK) == ACPI_S2IDLE_FUNC_MASK) {
|
||||
lps0_dsm_func_mask = bitmask;
|
||||
lps0_device_handle = adev->handle;
|
||||
}
|
||||
|
||||
acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n",
|
||||
bitmask);
|
||||
} else {
|
||||
acpi_handle_debug(adev->handle,
|
||||
"_DSM function 0 evaluation failed\n");
|
||||
}
|
||||
ACPI_FREE(out_obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_scan_handler lps0_handler = {
|
||||
.ids = lps0_device_ids,
|
||||
.attach = lps0_device_attach,
|
||||
};
|
||||
|
||||
static int acpi_freeze_begin(void)
|
||||
{
|
||||
acpi_scan_lock_acquire();
|
||||
s2idle_in_progress = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_freeze_prepare(void)
|
||||
{
|
||||
acpi_enable_wakeup_devices(ACPI_STATE_S0);
|
||||
acpi_enable_all_wakeup_gpes();
|
||||
acpi_os_wait_events_complete();
|
||||
if (lps0_device_handle) {
|
||||
acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
|
||||
acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
|
||||
} else {
|
||||
/*
|
||||
* The configuration of GPEs is changed here to avoid spurious
|
||||
* wakeups, but that should not be necessary if this is a
|
||||
* "low-power S0" platform and the low-power S0 _DSM is present.
|
||||
*/
|
||||
acpi_enable_all_wakeup_gpes();
|
||||
acpi_os_wait_events_complete();
|
||||
}
|
||||
if (acpi_sci_irq_valid())
|
||||
enable_irq_wake(acpi_sci_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_freeze_wake(void)
|
||||
{
|
||||
/*
|
||||
* If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
|
||||
* that the SCI has triggered while suspended, so cancel the wakeup in
|
||||
* case it has not been a wakeup event (the GPEs will be checked later).
|
||||
*/
|
||||
if (acpi_sci_irq_valid() &&
|
||||
!irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
|
||||
pm_system_cancel_wakeup();
|
||||
s2idle_wakeup = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_freeze_sync(void)
|
||||
{
|
||||
/*
|
||||
* Process all pending events in case there are any wakeup ones.
|
||||
*
|
||||
* The EC driver uses the system workqueue, so that one needs to be
|
||||
* flushed too.
|
||||
*/
|
||||
acpi_os_wait_events_complete();
|
||||
flush_scheduled_work();
|
||||
s2idle_wakeup = false;
|
||||
}
|
||||
|
||||
static void acpi_freeze_restore(void)
|
||||
{
|
||||
acpi_disable_wakeup_devices(ACPI_STATE_S0);
|
||||
if (acpi_sci_irq_valid())
|
||||
disable_irq_wake(acpi_sci_irq);
|
||||
acpi_enable_all_runtime_gpes();
|
||||
|
||||
if (lps0_device_handle) {
|
||||
acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
|
||||
acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
|
||||
} else {
|
||||
acpi_enable_all_runtime_gpes();
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_freeze_end(void)
|
||||
{
|
||||
s2idle_in_progress = false;
|
||||
acpi_scan_lock_release();
|
||||
}
|
||||
|
||||
static const struct platform_freeze_ops acpi_freeze_ops = {
|
||||
.begin = acpi_freeze_begin,
|
||||
.prepare = acpi_freeze_prepare,
|
||||
.wake = acpi_freeze_wake,
|
||||
.sync = acpi_freeze_sync,
|
||||
.restore = acpi_freeze_restore,
|
||||
.end = acpi_freeze_end,
|
||||
};
|
||||
|
@ -696,13 +823,28 @@ static void acpi_sleep_suspend_setup(void)
|
|||
|
||||
suspend_set_ops(old_suspend_ordering ?
|
||||
&acpi_suspend_ops_old : &acpi_suspend_ops);
|
||||
|
||||
acpi_scan_add_handler(&lps0_handler);
|
||||
freeze_set_ops(&acpi_freeze_ops);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_SUSPEND */
|
||||
#define s2idle_in_progress (false)
|
||||
#define s2idle_wakeup (false)
|
||||
#define lps0_device_handle (NULL)
|
||||
static inline void acpi_sleep_suspend_setup(void) {}
|
||||
#endif /* !CONFIG_SUSPEND */
|
||||
|
||||
bool acpi_s2idle_wakeup(void)
|
||||
{
|
||||
return s2idle_wakeup;
|
||||
}
|
||||
|
||||
bool acpi_sleep_no_ec_events(void)
|
||||
{
|
||||
return !s2idle_in_progress || !lps0_device_handle;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static u32 saved_bm_rld;
|
||||
|
||||
|
|
|
@ -174,8 +174,7 @@ void zpodd_enable_run_wake(struct ata_device *dev)
|
|||
sdev_disable_disk_events(dev->sdev);
|
||||
|
||||
zpodd->powered_off = true;
|
||||
device_set_run_wake(&dev->tdev, true);
|
||||
acpi_pm_device_run_wake(&dev->tdev, true);
|
||||
acpi_pm_set_device_wakeup(&dev->tdev, true);
|
||||
}
|
||||
|
||||
/* Disable runtime wake capability if it is enabled */
|
||||
|
@ -183,10 +182,8 @@ void zpodd_disable_run_wake(struct ata_device *dev)
|
|||
{
|
||||
struct zpodd *zpodd = dev->zpodd;
|
||||
|
||||
if (zpodd->powered_off) {
|
||||
acpi_pm_device_run_wake(&dev->tdev, false);
|
||||
device_set_run_wake(&dev->tdev, false);
|
||||
}
|
||||
if (zpodd->powered_off)
|
||||
acpi_pm_set_device_wakeup(&dev->tdev, false);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -418,6 +418,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, const char *info,
|
|||
dev_name(dev), pm_verb(state.event), info, error);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEBUG
|
||||
static void dpm_show_time(ktime_t starttime, pm_message_t state,
|
||||
const char *info)
|
||||
{
|
||||
|
@ -435,6 +436,10 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state,
|
|||
info ?: "", info ? " " : "", pm_verb(state.event),
|
||||
usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
|
||||
}
|
||||
#else
|
||||
static inline void dpm_show_time(ktime_t starttime, pm_message_t state,
|
||||
const char *info) {}
|
||||
#endif /* CONFIG_PM_DEBUG */
|
||||
|
||||
static int dpm_run_callback(pm_callback_t cb, struct device *dev,
|
||||
pm_message_t state, const char *info)
|
||||
|
@ -1093,11 +1098,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
|
|||
if (async_error)
|
||||
goto Complete;
|
||||
|
||||
if (pm_wakeup_pending()) {
|
||||
async_error = -EBUSY;
|
||||
goto Complete;
|
||||
}
|
||||
|
||||
if (dev->power.syscore || dev->power.direct_complete)
|
||||
goto Complete;
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ bool events_check_enabled __read_mostly;
|
|||
/* First wakeup IRQ seen by the kernel in the last cycle. */
|
||||
unsigned int pm_wakeup_irq __read_mostly;
|
||||
|
||||
/* If set and the system is suspending, terminate the suspend. */
|
||||
static bool pm_abort_suspend __read_mostly;
|
||||
/* If greater than 0 and the system is suspending, terminate the suspend. */
|
||||
static atomic_t pm_abort_suspend __read_mostly;
|
||||
|
||||
/*
|
||||
* Combined counters of registered wakeup events and wakeup events in progress.
|
||||
|
@ -857,20 +857,26 @@ bool pm_wakeup_pending(void)
|
|||
pm_print_active_wakeup_sources();
|
||||
}
|
||||
|
||||
return ret || pm_abort_suspend;
|
||||
return ret || atomic_read(&pm_abort_suspend) > 0;
|
||||
}
|
||||
|
||||
void pm_system_wakeup(void)
|
||||
{
|
||||
pm_abort_suspend = true;
|
||||
atomic_inc(&pm_abort_suspend);
|
||||
freeze_wake();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_system_wakeup);
|
||||
|
||||
void pm_wakeup_clear(void)
|
||||
void pm_system_cancel_wakeup(void)
|
||||
{
|
||||
atomic_dec(&pm_abort_suspend);
|
||||
}
|
||||
|
||||
void pm_wakeup_clear(bool reset)
|
||||
{
|
||||
pm_abort_suspend = false;
|
||||
pm_wakeup_irq = 0;
|
||||
if (reset)
|
||||
atomic_set(&pm_abort_suspend, 0);
|
||||
}
|
||||
|
||||
void pm_system_irq_wakeup(unsigned int irq_number)
|
||||
|
|
|
@ -394,29 +394,26 @@ bool pciehp_is_native(struct pci_dev *pdev)
|
|||
|
||||
/**
|
||||
* pci_acpi_wake_bus - Root bus wakeup notification fork function.
|
||||
* @work: Work item to handle.
|
||||
* @context: Device wakeup context.
|
||||
*/
|
||||
static void pci_acpi_wake_bus(struct work_struct *work)
|
||||
static void pci_acpi_wake_bus(struct acpi_device_wakeup_context *context)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
struct acpi_pci_root *root;
|
||||
|
||||
adev = container_of(work, struct acpi_device, wakeup.context.work);
|
||||
adev = container_of(context, struct acpi_device, wakeup.context);
|
||||
root = acpi_driver_data(adev);
|
||||
pci_pme_wakeup_bus(root->bus);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_acpi_wake_dev - PCI device wakeup notification work function.
|
||||
* @handle: ACPI handle of a device the notification is for.
|
||||
* @work: Work item to handle.
|
||||
* @context: Device wakeup context.
|
||||
*/
|
||||
static void pci_acpi_wake_dev(struct work_struct *work)
|
||||
static void pci_acpi_wake_dev(struct acpi_device_wakeup_context *context)
|
||||
{
|
||||
struct acpi_device_wakeup_context *context;
|
||||
struct pci_dev *pci_dev;
|
||||
|
||||
context = container_of(work, struct acpi_device_wakeup_context, work);
|
||||
pci_dev = to_pci_dev(context->dev);
|
||||
|
||||
if (pci_dev->pme_poll)
|
||||
|
@ -424,7 +421,7 @@ static void pci_acpi_wake_dev(struct work_struct *work)
|
|||
|
||||
if (pci_dev->current_state == PCI_D3cold) {
|
||||
pci_wakeup_event(pci_dev);
|
||||
pm_runtime_resume(&pci_dev->dev);
|
||||
pm_request_resume(&pci_dev->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -433,7 +430,7 @@ static void pci_acpi_wake_dev(struct work_struct *work)
|
|||
pci_check_pme_status(pci_dev);
|
||||
|
||||
pci_wakeup_event(pci_dev);
|
||||
pm_runtime_resume(&pci_dev->dev);
|
||||
pm_request_resume(&pci_dev->dev);
|
||||
|
||||
pci_pme_wakeup_bus(pci_dev->subordinate);
|
||||
}
|
||||
|
@ -572,67 +569,29 @@ static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev)
|
|||
return state_conv[state];
|
||||
}
|
||||
|
||||
static bool acpi_pci_can_wakeup(struct pci_dev *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
|
||||
return adev ? acpi_device_can_wakeup(adev) : false;
|
||||
}
|
||||
|
||||
static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
|
||||
static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
|
||||
{
|
||||
while (bus->parent) {
|
||||
if (!acpi_pm_device_sleep_wake(&bus->self->dev, enable))
|
||||
return;
|
||||
if (acpi_pm_device_can_wakeup(&bus->self->dev))
|
||||
return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
|
||||
|
||||
bus = bus->parent;
|
||||
}
|
||||
|
||||
/* We have reached the root bus. */
|
||||
if (bus->bridge)
|
||||
acpi_pm_device_sleep_wake(bus->bridge, enable);
|
||||
}
|
||||
|
||||
static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
if (acpi_pci_can_wakeup(dev))
|
||||
return acpi_pm_device_sleep_wake(&dev->dev, enable);
|
||||
|
||||
acpi_pci_propagate_wakeup_enable(dev->bus, enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
|
||||
{
|
||||
while (bus->parent) {
|
||||
struct pci_dev *bridge = bus->self;
|
||||
|
||||
if (bridge->pme_interrupt)
|
||||
return;
|
||||
if (!acpi_pm_device_run_wake(&bridge->dev, enable))
|
||||
return;
|
||||
bus = bus->parent;
|
||||
if (bus->bridge) {
|
||||
if (acpi_pm_device_can_wakeup(bus->bridge))
|
||||
return acpi_pm_set_device_wakeup(bus->bridge, enable);
|
||||
}
|
||||
|
||||
/* We have reached the root bus. */
|
||||
if (bus->bridge)
|
||||
acpi_pm_device_run_wake(bus->bridge, enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
|
||||
static int acpi_pci_wakeup(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
/*
|
||||
* Per PCI Express Base Specification Revision 2.0 section
|
||||
* 5.3.3.2 Link Wakeup, platform support is needed for D3cold
|
||||
* waking up to power on the main link even if there is PME
|
||||
* support for D3cold
|
||||
*/
|
||||
if (dev->pme_interrupt && !dev->runtime_d3cold)
|
||||
return 0;
|
||||
if (acpi_pm_device_can_wakeup(&dev->dev))
|
||||
return acpi_pm_set_device_wakeup(&dev->dev, enable);
|
||||
|
||||
if (!acpi_pm_device_run_wake(&dev->dev, enable))
|
||||
return 0;
|
||||
|
||||
acpi_pci_propagate_run_wake(dev->bus, enable);
|
||||
return 0;
|
||||
return acpi_pci_propagate_wakeup(dev->bus, enable);
|
||||
}
|
||||
|
||||
static bool acpi_pci_need_resume(struct pci_dev *dev)
|
||||
|
@ -656,8 +615,7 @@ static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
|
|||
.set_state = acpi_pci_set_power_state,
|
||||
.get_state = acpi_pci_get_power_state,
|
||||
.choose_state = acpi_pci_choose_state,
|
||||
.sleep_wake = acpi_pci_sleep_wake,
|
||||
.run_wake = acpi_pci_run_wake,
|
||||
.set_wakeup = acpi_pci_wakeup,
|
||||
.need_resume = acpi_pci_need_resume,
|
||||
};
|
||||
|
||||
|
@ -780,9 +738,7 @@ static void pci_acpi_setup(struct device *dev)
|
|||
return;
|
||||
|
||||
device_set_wakeup_capable(dev, true);
|
||||
acpi_pci_sleep_wake(pci_dev, false);
|
||||
if (adev->wakeup.flags.run_wake)
|
||||
device_set_run_wake(dev, true);
|
||||
acpi_pci_wakeup(pci_dev, false);
|
||||
}
|
||||
|
||||
static void pci_acpi_cleanup(struct device *dev)
|
||||
|
@ -793,10 +749,8 @@ static void pci_acpi_cleanup(struct device *dev)
|
|||
return;
|
||||
|
||||
pci_acpi_remove_pm_notifier(adev);
|
||||
if (adev->wakeup.flags.valid) {
|
||||
if (adev->wakeup.flags.valid)
|
||||
device_set_wakeup_capable(dev, false);
|
||||
device_set_run_wake(dev, false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool pci_acpi_bus_match(struct device *dev)
|
||||
|
|
|
@ -1216,7 +1216,7 @@ static int pci_pm_runtime_resume(struct device *dev)
|
|||
|
||||
pci_restore_standard_config(pci_dev);
|
||||
pci_fixup_device(pci_fixup_resume_early, pci_dev);
|
||||
__pci_enable_wake(pci_dev, PCI_D0, true, false);
|
||||
pci_enable_wake(pci_dev, PCI_D0, false);
|
||||
pci_fixup_device(pci_fixup_resume, pci_dev);
|
||||
|
||||
rc = pm->runtime_resume(dev);
|
||||
|
|
|
@ -39,12 +39,7 @@ static pci_power_t mid_pci_choose_state(struct pci_dev *pdev)
|
|||
return PCI_D3hot;
|
||||
}
|
||||
|
||||
static int mid_pci_sleep_wake(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mid_pci_run_wake(struct pci_dev *dev, bool enable)
|
||||
static int mid_pci_wakeup(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -59,8 +54,7 @@ static const struct pci_platform_pm_ops mid_pci_platform_pm = {
|
|||
.set_state = mid_pci_set_power_state,
|
||||
.get_state = mid_pci_get_power_state,
|
||||
.choose_state = mid_pci_choose_state,
|
||||
.sleep_wake = mid_pci_sleep_wake,
|
||||
.run_wake = mid_pci_run_wake,
|
||||
.set_wakeup = mid_pci_wakeup,
|
||||
.need_resume = mid_pci_need_resume,
|
||||
};
|
||||
|
||||
|
|
|
@ -574,8 +574,7 @@ static const struct pci_platform_pm_ops *pci_platform_pm;
|
|||
int pci_set_platform_pm(const struct pci_platform_pm_ops *ops)
|
||||
{
|
||||
if (!ops->is_manageable || !ops->set_state || !ops->get_state ||
|
||||
!ops->choose_state || !ops->sleep_wake || !ops->run_wake ||
|
||||
!ops->need_resume)
|
||||
!ops->choose_state || !ops->set_wakeup || !ops->need_resume)
|
||||
return -EINVAL;
|
||||
pci_platform_pm = ops;
|
||||
return 0;
|
||||
|
@ -603,16 +602,10 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
|
|||
pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR;
|
||||
}
|
||||
|
||||
static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
|
||||
static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
return pci_platform_pm ?
|
||||
pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
|
||||
}
|
||||
|
||||
static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable)
|
||||
{
|
||||
return pci_platform_pm ?
|
||||
pci_platform_pm->run_wake(dev, enable) : -ENODEV;
|
||||
pci_platform_pm->set_wakeup(dev, enable) : -ENODEV;
|
||||
}
|
||||
|
||||
static inline bool platform_pci_need_resume(struct pci_dev *dev)
|
||||
|
@ -1805,6 +1798,23 @@ static void __pci_pme_active(struct pci_dev *dev, bool enable)
|
|||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
|
||||
}
|
||||
|
||||
static void pci_pme_restore(struct pci_dev *dev)
|
||||
{
|
||||
u16 pmcsr;
|
||||
|
||||
if (!dev->pme_support)
|
||||
return;
|
||||
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
if (dev->wakeup_prepared) {
|
||||
pmcsr |= PCI_PM_CTRL_PME_ENABLE;
|
||||
} else {
|
||||
pmcsr &= ~PCI_PM_CTRL_PME_ENABLE;
|
||||
pmcsr |= PCI_PM_CTRL_PME_STATUS;
|
||||
}
|
||||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_pme_active - enable or disable PCI device's PME# function
|
||||
* @dev: PCI device to handle.
|
||||
|
@ -1872,10 +1882,9 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
|
|||
EXPORT_SYMBOL(pci_pme_active);
|
||||
|
||||
/**
|
||||
* __pci_enable_wake - enable PCI device as wakeup event source
|
||||
* pci_enable_wake - enable PCI device as wakeup event source
|
||||
* @dev: PCI device affected
|
||||
* @state: PCI state from which device will issue wakeup events
|
||||
* @runtime: True if the events are to be generated at run time
|
||||
* @enable: True to enable event generation; false to disable
|
||||
*
|
||||
* This enables the device as a wakeup event source, or disables it.
|
||||
|
@ -1891,17 +1900,18 @@ EXPORT_SYMBOL(pci_pme_active);
|
|||
* Error code depending on the platform is returned if both the platform and
|
||||
* the native mechanism fail to enable the generation of wake-up events
|
||||
*/
|
||||
int __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
|
||||
bool runtime, bool enable)
|
||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (enable && !runtime && !device_may_wakeup(&dev->dev))
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't do the same thing twice in a row for one device. */
|
||||
if (!!enable == !!dev->wakeup_prepared)
|
||||
/*
|
||||
* Don't do the same thing twice in a row for one device, but restore
|
||||
* PME Enable in case it has been updated by config space restoration.
|
||||
*/
|
||||
if (!!enable == !!dev->wakeup_prepared) {
|
||||
pci_pme_restore(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to "PCI System Architecture" 4th ed. by Tom Shanley & Don
|
||||
|
@ -1916,24 +1926,20 @@ int __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
|
|||
pci_pme_active(dev, true);
|
||||
else
|
||||
ret = 1;
|
||||
error = runtime ? platform_pci_run_wake(dev, true) :
|
||||
platform_pci_sleep_wake(dev, true);
|
||||
error = platform_pci_set_wakeup(dev, true);
|
||||
if (ret)
|
||||
ret = error;
|
||||
if (!ret)
|
||||
dev->wakeup_prepared = true;
|
||||
} else {
|
||||
if (runtime)
|
||||
platform_pci_run_wake(dev, false);
|
||||
else
|
||||
platform_pci_sleep_wake(dev, false);
|
||||
platform_pci_set_wakeup(dev, false);
|
||||
pci_pme_active(dev, false);
|
||||
dev->wakeup_prepared = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(__pci_enable_wake);
|
||||
EXPORT_SYMBOL(pci_enable_wake);
|
||||
|
||||
/**
|
||||
* pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
|
||||
|
@ -2075,12 +2081,12 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
|
|||
|
||||
dev->runtime_d3cold = target_state == PCI_D3cold;
|
||||
|
||||
__pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev));
|
||||
pci_enable_wake(dev, target_state, pci_dev_run_wake(dev));
|
||||
|
||||
error = pci_set_power_state(dev, target_state);
|
||||
|
||||
if (error) {
|
||||
__pci_enable_wake(dev, target_state, true, false);
|
||||
pci_enable_wake(dev, target_state, false);
|
||||
dev->runtime_d3cold = false;
|
||||
}
|
||||
|
||||
|
@ -2099,7 +2105,7 @@ bool pci_dev_run_wake(struct pci_dev *dev)
|
|||
{
|
||||
struct pci_bus *bus = dev->bus;
|
||||
|
||||
if (device_run_wake(&dev->dev))
|
||||
if (device_can_wakeup(&dev->dev))
|
||||
return true;
|
||||
|
||||
if (!dev->pme_support)
|
||||
|
@ -2112,7 +2118,7 @@ bool pci_dev_run_wake(struct pci_dev *dev)
|
|||
while (bus->parent) {
|
||||
struct pci_dev *bridge = bus->self;
|
||||
|
||||
if (device_run_wake(&bridge->dev))
|
||||
if (device_can_wakeup(&bridge->dev))
|
||||
return true;
|
||||
|
||||
bus = bus->parent;
|
||||
|
@ -2120,7 +2126,7 @@ bool pci_dev_run_wake(struct pci_dev *dev)
|
|||
|
||||
/* We have reached the root bus. */
|
||||
if (bus->bridge)
|
||||
return device_run_wake(bus->bridge);
|
||||
return device_can_wakeup(bus->bridge);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -47,11 +47,7 @@ int pci_probe_reset_function(struct pci_dev *dev);
|
|||
* platform; to be used during system-wide transitions from a
|
||||
* sleeping state to the working state and vice versa
|
||||
*
|
||||
* @sleep_wake: enables/disables the system wake up capability of given device
|
||||
*
|
||||
* @run_wake: enables/disables the platform to generate run-time wake-up events
|
||||
* for given device (the device's wake-up capability has to be
|
||||
* enabled by @sleep_wake for this feature to work)
|
||||
* @set_wakeup: enables/disables wakeup capability for the device
|
||||
*
|
||||
* @need_resume: returns 'true' if the given device (which is currently
|
||||
* suspended) needs to be resumed to be configured for system
|
||||
|
@ -65,8 +61,7 @@ struct pci_platform_pm_ops {
|
|||
int (*set_state)(struct pci_dev *dev, pci_power_t state);
|
||||
pci_power_t (*get_state)(struct pci_dev *dev);
|
||||
pci_power_t (*choose_state)(struct pci_dev *dev);
|
||||
int (*sleep_wake)(struct pci_dev *dev, bool enable);
|
||||
int (*run_wake)(struct pci_dev *dev, bool enable);
|
||||
int (*set_wakeup)(struct pci_dev *dev, bool enable);
|
||||
bool (*need_resume)(struct pci_dev *dev);
|
||||
};
|
||||
|
||||
|
|
|
@ -294,31 +294,29 @@ static irqreturn_t pcie_pme_irq(int irq, void *context)
|
|||
}
|
||||
|
||||
/**
|
||||
* pcie_pme_set_native - Set the PME interrupt flag for given device.
|
||||
* pcie_pme_can_wakeup - Set the wakeup capability flag.
|
||||
* @dev: PCI device to handle.
|
||||
* @ign: Ignored.
|
||||
*/
|
||||
static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
|
||||
static int pcie_pme_can_wakeup(struct pci_dev *dev, void *ign)
|
||||
{
|
||||
device_set_run_wake(&dev->dev, true);
|
||||
dev->pme_interrupt = true;
|
||||
device_set_wakeup_capable(&dev->dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
|
||||
* pcie_pme_mark_devices - Set the wakeup flag for devices below a port.
|
||||
* @port: PCIe root port or event collector to handle.
|
||||
*
|
||||
* For each device below given root port, including the port itself (or for each
|
||||
* root complex integrated endpoint if @port is a root complex event collector)
|
||||
* set the flag indicating that it can signal run-time wake-up events via PCIe
|
||||
* PME interrupts.
|
||||
* set the flag indicating that it can signal run-time wake-up events.
|
||||
*/
|
||||
static void pcie_pme_mark_devices(struct pci_dev *port)
|
||||
{
|
||||
pcie_pme_set_native(port, NULL);
|
||||
pcie_pme_can_wakeup(port, NULL);
|
||||
if (port->subordinate)
|
||||
pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
|
||||
pci_walk_bus(port->subordinate, pcie_pme_can_wakeup, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -794,6 +794,25 @@ config INTEL_CHT_INT33FE
|
|||
This driver instantiates i2c-clients for these, so that standard
|
||||
i2c drivers for these chips can bind to the them.
|
||||
|
||||
config INTEL_INT0002_VGPIO
|
||||
tristate "Intel ACPI INT0002 Virtual GPIO driver"
|
||||
depends on GPIOLIB && ACPI
|
||||
select GPIOLIB_IRQCHIP
|
||||
---help---
|
||||
Some peripherals on Bay Trail and Cherry Trail platforms signal a
|
||||
Power Management Event (PME) to the Power Management Controller (PMC)
|
||||
to wakeup the system. When this happens software needs to explicitly
|
||||
clear the PME bus 0 status bit in the GPE0a_STS register to avoid an
|
||||
IRQ storm on IRQ 9.
|
||||
|
||||
This is modelled in ACPI through the INT0002 ACPI device, which is
|
||||
called a "Virtual GPIO controller" in ACPI because it defines the
|
||||
event handler to call when the PME triggers through _AEI and _L02
|
||||
methods as would be done for a real GPIO interrupt in ACPI.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel_int0002_vgpio.
|
||||
|
||||
config INTEL_HID_EVENT
|
||||
tristate "INTEL HID Event"
|
||||
depends on ACPI
|
||||
|
|
|
@ -46,6 +46,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
|||
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
|
||||
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
|
||||
obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
|
||||
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
|
||||
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
|
||||
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -75,6 +76,7 @@ static const struct key_entry intel_array_keymap[] = {
|
|||
struct intel_hid_priv {
|
||||
struct input_dev *input_dev;
|
||||
struct input_dev *array;
|
||||
bool wakeup_mode;
|
||||
};
|
||||
|
||||
static int intel_hid_set_enable(struct device *device, bool enable)
|
||||
|
@ -116,23 +118,37 @@ static void intel_button_array_enable(struct device *device, bool enable)
|
|||
dev_warn(device, "failed to set button capability\n");
|
||||
}
|
||||
|
||||
static int intel_hid_pm_prepare(struct device *device)
|
||||
{
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(device);
|
||||
|
||||
priv->wakeup_mode = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hid_pl_suspend_handler(struct device *device)
|
||||
{
|
||||
intel_hid_set_enable(device, false);
|
||||
intel_button_array_enable(device, false);
|
||||
|
||||
if (pm_suspend_via_firmware()) {
|
||||
intel_hid_set_enable(device, false);
|
||||
intel_button_array_enable(device, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hid_pl_resume_handler(struct device *device)
|
||||
{
|
||||
intel_hid_set_enable(device, true);
|
||||
intel_button_array_enable(device, true);
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(device);
|
||||
|
||||
priv->wakeup_mode = false;
|
||||
if (pm_resume_via_firmware()) {
|
||||
intel_hid_set_enable(device, true);
|
||||
intel_button_array_enable(device, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops intel_hid_pl_pm_ops = {
|
||||
.prepare = intel_hid_pm_prepare,
|
||||
.freeze = intel_hid_pl_suspend_handler,
|
||||
.thaw = intel_hid_pl_resume_handler,
|
||||
.restore = intel_hid_pl_resume_handler,
|
||||
|
@ -186,6 +202,19 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
|
|||
unsigned long long ev_index;
|
||||
acpi_status status;
|
||||
|
||||
if (priv->wakeup_mode) {
|
||||
/* Wake up on 5-button array events only. */
|
||||
if (event == 0xc0 || !priv->array)
|
||||
return;
|
||||
|
||||
if (sparse_keymap_entry_from_scancode(priv->array, event))
|
||||
pm_wakeup_hard_event(&device->dev);
|
||||
else
|
||||
dev_info(&device->dev, "unknown event 0x%x\n", event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* 0xC0 is for HID events, other values are for 5 button array */
|
||||
if (event != 0xc0) {
|
||||
if (!priv->array ||
|
||||
|
@ -270,6 +299,7 @@ static int intel_hid_probe(struct platform_device *device)
|
|||
"failed to enable HID power button\n");
|
||||
}
|
||||
|
||||
device_init_wakeup(&device->dev, true);
|
||||
return 0;
|
||||
|
||||
err_remove_notify:
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -46,6 +47,7 @@ static const struct key_entry intel_vbtn_keymap[] = {
|
|||
|
||||
struct intel_vbtn_priv {
|
||||
struct input_dev *input_dev;
|
||||
bool wakeup_mode;
|
||||
};
|
||||
|
||||
static int intel_vbtn_input_setup(struct platform_device *device)
|
||||
|
@ -73,9 +75,15 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
|
|||
struct platform_device *device = context;
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
|
||||
if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
|
||||
dev_info(&device->dev, "unknown event index 0x%x\n",
|
||||
event);
|
||||
if (priv->wakeup_mode) {
|
||||
if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) {
|
||||
pm_wakeup_hard_event(&device->dev);
|
||||
return;
|
||||
}
|
||||
} else if (sparse_keymap_report_event(priv->input_dev, event, 1, true)) {
|
||||
return;
|
||||
}
|
||||
dev_info(&device->dev, "unknown event index 0x%x\n", event);
|
||||
}
|
||||
|
||||
static int intel_vbtn_probe(struct platform_device *device)
|
||||
|
@ -109,6 +117,7 @@ static int intel_vbtn_probe(struct platform_device *device)
|
|||
if (ACPI_FAILURE(status))
|
||||
return -EBUSY;
|
||||
|
||||
device_init_wakeup(&device->dev, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -125,10 +134,34 @@ static int intel_vbtn_remove(struct platform_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int intel_vbtn_pm_prepare(struct device *dev)
|
||||
{
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
priv->wakeup_mode = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_vbtn_pm_resume(struct device *dev)
|
||||
{
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
priv->wakeup_mode = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops intel_vbtn_pm_ops = {
|
||||
.prepare = intel_vbtn_pm_prepare,
|
||||
.resume = intel_vbtn_pm_resume,
|
||||
.restore = intel_vbtn_pm_resume,
|
||||
.thaw = intel_vbtn_pm_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver intel_vbtn_pl_driver = {
|
||||
.driver = {
|
||||
.name = "intel-vbtn",
|
||||
.acpi_match_table = intel_vbtn_ids,
|
||||
.pm = &intel_vbtn_pm_ops,
|
||||
},
|
||||
.probe = intel_vbtn_probe,
|
||||
.remove = intel_vbtn_remove,
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Intel INT0002 "Virtual GPIO" driver
|
||||
*
|
||||
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* Loosely based on android x86 kernel code which is:
|
||||
*
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* Author: Dyut Kumar Sil <dyut.k.sil@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Some peripherals on Bay Trail and Cherry Trail platforms signal a Power
|
||||
* Management Event (PME) to the Power Management Controller (PMC) to wakeup
|
||||
* the system. When this happens software needs to clear the PME bus 0 status
|
||||
* bit in the GPE0a_STS register to avoid an IRQ storm on IRQ 9.
|
||||
*
|
||||
* This is modelled in ACPI through the INT0002 ACPI device, which is
|
||||
* called a "Virtual GPIO controller" in ACPI because it defines the event
|
||||
* handler to call when the PME triggers through _AEI and _L02 / _E02
|
||||
* methods as would be done for a real GPIO interrupt in ACPI. Note this
|
||||
* is a hack to define an AML event handler for the PME while using existing
|
||||
* ACPI mechanisms, this is not a real GPIO at all.
|
||||
*
|
||||
* This driver will bind to the INT0002 device, and register as a GPIO
|
||||
* controller, letting gpiolib-acpi.c call the _L02 handler as it would
|
||||
* for a real GPIO controller.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
|
||||
#define DRV_NAME "INT0002 Virtual GPIO"
|
||||
|
||||
/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */
|
||||
#define GPE0A_PME_B0_VIRT_GPIO_PIN 2
|
||||
|
||||
#define GPE0A_PME_B0_STS_BIT BIT(13)
|
||||
#define GPE0A_PME_B0_EN_BIT BIT(13)
|
||||
#define GPE0A_STS_PORT 0x420
|
||||
#define GPE0A_EN_PORT 0x428
|
||||
|
||||
#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
|
||||
|
||||
static const struct x86_cpu_id int0002_cpu_ids[] = {
|
||||
/*
|
||||
* Limit ourselves to Cherry Trail for now, until testing shows we
|
||||
* need to handle the INT0002 device on Baytrail too.
|
||||
* ICPU(INTEL_FAM6_ATOM_SILVERMONT1), * Valleyview, Bay Trail *
|
||||
*/
|
||||
ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* As this is not a real GPIO at all, but just a hack to model an event in
|
||||
* ACPI the get / set functions are dummy functions.
|
||||
*/
|
||||
|
||||
static int int0002_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void int0002_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
}
|
||||
|
||||
static int int0002_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void int0002_irq_ack(struct irq_data *data)
|
||||
{
|
||||
outl(GPE0A_PME_B0_STS_BIT, GPE0A_STS_PORT);
|
||||
}
|
||||
|
||||
static void int0002_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
u32 gpe_en_reg;
|
||||
|
||||
gpe_en_reg = inl(GPE0A_EN_PORT);
|
||||
gpe_en_reg |= GPE0A_PME_B0_EN_BIT;
|
||||
outl(gpe_en_reg, GPE0A_EN_PORT);
|
||||
}
|
||||
|
||||
static void int0002_irq_mask(struct irq_data *data)
|
||||
{
|
||||
u32 gpe_en_reg;
|
||||
|
||||
gpe_en_reg = inl(GPE0A_EN_PORT);
|
||||
gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT;
|
||||
outl(gpe_en_reg, GPE0A_EN_PORT);
|
||||
}
|
||||
|
||||
static irqreturn_t int0002_irq(int irq, void *data)
|
||||
{
|
||||
struct gpio_chip *chip = data;
|
||||
u32 gpe_sts_reg;
|
||||
|
||||
gpe_sts_reg = inl(GPE0A_STS_PORT);
|
||||
if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT))
|
||||
return IRQ_NONE;
|
||||
|
||||
generic_handle_irq(irq_find_mapping(chip->irqdomain,
|
||||
GPE0A_PME_B0_VIRT_GPIO_PIN));
|
||||
|
||||
pm_system_wakeup();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irq_chip int0002_irqchip = {
|
||||
.name = DRV_NAME,
|
||||
.irq_ack = int0002_irq_ack,
|
||||
.irq_mask = int0002_irq_mask,
|
||||
.irq_unmask = int0002_irq_unmask,
|
||||
};
|
||||
|
||||
static int int0002_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct x86_cpu_id *cpu_id;
|
||||
struct gpio_chip *chip;
|
||||
int irq, ret;
|
||||
|
||||
/* Menlow has a different INT0002 device? <sigh> */
|
||||
cpu_id = x86_match_cpu(int0002_cpu_ids);
|
||||
if (!cpu_id)
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "Error getting IRQ: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->label = DRV_NAME;
|
||||
chip->parent = dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
chip->get = int0002_gpio_get;
|
||||
chip->set = int0002_gpio_set;
|
||||
chip->direction_input = int0002_gpio_get;
|
||||
chip->direction_output = int0002_gpio_direction_output;
|
||||
chip->base = -1;
|
||||
chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1;
|
||||
chip->irq_need_valid_mask = true;
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error adding gpio chip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bitmap_clear(chip->irq_valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN);
|
||||
|
||||
/*
|
||||
* We manually request the irq here instead of passing a flow-handler
|
||||
* to gpiochip_set_chained_irqchip, because the irq is shared.
|
||||
*/
|
||||
ret = devm_request_irq(dev, irq, int0002_irq,
|
||||
IRQF_SHARED | IRQF_NO_THREAD, "INT0002", chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq,
|
||||
IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error adding irqchip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id int0002_acpi_ids[] = {
|
||||
{ "INT0002", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids);
|
||||
|
||||
static struct platform_driver int0002_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.acpi_match_table = int0002_acpi_ids,
|
||||
},
|
||||
.probe = int0002_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(int0002_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -149,8 +149,8 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
|
|||
}
|
||||
|
||||
if (device_can_wakeup(&dev->dev)) {
|
||||
error = acpi_pm_device_sleep_wake(&dev->dev,
|
||||
device_may_wakeup(&dev->dev));
|
||||
error = acpi_pm_set_device_wakeup(&dev->dev,
|
||||
device_may_wakeup(&dev->dev));
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ static int pnpacpi_resume(struct pnp_dev *dev)
|
|||
}
|
||||
|
||||
if (device_may_wakeup(&dev->dev))
|
||||
acpi_pm_device_sleep_wake(&dev->dev, false);
|
||||
acpi_pm_set_device_wakeup(&dev->dev, false);
|
||||
|
||||
if (acpi_device_power_manageable(acpi_dev))
|
||||
error = acpi_device_set_power(acpi_dev, ACPI_STATE_D0);
|
||||
|
|
|
@ -584,12 +584,7 @@ static int hcd_pci_suspend_noirq(struct device *dev)
|
|||
|
||||
static int hcd_pci_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
||||
powermac_set_asic(pci_dev, 1);
|
||||
|
||||
/* Go back to D0 and disable remote wakeup */
|
||||
pci_back_from_sleep(pci_dev);
|
||||
powermac_set_asic(to_pci_dev(dev), 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
}
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
device_set_run_wake(dev, true);
|
||||
pci_set_drvdata(pci, dwc);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
@ -310,7 +309,7 @@ static int dwc3_pci_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct dwc3_pci *dwc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_run_wake(dev))
|
||||
if (device_can_wakeup(dev))
|
||||
return dwc3_pci_dsm(dwc, PCI_INTEL_BXT_STATE_D3);
|
||||
|
||||
return -EBUSY;
|
||||
|
|
|
@ -131,7 +131,7 @@ static int uhci_pci_init(struct usb_hcd *hcd)
|
|||
|
||||
/* Intel controllers use non-PME wakeup signalling */
|
||||
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
|
||||
device_set_run_wake(uhci_dev(uhci), 1);
|
||||
device_set_wakeup_capable(uhci_dev(uhci), true);
|
||||
|
||||
/* Set up pointers to PCI-specific functions */
|
||||
uhci->reset_hc = uhci_pci_reset_hc;
|
||||
|
|
|
@ -315,13 +315,12 @@ struct acpi_device_perf {
|
|||
/* Wakeup Management */
|
||||
struct acpi_device_wakeup_flags {
|
||||
u8 valid:1; /* Can successfully enable wakeup? */
|
||||
u8 run_wake:1; /* Run-Wake GPE devices */
|
||||
u8 notifier_present:1; /* Wake-up notify handler has been installed */
|
||||
u8 enabled:1; /* Enabled for wakeup */
|
||||
};
|
||||
|
||||
struct acpi_device_wakeup_context {
|
||||
struct work_struct work;
|
||||
void (*func)(struct acpi_device_wakeup_context *context);
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
|
@ -600,15 +599,20 @@ static inline bool acpi_device_always_present(struct acpi_device *adev)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void acpi_pm_wakeup_event(struct device *dev);
|
||||
acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
|
||||
void (*work_func)(struct work_struct *work));
|
||||
void (*func)(struct acpi_device_wakeup_context *context));
|
||||
acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
|
||||
bool acpi_pm_device_can_wakeup(struct device *dev);
|
||||
int acpi_pm_device_sleep_state(struct device *, int *, int);
|
||||
int acpi_pm_device_run_wake(struct device *, bool);
|
||||
int acpi_pm_set_device_wakeup(struct device *dev, bool enable);
|
||||
#else
|
||||
static inline void acpi_pm_wakeup_event(struct device *dev)
|
||||
{
|
||||
}
|
||||
static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
|
||||
struct device *dev,
|
||||
void (*work_func)(struct work_struct *work))
|
||||
void (*func)(struct acpi_device_wakeup_context *context))
|
||||
{
|
||||
return AE_SUPPORT;
|
||||
}
|
||||
|
@ -616,6 +620,10 @@ static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
|
|||
{
|
||||
return AE_SUPPORT;
|
||||
}
|
||||
static inline bool acpi_pm_device_can_wakeup(struct device *dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m)
|
||||
{
|
||||
if (p)
|
||||
|
@ -624,16 +632,7 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m)
|
|||
return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ?
|
||||
m : ACPI_STATE_D0;
|
||||
}
|
||||
static inline int acpi_pm_device_run_wake(struct device *dev, bool enable)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int acpi_pm_device_sleep_wake(struct device *, bool);
|
||||
#else
|
||||
static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
|
||||
static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -307,7 +307,6 @@ struct pci_dev {
|
|||
u8 pm_cap; /* PM capability offset */
|
||||
unsigned int pme_support:5; /* Bitmask of states from which PME#
|
||||
can be generated */
|
||||
unsigned int pme_interrupt:1;
|
||||
unsigned int pme_poll:1; /* Poll device's PME status bit */
|
||||
unsigned int d1_support:1; /* Low power state D1 is supported */
|
||||
unsigned int d2_support:1; /* Low power state D2 is supported */
|
||||
|
@ -1098,8 +1097,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
|
|||
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
|
||||
bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
|
||||
void pci_pme_active(struct pci_dev *dev, bool enable);
|
||||
int __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
|
||||
bool runtime, bool enable);
|
||||
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);
|
||||
int pci_wake_from_d3(struct pci_dev *dev, bool enable);
|
||||
int pci_prepare_to_sleep(struct pci_dev *dev);
|
||||
int pci_back_from_sleep(struct pci_dev *dev);
|
||||
|
@ -1109,12 +1107,6 @@ void pci_pme_wakeup_bus(struct pci_bus *bus);
|
|||
void pci_d3cold_enable(struct pci_dev *dev);
|
||||
void pci_d3cold_disable(struct pci_dev *dev);
|
||||
|
||||
static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state,
|
||||
bool enable)
|
||||
{
|
||||
return __pci_enable_wake(dev, state, false, enable);
|
||||
}
|
||||
|
||||
/* PCI Virtual Channel */
|
||||
int pci_save_vc_state(struct pci_dev *dev);
|
||||
void pci_restore_vc_state(struct pci_dev *dev);
|
||||
|
|
|
@ -584,7 +584,6 @@ struct dev_pm_info {
|
|||
unsigned int idle_notification:1;
|
||||
unsigned int request_pending:1;
|
||||
unsigned int deferred_resume:1;
|
||||
unsigned int run_wake:1;
|
||||
unsigned int runtime_auto:1;
|
||||
bool ignore_children:1;
|
||||
unsigned int no_callbacks:1;
|
||||
|
|
|
@ -76,16 +76,6 @@ static inline void pm_runtime_put_noidle(struct device *dev)
|
|||
atomic_add_unless(&dev->power.usage_count, -1, 0);
|
||||
}
|
||||
|
||||
static inline bool device_run_wake(struct device *dev)
|
||||
{
|
||||
return dev->power.run_wake;
|
||||
}
|
||||
|
||||
static inline void device_set_run_wake(struct device *dev, bool enable)
|
||||
{
|
||||
dev->power.run_wake = enable;
|
||||
}
|
||||
|
||||
static inline bool pm_runtime_suspended(struct device *dev)
|
||||
{
|
||||
return dev->power.runtime_status == RPM_SUSPENDED
|
||||
|
@ -163,8 +153,6 @@ static inline void pm_runtime_forbid(struct device *dev) {}
|
|||
static inline void pm_suspend_ignore_children(struct device *dev, bool enable) {}
|
||||
static inline void pm_runtime_get_noresume(struct device *dev) {}
|
||||
static inline void pm_runtime_put_noidle(struct device *dev) {}
|
||||
static inline bool device_run_wake(struct device *dev) { return false; }
|
||||
static inline void device_set_run_wake(struct device *dev, bool enable) {}
|
||||
static inline bool pm_runtime_suspended(struct device *dev) { return false; }
|
||||
static inline bool pm_runtime_active(struct device *dev) { return true; }
|
||||
static inline bool pm_runtime_status_suspended(struct device *dev) { return false; }
|
||||
|
|
|
@ -189,6 +189,8 @@ struct platform_suspend_ops {
|
|||
struct platform_freeze_ops {
|
||||
int (*begin)(void);
|
||||
int (*prepare)(void);
|
||||
void (*wake)(void);
|
||||
void (*sync)(void);
|
||||
void (*restore)(void);
|
||||
void (*end)(void);
|
||||
};
|
||||
|
@ -428,7 +430,8 @@ extern unsigned int pm_wakeup_irq;
|
|||
|
||||
extern bool pm_wakeup_pending(void);
|
||||
extern void pm_system_wakeup(void);
|
||||
extern void pm_wakeup_clear(void);
|
||||
extern void pm_system_cancel_wakeup(void);
|
||||
extern void pm_wakeup_clear(bool reset);
|
||||
extern void pm_system_irq_wakeup(unsigned int irq_number);
|
||||
extern bool pm_get_wakeup_count(unsigned int *count, bool block);
|
||||
extern bool pm_save_wakeup_count(unsigned int count);
|
||||
|
@ -478,7 +481,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
|
|||
|
||||
static inline bool pm_wakeup_pending(void) { return false; }
|
||||
static inline void pm_system_wakeup(void) {}
|
||||
static inline void pm_wakeup_clear(void) {}
|
||||
static inline void pm_wakeup_clear(bool reset) {}
|
||||
static inline void pm_system_irq_wakeup(unsigned int irq_number) {}
|
||||
|
||||
static inline void lock_system_sleep(void) {}
|
||||
|
|
|
@ -132,7 +132,7 @@ int freeze_processes(void)
|
|||
if (!pm_freezing)
|
||||
atomic_inc(&system_freezing_cnt);
|
||||
|
||||
pm_wakeup_clear();
|
||||
pm_wakeup_clear(true);
|
||||
pr_info("Freezing user space processes ... ");
|
||||
pm_freezing = true;
|
||||
error = try_to_freeze_tasks(true);
|
||||
|
|
|
@ -72,6 +72,8 @@ static void freeze_begin(void)
|
|||
|
||||
static void freeze_enter(void)
|
||||
{
|
||||
trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true);
|
||||
|
||||
spin_lock_irq(&suspend_freeze_lock);
|
||||
if (pm_wakeup_pending())
|
||||
goto out;
|
||||
|
@ -84,11 +86,9 @@ static void freeze_enter(void)
|
|||
|
||||
/* Push all the CPUs into the idle loop. */
|
||||
wake_up_all_idle_cpus();
|
||||
pr_debug("PM: suspend-to-idle\n");
|
||||
/* Make the current CPU wait so it can enter the idle loop too. */
|
||||
wait_event(suspend_freeze_wait_head,
|
||||
suspend_freeze_state == FREEZE_STATE_WAKE);
|
||||
pr_debug("PM: resume from suspend-to-idle\n");
|
||||
|
||||
cpuidle_pause();
|
||||
put_online_cpus();
|
||||
|
@ -98,6 +98,31 @@ static void freeze_enter(void)
|
|||
out:
|
||||
suspend_freeze_state = FREEZE_STATE_NONE;
|
||||
spin_unlock_irq(&suspend_freeze_lock);
|
||||
|
||||
trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false);
|
||||
}
|
||||
|
||||
static void s2idle_loop(void)
|
||||
{
|
||||
pr_debug("PM: suspend-to-idle\n");
|
||||
|
||||
do {
|
||||
freeze_enter();
|
||||
|
||||
if (freeze_ops && freeze_ops->wake)
|
||||
freeze_ops->wake();
|
||||
|
||||
dpm_resume_noirq(PMSG_RESUME);
|
||||
if (freeze_ops && freeze_ops->sync)
|
||||
freeze_ops->sync();
|
||||
|
||||
if (pm_wakeup_pending())
|
||||
break;
|
||||
|
||||
pm_wakeup_clear(false);
|
||||
} while (!dpm_suspend_noirq(PMSG_SUSPEND));
|
||||
|
||||
pr_debug("PM: resume from suspend-to-idle\n");
|
||||
}
|
||||
|
||||
void freeze_wake(void)
|
||||
|
@ -371,10 +396,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|||
* all the devices are suspended.
|
||||
*/
|
||||
if (state == PM_SUSPEND_FREEZE) {
|
||||
trace_suspend_resume(TPS("machine_suspend"), state, true);
|
||||
freeze_enter();
|
||||
trace_suspend_resume(TPS("machine_suspend"), state, false);
|
||||
goto Platform_wake;
|
||||
s2idle_loop();
|
||||
goto Platform_early_resume;
|
||||
}
|
||||
|
||||
error = disable_nonboot_cpus();
|
||||
|
|
Loading…
Reference in New Issue