|
@@ -35,7 +35,6 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/cpumask.h>
|
|
|
-#include <linux/sched.h> /* for current / set_cpus_allowed() */
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/delay.h>
|
|
|
|
|
@@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
-/* Driver entry point to switch to the target frequency */
|
|
|
-static int powernowk8_target(struct cpufreq_policy *pol,
|
|
|
- unsigned targfreq, unsigned relation)
|
|
|
+struct powernowk8_target_arg {
|
|
|
+ struct cpufreq_policy *pol;
|
|
|
+ unsigned targfreq;
|
|
|
+ unsigned relation;
|
|
|
+};
|
|
|
+
|
|
|
+static long powernowk8_target_fn(void *arg)
|
|
|
{
|
|
|
- cpumask_var_t oldmask;
|
|
|
+ struct powernowk8_target_arg *pta = arg;
|
|
|
+ struct cpufreq_policy *pol = pta->pol;
|
|
|
+ unsigned targfreq = pta->targfreq;
|
|
|
+ unsigned relation = pta->relation;
|
|
|
struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
|
|
|
u32 checkfid;
|
|
|
u32 checkvid;
|
|
|
unsigned int newstate;
|
|
|
- int ret = -EIO;
|
|
|
+ int ret;
|
|
|
|
|
|
if (!data)
|
|
|
return -EINVAL;
|
|
@@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|
|
checkfid = data->currfid;
|
|
|
checkvid = data->currvid;
|
|
|
|
|
|
- /* only run on specific CPU from here on. */
|
|
|
- /* This is poor form: use a workqueue or smp_call_function_single */
|
|
|
- if (!alloc_cpumask_var(&oldmask, GFP_KERNEL))
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- cpumask_copy(oldmask, tsk_cpus_allowed(current));
|
|
|
- set_cpus_allowed_ptr(current, cpumask_of(pol->cpu));
|
|
|
-
|
|
|
- if (smp_processor_id() != pol->cpu) {
|
|
|
- printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
|
|
|
- goto err_out;
|
|
|
- }
|
|
|
-
|
|
|
if (pending_bit_stuck()) {
|
|
|
printk(KERN_ERR PFX "failing targ, change pending bit set\n");
|
|
|
- goto err_out;
|
|
|
+ return -EIO;
|
|
|
}
|
|
|
|
|
|
pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
|
|
|
pol->cpu, targfreq, pol->min, pol->max, relation);
|
|
|
|
|
|
if (query_current_values_with_pending_wait(data))
|
|
|
- goto err_out;
|
|
|
+ return -EIO;
|
|
|
|
|
|
if (cpu_family != CPU_HW_PSTATE) {
|
|
|
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
|
|
@@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|
|
|
|
|
if (cpufreq_frequency_table_target(pol, data->powernow_table,
|
|
|
targfreq, relation, &newstate))
|
|
|
- goto err_out;
|
|
|
+ return -EIO;
|
|
|
|
|
|
mutex_lock(&fidvid_mutex);
|
|
|
|
|
@@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|
|
ret = transition_frequency_fidvid(data, newstate);
|
|
|
if (ret) {
|
|
|
printk(KERN_ERR PFX "transition frequency failed\n");
|
|
|
- ret = 1;
|
|
|
mutex_unlock(&fidvid_mutex);
|
|
|
- goto err_out;
|
|
|
+ return 1;
|
|
|
}
|
|
|
mutex_unlock(&fidvid_mutex);
|
|
|
|
|
@@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpufreq_policy *pol,
|
|
|
data->powernow_table[newstate].index);
|
|
|
else
|
|
|
pol->cur = find_khz_freq_from_fid(data->currfid);
|
|
|
- ret = 0;
|
|
|
|
|
|
-err_out:
|
|
|
- set_cpus_allowed_ptr(current, oldmask);
|
|
|
- free_cpumask_var(oldmask);
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Driver entry point to switch to the target frequency */
|
|
|
+static int powernowk8_target(struct cpufreq_policy *pol,
|
|
|
+ unsigned targfreq, unsigned relation)
|
|
|
+{
|
|
|
+ struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq,
|
|
|
+ .relation = relation };
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Must run on @pol->cpu. cpufreq core is responsible for ensuring
|
|
|
+ * that we're bound to the current CPU and pol->cpu stays online.
|
|
|
+ */
|
|
|
+ if (smp_processor_id() == pol->cpu)
|
|
|
+ return powernowk8_target_fn(&pta);
|
|
|
+ else
|
|
|
+ return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta);
|
|
|
}
|
|
|
|
|
|
/* Driver entry point to verify the policy and range of frequencies */
|