Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -1619,6 +1619,58 @@ int __init acpi_ec_ecdt_probe(void)
	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)
+2 −0
Original line number Diff line number Diff line
@@ -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;