diff options
author | Lv Zheng <lv.zheng@intel.com> | 2016-08-02 21:07:58 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-08-16 20:37:02 -0400 |
commit | df45db6177f8dde380d44149cca46ad800a00575 (patch) | |
tree | cb42fc57f30847b2b38b19e3790e50ff140f5592 | |
parent | 694d0d0bb2030d2e36df73e2d23d5770511dbc8d (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.c | 53 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 2 |
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 | ||
1623 | static 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 | |||
1638 | static 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 | |||
1651 | static 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 | |||
1660 | static 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 | |||
1670 | static 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 | |||
1622 | static int param_set_event_clearing(const char *val, struct kernel_param *kp) | 1674 | static 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 | ||
1669 | static inline int acpi_ec_query_init(void) | 1722 | static 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 | ||
179 | extern struct acpi_ec *first_ec; | 181 | extern struct acpi_ec *first_ec; |