summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-07-03 14:23:09 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-07-03 14:23:09 +0200
commit8f8e5c3e2796eaf150d6262115af12707c2616dd (patch)
tree85f3ec09ce6960fd57c85610a5c97841f2ded9ba /drivers
parent9a5f2c871af4cf6bd63ddb20061faa7049103350 (diff)
parentde3ef1eb1cd0cc3a75f7a3661e10ed827f370ab8 (diff)
downloadop-kernel-dev-8f8e5c3e2796eaf150d6262115af12707c2616dd.zip
op-kernel-dev-8f8e5c3e2796eaf150d6262115af12707c2616dd.tar.gz
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
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/battery.c2
-rw-r--r--drivers/acpi/button.c5
-rw-r--r--drivers/acpi/device_pm.c102
-rw-r--r--drivers/acpi/ec.c2
-rw-r--r--drivers/acpi/internal.h4
-rw-r--r--drivers/acpi/pci_root.c5
-rw-r--r--drivers/acpi/proc.c4
-rw-r--r--drivers/acpi/scan.c23
-rw-r--r--drivers/acpi/sleep.c152
-rw-r--r--drivers/ata/libata-zpodd.c9
-rw-r--r--drivers/base/power/main.c10
-rw-r--r--drivers/base/power/wakeup.c18
-rw-r--r--drivers/pci/pci-acpi.c90
-rw-r--r--drivers/pci/pci-driver.c2
-rw-r--r--drivers/pci/pci-mid.c10
-rw-r--r--drivers/pci/pci.c68
-rw-r--r--drivers/pci/pci.h9
-rw-r--r--drivers/pci/pcie/pme.c16
-rw-r--r--drivers/platform/x86/Kconfig19
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/intel-hid.c40
-rw-r--r--drivers/platform/x86/intel-vbtn.c39
-rw-r--r--drivers/platform/x86/intel_int0002_vgpio.c219
-rw-r--r--drivers/pnp/pnpacpi/core.c6
-rw-r--r--drivers/usb/core/hcd-pci.c7
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c3
-rw-r--r--drivers/usb/host/uhci-pci.c2
27 files changed, 617 insertions, 250 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index d42eeef..1cbb88d 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -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;
}
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index e19f530..91cfdf3 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -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;
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 993fd31..28938b5 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -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.
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index c24235d..156e15c 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -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;
}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 66229ff..be79f7d 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -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
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 919be0a..0d34e62 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -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);
diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
index a34669c..85ac848 100644
--- a/drivers/acpi/proc.c
+++ b/drivers/acpi/proc.c
@@ -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",
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index d531629..09f65f5 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -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
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 097d630..be17664 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -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;
diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c
index f3a65a3..8a01d09 100644
--- a/drivers/ata/libata-zpodd.c
+++ b/drivers/ata/libata-zpodd.c
@@ -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);
}
/*
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 6add287..c99f873 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -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;
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 994bbf8..144e6d8 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -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)
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 47070cf..e70c1c7 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -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)
-{
- while (bus->parent) {
- if (!acpi_pm_device_sleep_wake(&bus->self->dev, enable))
- return;
- 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)
+static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
{
while (bus->parent) {
- struct pci_dev *bridge = bus->self;
+ if (acpi_pm_device_can_wakeup(&bus->self->dev))
+ return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
- if (bridge->pme_interrupt)
- return;
- if (!acpi_pm_device_run_wake(&bridge->dev, enable))
- return;
bus = bus->parent;
}
/* We have reached the root bus. */
- if (bus->bridge)
- acpi_pm_device_run_wake(bus->bridge, enable);
+ if (bus->bridge) {
+ if (acpi_pm_device_can_wakeup(bus->bridge))
+ return acpi_pm_set_device_wakeup(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_run_wake(&dev->dev, enable))
- return 0;
+ if (acpi_pm_device_can_wakeup(&dev->dev))
+ return acpi_pm_set_device_wakeup(&dev->dev, enable);
- 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)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 192e7b6..ffe7d54 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -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);
diff --git a/drivers/pci/pci-mid.c b/drivers/pci/pci-mid.c
index 1c4af72..a4ac940 100644
--- a/drivers/pci/pci-mid.c
+++ b/drivers/pci/pci-mid.c
@@ -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,
};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 563901c..0b5302a 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -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;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f8113e5..240b2c0 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -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);
};
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 2dd1c68..80e58d2 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -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);
}
/**
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 8489020..a3ccc3c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -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
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 182a3ed..ab22ce7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -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
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index 63ba2cb..8519e0f 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -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_pl_suspend_handler(struct device *device)
+static int intel_hid_pm_prepare(struct device *device)
{
- intel_hid_set_enable(device, false);
- intel_button_array_enable(device, false);
+ 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)
+{
+ 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:
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
index c2035e1..61f1063 100644
--- a/drivers/platform/x86/intel-vbtn.c
+++ b/drivers/platform/x86/intel-vbtn.c
@@ -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,
diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c
new file mode 100644
index 0000000..92dc230
--- /dev/null
+++ b/drivers/platform/x86/intel_int0002_vgpio.c
@@ -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");
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 9113876..3a4c1aa 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -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);
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 7859d73..ea829ad 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -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;
}
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index fe85154..7e995df 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -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;
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 02260cf..49effdc 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -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;
OpenPOWER on IntegriCloud