Commit df45db61 authored by Lv Zheng's avatar Lv Zheng Committed by Rafael J. Wysocki

ACPI / EC: Add PM operations for suspend/resume noirq stage

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: default avatarLv Zheng <lv.zheng@intel.com>
Reported-and-tested-by: default avatarTodd E Brandt <todd.e.brandt@linux.intel.com>
[ rjw: Subject ]
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 694d0d0b
...@@ -1619,6 +1619,58 @@ int __init acpi_ec_ecdt_probe(void) ...@@ -1619,6 +1619,58 @@ int __init acpi_ec_ecdt_probe(void)
return ret; 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) static int param_set_event_clearing(const char *val, struct kernel_param *kp)
{ {
int result = 0; int result = 0;
...@@ -1664,6 +1716,7 @@ static struct acpi_driver acpi_ec_driver = { ...@@ -1664,6 +1716,7 @@ static struct acpi_driver acpi_ec_driver = {
.add = acpi_ec_add, .add = acpi_ec_add,
.remove = acpi_ec_remove, .remove = acpi_ec_remove,
}, },
.drv.pm = &acpi_ec_pm,
}; };
static inline int acpi_ec_query_init(void) static inline int acpi_ec_query_init(void)
......
...@@ -174,6 +174,8 @@ struct acpi_ec { ...@@ -174,6 +174,8 @@ struct acpi_ec {
struct work_struct work; struct work_struct work;
unsigned long timestamp; unsigned long timestamp;
unsigned long nr_pending_queries; unsigned long nr_pending_queries;
bool saved_busy_polling;
unsigned int saved_polling_guard;
}; };
extern struct acpi_ec *first_ec; extern struct acpi_ec *first_ec;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment