Commit 7aa0557f authored by Srivatsa S. Bhat's avatar Srivatsa S. Bhat Committed by Rafael J. Wysocki

cpufreq: longhaul: Fix double invocation of cpufreq_freq_transition_begin/end

During frequency transitions, the cpufreq core takes the responsibility of
invoking cpufreq_freq_transition_begin() and cpufreq_freq_transition_end()
for those cpufreq drivers that define the ->target_index callback but don't
set the ASYNC_NOTIFICATION flag.

The longhaul cpufreq driver falls under this category, but this driver was
invoking the _begin() and _end() APIs itself around frequency transitions,
which led to double invocation of the _begin() API. The _begin API makes
contending callers wait until the previous invocation is complete. Hence,
the longhaul driver ended up waiting on itself, leading to system hangs
during boot.

Fix this by removing the calls to the _begin() and _end() APIs from the
longhaul driver, since they rightly belong to the cpufreq core.

(Note that during module_exit(), the longhaul driver sets the frequency
 without any help from the cpufreq core. So add explicit calls to the
 _begin() and _end() APIs around that frequency transition alone, to take
 care of that special case.)

Fixes: 12478cf0 (cpufreq: Make sure frequency transitions are serialized)
Reported-and-tested-by: default avatarMeelis Roos <mroos@linux.ee>
Signed-off-by: default avatarSrivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent d1db0eea
...@@ -242,7 +242,7 @@ static void do_powersaver(int cx_address, unsigned int mults_index, ...@@ -242,7 +242,7 @@ static void do_powersaver(int cx_address, unsigned int mults_index,
* Sets a new clock ratio. * Sets a new clock ratio.
*/ */
static void longhaul_setstate(struct cpufreq_policy *policy, static int longhaul_setstate(struct cpufreq_policy *policy,
unsigned int table_index) unsigned int table_index)
{ {
unsigned int mults_index; unsigned int mults_index;
...@@ -258,10 +258,12 @@ static void longhaul_setstate(struct cpufreq_policy *policy, ...@@ -258,10 +258,12 @@ static void longhaul_setstate(struct cpufreq_policy *policy,
/* Safety precautions */ /* Safety precautions */
mult = mults[mults_index & 0x1f]; mult = mults[mults_index & 0x1f];
if (mult == -1) if (mult == -1)
return; return -EINVAL;
speed = calc_speed(mult); speed = calc_speed(mult);
if ((speed > highest_speed) || (speed < lowest_speed)) if ((speed > highest_speed) || (speed < lowest_speed))
return; return -EINVAL;
/* Voltage transition before frequency transition? */ /* Voltage transition before frequency transition? */
if (can_scale_voltage && longhaul_index < table_index) if (can_scale_voltage && longhaul_index < table_index)
dir = 1; dir = 1;
...@@ -269,8 +271,6 @@ static void longhaul_setstate(struct cpufreq_policy *policy, ...@@ -269,8 +271,6 @@ static void longhaul_setstate(struct cpufreq_policy *policy,
freqs.old = calc_speed(longhaul_get_cpu_mult()); freqs.old = calc_speed(longhaul_get_cpu_mult());
freqs.new = speed; freqs.new = speed;
cpufreq_freq_transition_begin(policy, &freqs);
pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
fsb, mult/10, mult%10, print_speed(speed/1000)); fsb, mult/10, mult%10, print_speed(speed/1000));
retry_loop: retry_loop:
...@@ -385,12 +385,14 @@ static void longhaul_setstate(struct cpufreq_policy *policy, ...@@ -385,12 +385,14 @@ static void longhaul_setstate(struct cpufreq_policy *policy,
goto retry_loop; goto retry_loop;
} }
} }
/* Report true CPU frequency */
cpufreq_freq_transition_end(policy, &freqs, 0);
if (!bm_timeout) if (!bm_timeout) {
printk(KERN_INFO PFX "Warning: Timeout while waiting for " printk(KERN_INFO PFX "Warning: Timeout while waiting for "
"idle PCI bus.\n"); "idle PCI bus.\n");
return -EBUSY;
}
return 0;
} }
/* /*
...@@ -631,9 +633,10 @@ static int longhaul_target(struct cpufreq_policy *policy, ...@@ -631,9 +633,10 @@ static int longhaul_target(struct cpufreq_policy *policy,
unsigned int i; unsigned int i;
unsigned int dir = 0; unsigned int dir = 0;
u8 vid, current_vid; u8 vid, current_vid;
int retval = 0;
if (!can_scale_voltage) if (!can_scale_voltage)
longhaul_setstate(policy, table_index); retval = longhaul_setstate(policy, table_index);
else { else {
/* On test system voltage transitions exceeding single /* On test system voltage transitions exceeding single
* step up or down were turning motherboard off. Both * step up or down were turning motherboard off. Both
...@@ -648,7 +651,7 @@ static int longhaul_target(struct cpufreq_policy *policy, ...@@ -648,7 +651,7 @@ static int longhaul_target(struct cpufreq_policy *policy,
while (i != table_index) { while (i != table_index) {
vid = (longhaul_table[i].driver_data >> 8) & 0x1f; vid = (longhaul_table[i].driver_data >> 8) & 0x1f;
if (vid != current_vid) { if (vid != current_vid) {
longhaul_setstate(policy, i); retval = longhaul_setstate(policy, i);
current_vid = vid; current_vid = vid;
msleep(200); msleep(200);
} }
...@@ -657,10 +660,11 @@ static int longhaul_target(struct cpufreq_policy *policy, ...@@ -657,10 +660,11 @@ static int longhaul_target(struct cpufreq_policy *policy,
else else
i--; i--;
} }
longhaul_setstate(policy, table_index); retval = longhaul_setstate(policy, table_index);
} }
longhaul_index = table_index; longhaul_index = table_index;
return 0; return retval;
} }
...@@ -968,7 +972,15 @@ static void __exit longhaul_exit(void) ...@@ -968,7 +972,15 @@ static void __exit longhaul_exit(void)
for (i = 0; i < numscales; i++) { for (i = 0; i < numscales; i++) {
if (mults[i] == maxmult) { if (mults[i] == maxmult) {
struct cpufreq_freqs freqs;
freqs.old = policy->cur;
freqs.new = longhaul_table[i].frequency;
freqs.flags = 0;
cpufreq_freq_transition_begin(policy, &freqs);
longhaul_setstate(policy, i); longhaul_setstate(policy, i);
cpufreq_freq_transition_end(policy, &freqs, 0);
break; break;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment