Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1452364 > unrolled thread
| Started by | Lv Zheng <lv.zheng@intel.com> |
|---|---|
| First post | 2016-07-29 12:10 +0200 |
| Last post | 2016-08-03 03:10 +0200 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
[PATCH v2 0/2] ACPI / EC: Tune suspend/resume speed using PM operations Lv Zheng <lv.zheng@intel.com> - 2016-07-29 12:10 +0200
[PATCH v2 1/2] ACPI / EC: Add PM operations to tune polling mode efficiency Lv Zheng <lv.zheng@intel.com> - 2016-07-29 12:10 +0200
[PATCH v2 2/2] ACPI / EC: Add PM operations to block event handling Lv Zheng <lv.zheng@intel.com> - 2016-07-29 12:10 +0200
RE: [PATCH v2 2/2] ACPI / EC: Add PM operations to block event handling "Zheng, Lv" <lv.zheng@intel.com> - 2016-08-03 03:10 +0200
| From | Lv Zheng <lv.zheng@intel.com> |
|---|---|
| Date | 2016-07-29 12:10 +0200 |
| Subject | [PATCH v2 0/2] ACPI / EC: Tune suspend/resume speed using PM operations |
| Message-ID | <s0cTU-3ex-17@gated-at.bofh.it> |
There are 2 improvements can be done to the EC driver to make system suspend/resume faster: 1. Automatically use busy polling mode when noirq is entered 2. Disallow event handling (SCI_EVT/_Qxx) during suspend/resume period This patchset achieves such performance tuning on top of a recent workaround that creates ec_query_wq. Lv Zheng (2): ACPI / EC: Add PM operations to tune polling mode efficiency ACPI / EC: Add PM operations to block event handling drivers/acpi/ec.c | 199 ++++++++++++++++++++++++++++++++++++----------- drivers/acpi/internal.h | 3 +- drivers/acpi/sleep.c | 4 +- 3 files changed, 158 insertions(+), 48 deletions(-) -- 1.7.10
[toc] | [next] | [standalone]
| From | Lv Zheng <lv.zheng@intel.com> |
|---|---|
| Date | 2016-07-29 12:10 +0200 |
| Subject | [PATCH v2 1/2] ACPI / EC: Add PM operations to tune polling mode efficiency |
| Message-ID | <s0cTU-3ex-25@gated-at.bofh.it> |
| In reply to | #1452364 |
It is reported that on some platforms, resume speed is not fast. The cause
is: in noirq stage, EC driver is working in polling mode, and each state
machine advancement requires a context switch.
The context switch is not necessary to the EC driver's polling mode. This
patch implements PM hooks to automatically switch the driver to/from the
busy polling mode to eliminate the overhead caused by the context switch.
This finally contributes to the tuning result: acpi_pm_finish() execution
time is improved from 192ms to 6ms.
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Reported-and-tested-by: Todd E Brandt <todd.e.brandt@linux.intel.com>
---
drivers/acpi/ec.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/internal.h | 2 ++
2 files changed, 55 insertions(+)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 907b450..7cdcdf6 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1619,6 +1619,58 @@ error:
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static void acpi_ec_enter_noirq(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ if (ec == first_ec) {
+ spin_lock_irqsave(&ec->lock, flags);
+ ec->saved_busy_polling = ec_busy_polling;
+ ec->saved_polling_guard = ec_polling_guard;
+ ec_busy_polling = true;
+ ec_polling_guard = 0;
+ ec_log_drv("interrupt blocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+ }
+}
+
+static void acpi_ec_leave_noirq(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ if (ec == first_ec) {
+ spin_lock_irqsave(&ec->lock, flags);
+ ec_busy_polling = ec->saved_busy_polling;
+ ec_polling_guard = ec->saved_polling_guard;
+ ec_log_drv("interrupt unblocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+ }
+}
+
+static int acpi_ec_suspend_noirq(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_enter_noirq(ec);
+ return 0;
+}
+
+static int acpi_ec_resume_noirq(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_leave_noirq(ec);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops acpi_ec_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
+};
+
static int param_set_event_clearing(const char *val, struct kernel_param *kp)
{
int result = 0;
@@ -1664,6 +1716,7 @@ static struct acpi_driver acpi_ec_driver = {
.add = acpi_ec_add,
.remove = acpi_ec_remove,
},
+ .drv.pm = &acpi_ec_pm,
};
int __init acpi_ec_init(void)
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 940218f..6996121 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -174,6 +174,8 @@ struct acpi_ec {
struct work_struct work;
unsigned long timestamp;
unsigned long nr_pending_queries;
+ bool saved_busy_polling;
+ unsigned int saved_polling_guard;
};
extern struct acpi_ec *first_ec;
--
1.7.10
[toc] | [prev] | [next] | [standalone]
| From | Lv Zheng <lv.zheng@intel.com> |
|---|---|
| Date | 2016-07-29 12:10 +0200 |
| Subject | [PATCH v2 2/2] ACPI / EC: Add PM operations to block event handling |
| Message-ID | <s0cTU-3ex-23@gated-at.bofh.it> |
| In reply to | #1452364 |
Originally, EC driver stops handling both events and transactions in
acpi_ec_block_transactions(), and restarts to handle transactions in
acpi_ec_unblock_transactions_early(), restarts to handle both events and
transactions in acpi_ec_unblock_transactions().
Thus, the event handling is actually stopped after the IRQ is disabled, but
the EC driver is not capable of handling SCI_EVT in noirq stage, thus it is
likely the event is not detected by the EC driver.
This patch tries to restore the old behavior for the EC driver. 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 OS to
trigger the power saving operations. Thus EC driver should stop handling
events during this period, just like what the EC driver does during the
boot stage. And tests show this behavior is working and can make suspend
even faster when many events is triggered during this stage (for example,
during this stage, frequently trigger wifi switches).
OTOH, delivering EC events too early may confuse drivers because when the
drivers see the events, the drivers themselves may not have been resumed.
Thus this patch implements PM hooks, stops to handle event in .suspend()
hook and restarts to handle event in .resume() hook. This is different
from the original implementation, enlarging the event handling blocking
period longer:
Original Current
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 N
resume before EC Y N
resume after EC Y Y
Since this is experimental, a boot parameter is prepared to not to
disable event handling during suspend/resume period.
By implementing .resume() hook to re-enable the event handling, the
following 2 APIs serve for the same purpose (restart transactions) and can
be combined:
acpi_ec_unblock_transactions_early()/acpi_ec_unblock_transactions().
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Tested-by: Todd E Brandt <todd.e.brandt@linux.intel.com>
---
drivers/acpi/ec.c | 146 ++++++++++++++++++++++++++++++++---------------
drivers/acpi/internal.h | 1 -
drivers/acpi/sleep.c | 4 +-
3 files changed, 103 insertions(+), 48 deletions(-)
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 7cdcdf6..8c3034c 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -104,6 +104,7 @@ enum ec_command {
#define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */
enum {
+ EC_FLAGS_QUERY_ENABLED, /* Query is enabled */
EC_FLAGS_QUERY_PENDING, /* Query is pending */
EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */
EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */
@@ -145,6 +146,10 @@ static unsigned int ec_storm_threshold __read_mostly = 8;
module_param(ec_storm_threshold, uint, 0644);
MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm");
+static bool ec_freeze_events __read_mostly = true;
+module_param(ec_freeze_events, bool, 0644);
+MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume");
+
struct acpi_ec_query_handler {
struct list_head node;
acpi_ec_query_func func;
@@ -427,13 +432,19 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
return true;
}
+static void __acpi_ec_submit_query(struct acpi_ec *ec)
+{
+ ec_dbg_evt("Command(%s) submitted/blocked",
+ acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+ ec->nr_pending_queries++;
+ schedule_work(&ec->work);
+}
+
static void acpi_ec_submit_query(struct acpi_ec *ec)
{
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
- ec_dbg_evt("Command(%s) submitted/blocked",
- acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
- ec->nr_pending_queries++;
- schedule_work(&ec->work);
+ if (test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ __acpi_ec_submit_query(ec);
}
}
@@ -446,6 +457,70 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
}
}
+static bool acpi_ec_query_flushed(struct acpi_ec *ec)
+{
+ bool flushed;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ flushed = !ec->nr_pending_queries;
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ return flushed;
+}
+
+/*
+ * Process _Q events that might have accumulated in the EC.
+ * Run with locked ec mutex.
+ */
+static void acpi_ec_clear(struct acpi_ec *ec)
+{
+ int i, status;
+ u8 value = 0;
+
+ for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
+ status = acpi_ec_query(ec, &value);
+ if (status || !value)
+ break;
+ }
+ if (unlikely(i == ACPI_EC_CLEAR_MAX))
+ pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
+ else
+ pr_info("%d stale EC events cleared\n", i);
+}
+
+static void acpi_ec_disable_event(struct acpi_ec *ec, bool flushing)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags);
+ ec_log_drv("event blocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+ if (flushing && ec_query_wq) {
+ wait_event(ec->wait, acpi_ec_query_flushed(ec));
+ flush_workqueue(ec_query_wq);
+ }
+}
+
+static void acpi_ec_enable_event(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ ec_log_drv("event unblocked");
+ if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
+ __acpi_ec_submit_query(ec);
+ else
+ advance_transaction(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ /* Drain additional events if hardware requires that */
+ if (EC_FLAGS_CLEAR_ON_RESUME)
+ acpi_ec_clear(ec);
+}
+
static bool acpi_ec_guard_event(struct acpi_ec *ec)
{
bool guarded = true;
@@ -832,27 +907,6 @@ acpi_handle ec_get_handle(void)
}
EXPORT_SYMBOL(ec_get_handle);
-/*
- * Process _Q events that might have accumulated in the EC.
- * Run with locked ec mutex.
- */
-static void acpi_ec_clear(struct acpi_ec *ec)
-{
- int i, status;
- u8 value = 0;
-
- for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
- status = acpi_ec_query(ec, &value);
- if (status || !value)
- break;
- }
-
- if (unlikely(i == ACPI_EC_CLEAR_MAX))
- pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
- else
- pr_info("%d stale EC events cleared\n", i);
-}
-
static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
{
unsigned long flags;
@@ -919,20 +973,6 @@ void acpi_ec_block_transactions(void)
void acpi_ec_unblock_transactions(void)
{
- struct acpi_ec *ec = first_ec;
-
- if (!ec)
- return;
-
- /* Allow transactions to be carried out again */
- acpi_ec_start(ec, true);
-
- if (EC_FLAGS_CLEAR_ON_RESUME)
- acpi_ec_clear(ec);
-}
-
-void acpi_ec_unblock_transactions_early(void)
-{
/*
* Allow transactions to happen again (this function is called from
* atomic context during wakeup, so we don't need to acquire the mutex).
@@ -1234,13 +1274,13 @@ static struct acpi_ec *make_acpi_ec(void)
if (!ec)
return NULL;
- ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
mutex_init(&ec->mutex);
init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list);
spin_lock_init(&ec->lock);
INIT_WORK(&ec->work, acpi_ec_event_handler);
ec->timestamp = jiffies;
+ acpi_ec_disable_event(ec, false);
return ec;
}
@@ -1421,11 +1461,7 @@ static int acpi_ec_add(struct acpi_device *device)
acpi_walk_dep_device_list(ec->handle);
/* EC is fully operational, allow queries */
- clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
-
- /* Clear stale _Q events if hardware might require that */
- if (EC_FLAGS_CLEAR_ON_RESUME)
- acpi_ec_clear(ec);
+ acpi_ec_enable_event(ec);
return ret;
}
@@ -1665,10 +1701,30 @@ static int acpi_ec_resume_noirq(struct device *dev)
acpi_ec_leave_noirq(ec);
return 0;
}
+
+static int acpi_ec_suspend(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ if (ec_freeze_events)
+ acpi_ec_disable_event(ec, true);
+ return 0;
+}
+
+static int acpi_ec_resume(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_enable_event(ec);
+ return 0;
+}
#endif
static const struct dev_pm_ops acpi_ec_pm = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
+ SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
};
static int param_set_event_clearing(const char *val, struct kernel_param *kp)
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 6996121..29f2063 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -189,7 +189,6 @@ int acpi_ec_ecdt_probe(void);
int acpi_ec_dsdt_probe(void);
void acpi_ec_block_transactions(void);
void acpi_ec_unblock_transactions(void);
-void acpi_ec_unblock_transactions_early(void);
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func,
void *data);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 9788663..deb0ff7 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -586,7 +586,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
*/
acpi_disable_all_gpes();
/* Allow EC transactions to happen. */
- acpi_ec_unblock_transactions_early();
+ acpi_ec_unblock_transactions();
suspend_nvs_restore();
@@ -784,7 +784,7 @@ static void acpi_hibernation_leave(void)
/* Restore the NVS memory area */
suspend_nvs_restore();
/* Allow EC transactions to happen. */
- acpi_ec_unblock_transactions_early();
+ acpi_ec_unblock_transactions();
}
static void acpi_pm_thaw(void)
--
1.7.10
[toc] | [prev] | [next] | [standalone]
| From | "Zheng, Lv" <lv.zheng@intel.com> |
|---|---|
| Date | 2016-08-03 03:10 +0200 |
| Subject | RE: [PATCH v2 2/2] ACPI / EC: Add PM operations to block event handling |
| Message-ID | <s1SR3-3R6-7@gated-at.bofh.it> |
| In reply to | #1452367 |
Hi, Rafael
This patch in fact contains many fixes.
I'll split this patch into several small patches to make it easier for the reviewers.
Thus I'll re-send this patch using a separate series and re-send only patch 1 as v3 to this thread.
Sorry for the noise.
Thanks and best regards
-Lv
> From: Zheng, Lv
> Sent: Friday, July 29, 2016 6:06 PM
> Subject: [PATCH v2 2/2] ACPI / EC: Add PM operations to block event
> handling
>
> Originally, EC driver stops handling both events and transactions in
> acpi_ec_block_transactions(), and restarts to handle transactions in
> acpi_ec_unblock_transactions_early(), restarts to handle both events and
> transactions in acpi_ec_unblock_transactions().
>
> Thus, the event handling is actually stopped after the IRQ is disabled, but
> the EC driver is not capable of handling SCI_EVT in noirq stage, thus it is
> likely the event is not detected by the EC driver.
>
> This patch tries to restore the old behavior for the EC driver. 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 OS to
> trigger the power saving operations. Thus EC driver should stop handling
> events during this period, just like what the EC driver does during the
> boot stage. And tests show this behavior is working and can make suspend
> even faster when many events is triggered during this stage (for example,
> during this stage, frequently trigger wifi switches).
>
> OTOH, delivering EC events too early may confuse drivers because when
> the
> drivers see the events, the drivers themselves may not have been resumed.
>
> Thus this patch implements PM hooks, stops to handle event in .suspend()
> hook and restarts to handle event in .resume() hook. This is different
> from the original implementation, enlarging the event handling blocking
> period longer:
> Original Current
> 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 N
> resume before EC Y N
> resume after EC Y Y
> Since this is experimental, a boot parameter is prepared to not to
> disable event handling during suspend/resume period.
>
> By implementing .resume() hook to re-enable the event handling, the
> following 2 APIs serve for the same purpose (restart transactions) and can
> be combined:
> acpi_ec_unblock_transactions_early()/acpi_ec_unblock_transactions().
>
> Signed-off-by: Lv Zheng <lv.zheng@intel.com>
> Tested-by: Todd E Brandt <todd.e.brandt@linux.intel.com>
> ---
> drivers/acpi/ec.c | 146 ++++++++++++++++++++++++++++++++-------
> --------
> drivers/acpi/internal.h | 1 -
> drivers/acpi/sleep.c | 4 +-
> 3 files changed, 103 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
> index 7cdcdf6..8c3034c 100644
> --- a/drivers/acpi/ec.c
> +++ b/drivers/acpi/ec.c
> @@ -104,6 +104,7 @@ enum ec_command {
> #define ACPI_EC_MAX_QUERIES 16 /* Maximum number of
> parallel queries */
>
> enum {
> + EC_FLAGS_QUERY_ENABLED, /* Query is enabled */
> EC_FLAGS_QUERY_PENDING, /* Query is pending */
> EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check
> */
> EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */
> @@ -145,6 +146,10 @@ static unsigned int ec_storm_threshold
> __read_mostly = 8;
> module_param(ec_storm_threshold, uint, 0644);
> MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers
> not considered as GPE storm");
>
> +static bool ec_freeze_events __read_mostly = true;
> +module_param(ec_freeze_events, bool, 0644);
> +MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling
> during suspend/resume");
> +
> struct acpi_ec_query_handler {
> struct list_head node;
> acpi_ec_query_func func;
> @@ -427,13 +432,19 @@ static bool
> acpi_ec_submit_flushable_request(struct acpi_ec *ec)
> return true;
> }
>
> +static void __acpi_ec_submit_query(struct acpi_ec *ec)
> +{
> + ec_dbg_evt("Command(%s) submitted/blocked",
> + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
> + ec->nr_pending_queries++;
> + schedule_work(&ec->work);
> +}
> +
> static void acpi_ec_submit_query(struct acpi_ec *ec)
> {
> if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
> - ec_dbg_evt("Command(%s) submitted/blocked",
> -
> acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
> - ec->nr_pending_queries++;
> - schedule_work(&ec->work);
> + if (test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
> + __acpi_ec_submit_query(ec);
> }
> }
>
> @@ -446,6 +457,70 @@ static void acpi_ec_complete_query(struct
> acpi_ec *ec)
> }
> }
>
> +static bool acpi_ec_query_flushed(struct acpi_ec *ec)
> +{
> + bool flushed;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ec->lock, flags);
> + flushed = !ec->nr_pending_queries;
> + spin_unlock_irqrestore(&ec->lock, flags);
> +
> + return flushed;
> +}
> +
> +/*
> + * Process _Q events that might have accumulated in the EC.
> + * Run with locked ec mutex.
> + */
> +static void acpi_ec_clear(struct acpi_ec *ec)
> +{
> + int i, status;
> + u8 value = 0;
> +
> + for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
> + status = acpi_ec_query(ec, &value);
> + if (status || !value)
> + break;
> + }
> + if (unlikely(i == ACPI_EC_CLEAR_MAX))
> + pr_warn("Warning: Maximum of %d stale EC events
> cleared\n", i);
> + else
> + pr_info("%d stale EC events cleared\n", i);
> +}
> +
> +static void acpi_ec_disable_event(struct acpi_ec *ec, bool flushing)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ec->lock, flags);
> + clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags);
> + ec_log_drv("event blocked");
> + spin_unlock_irqrestore(&ec->lock, flags);
> + if (flushing && ec_query_wq) {
> + wait_event(ec->wait, acpi_ec_query_flushed(ec));
> + flush_workqueue(ec_query_wq);
> + }
> +}
> +
> +static void acpi_ec_enable_event(struct acpi_ec *ec)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ec->lock, flags);
> + if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
> + ec_log_drv("event unblocked");
> + if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
> + __acpi_ec_submit_query(ec);
> + else
> + advance_transaction(ec);
> + spin_unlock_irqrestore(&ec->lock, flags);
> +
> + /* Drain additional events if hardware requires that */
> + if (EC_FLAGS_CLEAR_ON_RESUME)
> + acpi_ec_clear(ec);
> +}
> +
> static bool acpi_ec_guard_event(struct acpi_ec *ec)
> {
> bool guarded = true;
> @@ -832,27 +907,6 @@ acpi_handle ec_get_handle(void)
> }
> EXPORT_SYMBOL(ec_get_handle);
>
> -/*
> - * Process _Q events that might have accumulated in the EC.
> - * Run with locked ec mutex.
> - */
> -static void acpi_ec_clear(struct acpi_ec *ec)
> -{
> - int i, status;
> - u8 value = 0;
> -
> - for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
> - status = acpi_ec_query(ec, &value);
> - if (status || !value)
> - break;
> - }
> -
> - if (unlikely(i == ACPI_EC_CLEAR_MAX))
> - pr_warn("Warning: Maximum of %d stale EC events
> cleared\n", i);
> - else
> - pr_info("%d stale EC events cleared\n", i);
> -}
> -
> static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
> {
> unsigned long flags;
> @@ -919,20 +973,6 @@ void acpi_ec_block_transactions(void)
>
> void acpi_ec_unblock_transactions(void)
> {
> - struct acpi_ec *ec = first_ec;
> -
> - if (!ec)
> - return;
> -
> - /* Allow transactions to be carried out again */
> - acpi_ec_start(ec, true);
> -
> - if (EC_FLAGS_CLEAR_ON_RESUME)
> - acpi_ec_clear(ec);
> -}
> -
> -void acpi_ec_unblock_transactions_early(void)
> -{
> /*
> * Allow transactions to happen again (this function is called from
> * atomic context during wakeup, so we don't need to acquire the
> mutex).
> @@ -1234,13 +1274,13 @@ static struct acpi_ec *make_acpi_ec(void)
>
> if (!ec)
> return NULL;
> - ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
> mutex_init(&ec->mutex);
> init_waitqueue_head(&ec->wait);
> INIT_LIST_HEAD(&ec->list);
> spin_lock_init(&ec->lock);
> INIT_WORK(&ec->work, acpi_ec_event_handler);
> ec->timestamp = jiffies;
> + acpi_ec_disable_event(ec, false);
> return ec;
> }
>
> @@ -1421,11 +1461,7 @@ static int acpi_ec_add(struct acpi_device
> *device)
> acpi_walk_dep_device_list(ec->handle);
>
> /* EC is fully operational, allow queries */
> - clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
> -
> - /* Clear stale _Q events if hardware might require that */
> - if (EC_FLAGS_CLEAR_ON_RESUME)
> - acpi_ec_clear(ec);
> + acpi_ec_enable_event(ec);
> return ret;
> }
>
> @@ -1665,10 +1701,30 @@ static int acpi_ec_resume_noirq(struct
> device *dev)
> acpi_ec_leave_noirq(ec);
> return 0;
> }
> +
> +static int acpi_ec_suspend(struct device *dev)
> +{
> + struct acpi_ec *ec =
> + acpi_driver_data(to_acpi_device(dev));
> +
> + if (ec_freeze_events)
> + acpi_ec_disable_event(ec, true);
> + return 0;
> +}
> +
> +static int acpi_ec_resume(struct device *dev)
> +{
> + struct acpi_ec *ec =
> + acpi_driver_data(to_acpi_device(dev));
> +
> + acpi_ec_enable_event(ec);
> + return 0;
> +}
> #endif
>
> static const struct dev_pm_ops acpi_ec_pm = {
> SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq,
> acpi_ec_resume_noirq)
> + SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
> };
>
> static int param_set_event_clearing(const char *val, struct kernel_param
> *kp)
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 6996121..29f2063 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -189,7 +189,6 @@ int acpi_ec_ecdt_probe(void);
> int acpi_ec_dsdt_probe(void);
> void acpi_ec_block_transactions(void);
> void acpi_ec_unblock_transactions(void);
> -void acpi_ec_unblock_transactions_early(void);
> int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
> acpi_handle handle, acpi_ec_query_func func,
> void *data);
> diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
> index 9788663..deb0ff7 100644
> --- a/drivers/acpi/sleep.c
> +++ b/drivers/acpi/sleep.c
> @@ -586,7 +586,7 @@ static int acpi_suspend_enter(suspend_state_t
> pm_state)
> */
> acpi_disable_all_gpes();
> /* Allow EC transactions to happen. */
> - acpi_ec_unblock_transactions_early();
> + acpi_ec_unblock_transactions();
>
> suspend_nvs_restore();
>
> @@ -784,7 +784,7 @@ static void acpi_hibernation_leave(void)
> /* Restore the NVS memory area */
> suspend_nvs_restore();
> /* Allow EC transactions to happen. */
> - acpi_ec_unblock_transactions_early();
> + acpi_ec_unblock_transactions();
> }
>
> static void acpi_pm_thaw(void)
> --
> 1.7.10
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web