| From d4d4eda23794c701442e55129dd4f8f2fefd5e4d Mon Sep 17 00:00:00 2001 |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| Date: Mon, 9 Feb 2015 13:38:17 -0500 |
| Subject: cpufreq: speedstep-smi: enable interrupts when waiting |
| |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| |
| commit d4d4eda23794c701442e55129dd4f8f2fefd5e4d upstream. |
| |
| On Dell Latitude C600 laptop with Pentium 3 850MHz processor, the |
| speedstep-smi driver sometimes loads and sometimes doesn't load with |
| "change to state X failed" message. |
| |
| The hardware sometimes refuses to change frequency and in this case, we |
| need to retry later. I found out that we need to enable interrupts while |
| waiting. When we enable interrupts, the hardware blockage that prevents |
| frequency transition resolves and the transition is possible. With |
| disabled interrupts, the blockage doesn't resolve (no matter how long do |
| we wait). The exact reasons for this hardware behavior are unknown. |
| |
| This patch enables interrupts in the function speedstep_set_state that can |
| be called with disabled interrupts. However, this function is called with |
| disabled interrupts only from speedstep_get_freqs, so it shouldn't cause |
| any problem. |
| |
| Signed-off-by: Mikulas Patocka <mpatocka@redhat.com |
| Acked-by: Viresh Kumar <viresh.kumar@linaro.org> |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/cpufreq/speedstep-lib.c | 3 +++ |
| drivers/cpufreq/speedstep-smi.c | 12 ++++++++++++ |
| 2 files changed, 15 insertions(+) |
| |
| --- a/drivers/cpufreq/speedstep-lib.c |
| +++ b/drivers/cpufreq/speedstep-lib.c |
| @@ -400,6 +400,7 @@ unsigned int speedstep_get_freqs(enum sp |
| |
| pr_debug("previous speed is %u\n", prev_speed); |
| |
| + preempt_disable(); |
| local_irq_save(flags); |
| |
| /* switch to low state */ |
| @@ -464,6 +465,8 @@ unsigned int speedstep_get_freqs(enum sp |
| |
| out: |
| local_irq_restore(flags); |
| + preempt_enable(); |
| + |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(speedstep_get_freqs); |
| --- a/drivers/cpufreq/speedstep-smi.c |
| +++ b/drivers/cpufreq/speedstep-smi.c |
| @@ -156,6 +156,7 @@ static void speedstep_set_state(unsigned |
| return; |
| |
| /* Disable IRQs */ |
| + preempt_disable(); |
| local_irq_save(flags); |
| |
| command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); |
| @@ -166,9 +167,19 @@ static void speedstep_set_state(unsigned |
| |
| do { |
| if (retry) { |
| + /* |
| + * We need to enable interrupts, otherwise the blockage |
| + * won't resolve. |
| + * |
| + * We disable preemption so that other processes don't |
| + * run. If other processes were running, they could |
| + * submit more DMA requests, making the blockage worse. |
| + */ |
| pr_debug("retry %u, previous result %u, waiting...\n", |
| retry, result); |
| + local_irq_enable(); |
| mdelay(retry * 50); |
| + local_irq_disable(); |
| } |
| retry++; |
| __asm__ __volatile__( |
| @@ -185,6 +196,7 @@ static void speedstep_set_state(unsigned |
| |
| /* enable IRQs */ |
| local_irq_restore(flags); |
| + preempt_enable(); |
| |
| if (new_state == state) |
| pr_debug("change to %u MHz succeeded after %u tries " |