| From 91740fc8242b4f260cfa4d4536d8551804777fae Mon Sep 17 00:00:00 2001 |
| From: Kohji Okuno <okuno.kohji@jp.panasonic.com> |
| Date: Tue, 26 Feb 2019 11:34:13 +0900 |
| Subject: ARM: imx6q: cpuidle: fix bug that CPU might not wake up at expected time |
| |
| From: Kohji Okuno <okuno.kohji@jp.panasonic.com> |
| |
| commit 91740fc8242b4f260cfa4d4536d8551804777fae upstream. |
| |
| In the current cpuidle implementation for i.MX6q, the CPU that sets |
| 'WAIT_UNCLOCKED' and the CPU that returns to 'WAIT_CLOCKED' are always |
| the same. While the CPU that sets 'WAIT_UNCLOCKED' is in IDLE state of |
| "WAIT", if the other CPU wakes up and enters IDLE state of "WFI" |
| istead of "WAIT", this CPU can not wake up at expired time. |
| Because, in the case of "WFI", the CPU must be waked up by the local |
| timer interrupt. But, while 'WAIT_UNCLOCKED' is set, the local timer |
| is stopped, when all CPUs execute "wfi" instruction. As a result, the |
| local timer interrupt is not fired. |
| In this situation, this CPU will wake up by IRQ different from local |
| timer. (e.g. broacast timer) |
| |
| So, this fix changes CPU to return to 'WAIT_CLOCKED'. |
| |
| Signed-off-by: Kohji Okuno <okuno.kohji@jp.panasonic.com> |
| Fixes: e5f9dec8ff5f ("ARM: imx6q: support WAIT mode using cpuidle") |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Shawn Guo <shawnguo@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/arm/mach-imx/cpuidle-imx6q.c | 27 ++++++++++----------------- |
| 1 file changed, 10 insertions(+), 17 deletions(-) |
| |
| --- a/arch/arm/mach-imx/cpuidle-imx6q.c |
| +++ b/arch/arm/mach-imx/cpuidle-imx6q.c |
| @@ -16,30 +16,23 @@ |
| #include "cpuidle.h" |
| #include "hardware.h" |
| |
| -static atomic_t master = ATOMIC_INIT(0); |
| -static DEFINE_SPINLOCK(master_lock); |
| +static int num_idle_cpus = 0; |
| +static DEFINE_SPINLOCK(cpuidle_lock); |
| |
| static int imx6q_enter_wait(struct cpuidle_device *dev, |
| struct cpuidle_driver *drv, int index) |
| { |
| - if (atomic_inc_return(&master) == num_online_cpus()) { |
| - /* |
| - * With this lock, we prevent other cpu to exit and enter |
| - * this function again and become the master. |
| - */ |
| - if (!spin_trylock(&master_lock)) |
| - goto idle; |
| + spin_lock(&cpuidle_lock); |
| + if (++num_idle_cpus == num_online_cpus()) |
| imx6_set_lpm(WAIT_UNCLOCKED); |
| - cpu_do_idle(); |
| - imx6_set_lpm(WAIT_CLOCKED); |
| - spin_unlock(&master_lock); |
| - goto done; |
| - } |
| + spin_unlock(&cpuidle_lock); |
| |
| -idle: |
| cpu_do_idle(); |
| -done: |
| - atomic_dec(&master); |
| + |
| + spin_lock(&cpuidle_lock); |
| + if (num_idle_cpus-- == num_online_cpus()) |
| + imx6_set_lpm(WAIT_CLOCKED); |
| + spin_unlock(&cpuidle_lock); |
| |
| return index; |
| } |