aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/ec.c
diff options
context:
space:
mode:
authorLv Zheng <lv.zheng@intel.com>2016-08-03 04:01:43 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-08-30 18:32:11 -0400
commit39a2a2aa3e9e5538984e9130c92a6c889ad86435 (patch)
tree88601421f8fa20a1793786963fdba19c739555b9 /drivers/acpi/ec.c
parentc2b46d679b30c5c0d7eb47a21085943242bdd8dc (diff)
ACPI / EC: Add PM operations to improve event handling for suspend process
In the original EC driver, though the event handling is not explicitly stopped, the EC driver is actually not able to handle events during the noirq stage as the EC driver is not prepared to handle the EC events in the polling mode. So if there is no advance_transaction() triggered, the EC driver couldn't notice the EC events. However, do we actually need to handle EC events during suspend/resume stage? EC events are mostly useless for the suspend/resume period (key strokes and battery/thermal updates, etc.,), and the useful ones (lid close, power/sleep button press) should have already been delivered to the OSPM to trigger the power saving operations. Thus this patch implements acpi_ec_disable_event() to be a reverse call of acpi_ec_enable_event(), with which, the EC driver is able to stop handling the EC events in a position before entering the noirq stage. Since there are actually 2 choices for us: 1. implement event handling in polling mode; 2. stop event handling before entering noirq stage. And this patch only implements the second choice using .suspend() callback. Thus this is experimental (first choice is better? or different hook position is better?). This patch finally keeps the old behavior by default and prepares a boot parameter to enable this feature. The differences of the event handling availability between the old behavior (this patch is not applied) and the new behavior (this patch is applied) are as follows: !FreezeEvents FreezeEvents before suspend Y Y suspend before EC Y Y suspend after EC Y N suspend_late Y N suspend_noirq Y (actually N) N resume_noirq Y (actually N) N resume_late Y (actually N) N resume before EC Y (actually N) N resume after EC Y Y after resume Y Y Where "actually N" means if there is no EC transactions, the EC driver is actually not able to notice the pending events. We can see that FreezeEvents is the only approach now can actually flush the EC event handling with both query commands and _Qxx evaluations flushed, other modes can only flush the EC event handling with only query commands flushed, _Qxx evaluations occurred after stopping the EC driver may end up failure due to the failure of the EC transaction carried out in the _Qxx control methods. We also can see that this feature should be able to trigger some platform notifications later than resuming other drivers. Signed-off-by: Lv Zheng <lv.zheng@intel.com> Tested-by: Todd E Brandt <todd.e.brandt@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r--drivers/acpi/ec.c64
1 files changed, 59 insertions, 5 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 8d5444defd7e..2bec709eb4e5 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -146,6 +146,10 @@ static unsigned int ec_storm_threshold __read_mostly = 8;
146module_param(ec_storm_threshold, uint, 0644); 146module_param(ec_storm_threshold, uint, 0644);
147MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); 147MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm");
148 148
149static bool ec_freeze_events __read_mostly = false;
150module_param(ec_freeze_events, bool, 0644);
151MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume");
152
149struct acpi_ec_query_handler { 153struct acpi_ec_query_handler {
150 struct list_head node; 154 struct list_head node;
151 acpi_ec_query_func func; 155 acpi_ec_query_func func;
@@ -250,10 +254,18 @@ static bool acpi_ec_event_enabled(struct acpi_ec *ec)
250 if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) 254 if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
251 return false; 255 return false;
252 /* 256 /*
253 * The EC event handling is automatically disabled as soon as the 257 * However, disabling the event handling is experimental for late
254 * EC driver is stopped. 258 * stage (suspend), and is controlled by the boot parameter of
259 * "ec_freeze_events":
260 * 1. true: The EC event handling is disabled before entering
261 * the noirq stage.
262 * 2. false: The EC event handling is automatically disabled as
263 * soon as the EC driver is stopped.
255 */ 264 */
256 return test_bit(EC_FLAGS_STARTED, &ec->flags); 265 if (ec_freeze_events)
266 return acpi_ec_started(ec);
267 else
268 return test_bit(EC_FLAGS_STARTED, &ec->flags);
257} 269}
258 270
259static bool acpi_ec_flushed(struct acpi_ec *ec) 271static bool acpi_ec_flushed(struct acpi_ec *ec)
@@ -512,6 +524,38 @@ static void acpi_ec_enable_event(struct acpi_ec *ec)
512 acpi_ec_clear(ec); 524 acpi_ec_clear(ec);
513} 525}
514 526
527static bool acpi_ec_query_flushed(struct acpi_ec *ec)
528{
529 bool flushed;
530 unsigned long flags;
531
532 spin_lock_irqsave(&ec->lock, flags);
533 flushed = !ec->nr_pending_queries;
534 spin_unlock_irqrestore(&ec->lock, flags);
535 return flushed;
536}
537
538static void __acpi_ec_flush_event(struct acpi_ec *ec)
539{
540 /*
541 * When ec_freeze_events is true, we need to flush events in
542 * the proper position before entering the noirq stage.
543 */
544 wait_event(ec->wait, acpi_ec_query_flushed(ec));
545 if (ec_query_wq)
546 flush_workqueue(ec_query_wq);
547}
548
549static void acpi_ec_disable_event(struct acpi_ec *ec)
550{
551 unsigned long flags;
552
553 spin_lock_irqsave(&ec->lock, flags);
554 __acpi_ec_disable_event(ec);
555 spin_unlock_irqrestore(&ec->lock, flags);
556 __acpi_ec_flush_event(ec);
557}
558
515static bool acpi_ec_guard_event(struct acpi_ec *ec) 559static bool acpi_ec_guard_event(struct acpi_ec *ec)
516{ 560{
517 bool guarded = true; 561 bool guarded = true;
@@ -941,7 +985,7 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
941 if (!suspending) { 985 if (!suspending) {
942 acpi_ec_complete_request(ec); 986 acpi_ec_complete_request(ec);
943 ec_dbg_ref(ec, "Decrease driver"); 987 ec_dbg_ref(ec, "Decrease driver");
944 } else 988 } else if (!ec_freeze_events)
945 __acpi_ec_disable_event(ec); 989 __acpi_ec_disable_event(ec);
946 clear_bit(EC_FLAGS_STARTED, &ec->flags); 990 clear_bit(EC_FLAGS_STARTED, &ec->flags);
947 clear_bit(EC_FLAGS_STOPPED, &ec->flags); 991 clear_bit(EC_FLAGS_STOPPED, &ec->flags);
@@ -1693,6 +1737,16 @@ static int acpi_ec_resume_noirq(struct device *dev)
1693 return 0; 1737 return 0;
1694} 1738}
1695 1739
1740static int acpi_ec_suspend(struct device *dev)
1741{
1742 struct acpi_ec *ec =
1743 acpi_driver_data(to_acpi_device(dev));
1744
1745 if (ec_freeze_events)
1746 acpi_ec_disable_event(ec);
1747 return 0;
1748}
1749
1696static int acpi_ec_resume(struct device *dev) 1750static int acpi_ec_resume(struct device *dev)
1697{ 1751{
1698 struct acpi_ec *ec = 1752 struct acpi_ec *ec =
@@ -1705,7 +1759,7 @@ static int acpi_ec_resume(struct device *dev)
1705 1759
1706static const struct dev_pm_ops acpi_ec_pm = { 1760static const struct dev_pm_ops acpi_ec_pm = {
1707 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) 1761 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
1708 SET_SYSTEM_SLEEP_PM_OPS(NULL, acpi_ec_resume) 1762 SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
1709}; 1763};
1710 1764
1711static int param_set_event_clearing(const char *val, struct kernel_param *kp) 1765static int param_set_event_clearing(const char *val, struct kernel_param *kp)