Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab1b1c4e authored by Viresh Kumar's avatar Viresh Kumar Committed by Rafael J. Wysocki
Browse files

cpufreq: send new set of notification for transition failures



In the current code, if we fail during a frequency transition, we
simply send the POSTCHANGE notification with the old frequency. This
isn't enough.

One of the core users of these notifications is the code responsible
for keeping loops_per_jiffy aligned with frequency changes. And mostly
it is written as:

	if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||
	    (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) {
		update-loops-per-jiffy...
	}

So, suppose we are changing to a higher frequency and failed during
transition, then following will happen:
- CPUFREQ_PRECHANGE notification with freq-new > freq-old
- CPUFREQ_POSTCHANGE notification with freq-new == freq-old

The first one will update loops_per_jiffy and second one will do
nothing. Even if we send the 2nd notification by exchanging values of
freq-new and old, some users of these notifications might get
unstable.

This can be fixed by simply calling cpufreq_notify_post_transition()
with error code and this routine will take care of sending
notifications in the correct order.

Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
[rjw: Folded 3 patches into one, rebased unicore2 changes]
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent f7ba3b41
Loading
Loading
Loading
Loading
+2 −11
Original line number Original line Diff line number Diff line
@@ -1739,17 +1739,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
			pr_err("%s: Failed to change cpu frequency: %d\n",
			pr_err("%s: Failed to change cpu frequency: %d\n",
					__func__, retval);
					__func__, retval);


		if (notify) {
		if (notify)
			/*
			cpufreq_notify_post_transition(policy, &freqs, retval);
			 * Notify with old freq in case we failed to change
			 * frequency
			 */
			if (retval)
				freqs.new = freqs.old;

			cpufreq_notify_transition(policy, &freqs,
					CPUFREQ_POSTCHANGE);
		}
	}
	}


out:
out:
+7 −11
Original line number Original line Diff line number Diff line
@@ -213,6 +213,7 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy,
		cpu, target_freq,
		cpu, target_freq,
		(pcch_virt_addr + pcc_cpu_data->input_offset));
		(pcch_virt_addr + pcc_cpu_data->input_offset));


	freqs.old = policy->cur;
	freqs.new = target_freq;
	freqs.new = target_freq;
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);


@@ -228,25 +229,20 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy,
	memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
	memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);


	status = ioread16(&pcch_hdr->status);
	status = ioread16(&pcch_hdr->status);
	iowrite16(0, &pcch_hdr->status);

	cpufreq_notify_post_transition(policy, &freqs, status != CMD_COMPLETE);
	spin_unlock(&pcc_lock);

	if (status != CMD_COMPLETE) {
	if (status != CMD_COMPLETE) {
		pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",
		pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",
			cpu, status);
			cpu, status);
		goto cmd_incomplete;
		return -EINVAL;
	}
	}
	iowrite16(0, &pcch_hdr->status);


	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
	pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);
	pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);
	spin_unlock(&pcc_lock);


	return 0;
	return 0;

cmd_incomplete:
	freqs.new = freqs.old;
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
	iowrite16(0, &pcch_hdr->status);
	spin_unlock(&pcc_lock);
	return -EINVAL;
}
}


static int pcc_get_offset(int cpu)
static int pcc_get_offset(int cpu)
+1 −6
Original line number Original line Diff line number Diff line
@@ -964,14 +964,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
	cpufreq_cpu_put(policy);
	cpufreq_cpu_put(policy);


	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);

	res = transition_fid_vid(data, fid, vid);
	res = transition_fid_vid(data, fid, vid);
	if (res)
	cpufreq_notify_post_transition(policy, &freqs, res);
		freqs.new = freqs.old;
	else
		freqs.new = find_khz_freq_from_fid(data->currfid);


	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
	return res;
	return res;
}
}


+7 −9
Original line number Original line Diff line number Diff line
@@ -46,20 +46,18 @@ static int ucv2_target(struct cpufreq_policy *policy,
			 unsigned int target_freq,
			 unsigned int target_freq,
			 unsigned int relation)
			 unsigned int relation)
{
{
	unsigned int cur = ucv2_getspeed(0);
	struct cpufreq_freqs freqs;
	struct cpufreq_freqs freqs;
	struct clk *mclk = clk_get(NULL, "MAIN_CLK");
	struct clk *mclk = clk_get(NULL, "MAIN_CLK");
	int ret;


	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
	freqs.old = policy->cur;

	if (!clk_set_rate(mclk, target_freq * 1000)) {
		freqs.old = cur;
	freqs.new = target_freq;
	freqs.new = target_freq;
	}


	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
	ret = clk_set_rate(mclk, target_freq * 1000);
	cpufreq_notify_post_transition(policy, &freqs, ret);


	return 0;
	return ret;
}
}


static int __init ucv2_cpu_init(struct cpufreq_policy *policy)
static int __init ucv2_cpu_init(struct cpufreq_policy *policy)