aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLv Zheng <lv.zheng@intel.com>2016-08-02 21:07:58 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-08-16 20:37:02 -0400
commitdf45db6177f8dde380d44149cca46ad800a00575 (patch)
treecb42fc57f30847b2b38b19e3790e50ff140f5592
parent694d0d0bb2030d2e36df73e2d23d5770511dbc8d (diff)
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>
-rw-r--r--drivers/acpi/ec.c53
-rw-r--r--drivers/acpi/internal.h2
2 files changed, 55 insertions, 0 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index e7bd57cc550a..6f6c7d1eaf8c 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1619,6 +1619,58 @@ error:
1619 return ret; 1619 return ret;
1620} 1620}
1621 1621
1622#ifdef CONFIG_PM_SLEEP
1623static void acpi_ec_enter_noirq(struct acpi_ec *ec)
1624{
1625 unsigned long flags;
1626
1627 if (ec == first_ec) {
1628 spin_lock_irqsave(&ec->lock, flags);
1629 ec->saved_busy_polling = ec_busy_polling;
1630 ec->saved_polling_guard = ec_polling_guard;
1631 ec_busy_polling = true;
1632 ec_polling_guard = 0;
1633 ec_log_drv("interrupt blocked");
1634 spin_unlock_irqrestore(&ec->lock, flags);
1635 }
1636}
1637
1638static void acpi_ec_leave_noirq(struct acpi_ec *ec)
1639{
1640 unsigned long flags;
1641
1642 if (ec == first_ec) {
1643 spin_lock_irqsave(&ec->lock, flags);
1644 ec_busy_polling = ec->saved_busy_polling;
1645 ec_polling_guard = ec->saved_polling_guard;
1646 ec_log_drv("interrupt unblocked");
1647 spin_unlock_irqrestore(&ec->lock, flags);
1648 }
1649}
1650
1651static int acpi_ec_suspend_noirq(struct device *dev)
1652{
1653 struct acpi_ec *ec =
1654 acpi_driver_data(to_acpi_device(dev));
1655
1656 acpi_ec_enter_noirq(ec);
1657 return 0;
1658}
1659
1660static int acpi_ec_resume_noirq(struct device *dev)
1661{
1662 struct acpi_ec *ec =
1663 acpi_driver_data(to_acpi_device(dev));
1664
1665 acpi_ec_leave_noirq(ec);
1666 return 0;
1667}
1668#endif
1669
1670static const struct dev_pm_ops acpi_ec_pm = {
1671 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
1672};
1673
1622static int param_set_event_clearing(const char *val, struct kernel_param *kp) 1674static int param_set_event_clearing(const char *val, struct kernel_param *kp)
1623{ 1675{
1624 int result = 0; 1676 int result = 0;
@@ -1664,6 +1716,7 @@ static struct acpi_driver acpi_ec_driver = {
1664 .add = acpi_ec_add, 1716 .add = acpi_ec_add,
1665 .remove = acpi_ec_remove, 1717 .remove = acpi_ec_remove,
1666 }, 1718 },
1719 .drv.pm = &acpi_ec_pm,
1667}; 1720};
1668 1721
1669static inline int acpi_ec_query_init(void) 1722static inline int acpi_ec_query_init(void)
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 940218ff0193..6996121ee003 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -174,6 +174,8 @@ struct acpi_ec {
174 struct work_struct work; 174 struct work_struct work;
175 unsigned long timestamp; 175 unsigned long timestamp;
176 unsigned long nr_pending_queries; 176 unsigned long nr_pending_queries;
177 bool saved_busy_polling;
178 unsigned int saved_polling_guard;
177}; 179};
178 180
179extern struct acpi_ec *first_ec; 181extern struct acpi_ec *first_ec;