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: Lv Zheng <lv.zheng@intel.com> Reported-and-tested-by: Todd E Brandt <todd.e.brandt@linux.intel.com> [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
694d0d0bb2
commit
df45db6177
|
@ -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,
|
||||
};
|
||||
|
||||
static inline int acpi_ec_query_init(void)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue