|
@@ -1,3 +1,4 @@
|
|
|
+
|
|
|
/*
|
|
|
* (c) 2003-2006 Advanced Micro Devices, Inc.
|
|
|
* Your use of this code is subject to the terms and conditions of the
|
|
@@ -117,20 +118,17 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
|
|
|
u32 i = 0;
|
|
|
|
|
|
if (cpu_family == CPU_HW_PSTATE) {
|
|
|
- if (data->currpstate == HW_PSTATE_INVALID) {
|
|
|
- /* read (initial) hw pstate if not yet set */
|
|
|
- rdmsr(MSR_PSTATE_STATUS, lo, hi);
|
|
|
- i = lo & HW_PSTATE_MASK;
|
|
|
-
|
|
|
- /*
|
|
|
- * a workaround for family 11h erratum 311 might cause
|
|
|
- * an "out-of-range Pstate if the core is in Pstate-0
|
|
|
- */
|
|
|
- if (i >= data->numps)
|
|
|
- data->currpstate = HW_PSTATE_0;
|
|
|
- else
|
|
|
- data->currpstate = i;
|
|
|
- }
|
|
|
+ rdmsr(MSR_PSTATE_STATUS, lo, hi);
|
|
|
+ i = lo & HW_PSTATE_MASK;
|
|
|
+ data->currpstate = i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * a workaround for family 11h erratum 311 might cause
|
|
|
+ * an "out-of-range Pstate if the core is in Pstate-0
|
|
|
+ */
|
|
|
+ if ((boot_cpu_data.x86 == 0x11) && (i >= data->numps))
|
|
|
+ data->currpstate = HW_PSTATE_0;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
do {
|
|
@@ -510,41 +508,34 @@ static int core_voltage_post_transition(struct powernow_k8_data *data,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int check_supported_cpu(unsigned int cpu)
|
|
|
+static void check_supported_cpu(void *_rc)
|
|
|
{
|
|
|
- cpumask_t oldmask;
|
|
|
u32 eax, ebx, ecx, edx;
|
|
|
- unsigned int rc = 0;
|
|
|
-
|
|
|
- oldmask = current->cpus_allowed;
|
|
|
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
|
|
|
+ int *rc = _rc;
|
|
|
|
|
|
- if (smp_processor_id() != cpu) {
|
|
|
- printk(KERN_ERR PFX "limiting to cpu %u failed\n", cpu);
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ *rc = -ENODEV;
|
|
|
|
|
|
if (current_cpu_data.x86_vendor != X86_VENDOR_AMD)
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
|
|
|
eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
|
|
|
if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) &&
|
|
|
((eax & CPUID_XFAM) < CPUID_XFAM_10H))
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
|
|
|
if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
|
|
|
if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
|
|
|
((eax & CPUID_XMOD) > CPUID_XMOD_REV_MASK)) {
|
|
|
printk(KERN_INFO PFX
|
|
|
"Processor cpuid %x not supported\n", eax);
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
|
|
|
if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
|
|
|
printk(KERN_INFO PFX
|
|
|
"No frequency change capabilities detected\n");
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
|
|
@@ -552,21 +543,17 @@ static int check_supported_cpu(unsigned int cpu)
|
|
|
!= P_STATE_TRANSITION_CAPABLE) {
|
|
|
printk(KERN_INFO PFX
|
|
|
"Power state transitions not supported\n");
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
}
|
|
|
} else { /* must be a HW Pstate capable processor */
|
|
|
cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
|
|
|
if ((edx & USE_HW_PSTATE) == USE_HW_PSTATE)
|
|
|
cpu_family = CPU_HW_PSTATE;
|
|
|
else
|
|
|
- goto out;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- rc = 1;
|
|
|
-
|
|
|
-out:
|
|
|
- set_cpus_allowed_ptr(current, &oldmask);
|
|
|
- return rc;
|
|
|
+ *rc = 0;
|
|
|
}
|
|
|
|
|
|
static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
|
|
@@ -823,13 +810,14 @@ static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data,
|
|
|
if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE))
|
|
|
return;
|
|
|
|
|
|
- control = data->acpi_data.states[index].control; data->irt = (control
|
|
|
- >> IRT_SHIFT) & IRT_MASK; data->rvo = (control >>
|
|
|
- RVO_SHIFT) & RVO_MASK; data->exttype = (control
|
|
|
- >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
|
|
|
- data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK; data->vidmvs = 1
|
|
|
- << ((control >> MVS_SHIFT) & MVS_MASK); data->vstable =
|
|
|
- (control >> VST_SHIFT) & VST_MASK; }
|
|
|
+ control = data->acpi_data.states[index].control;
|
|
|
+ data->irt = (control >> IRT_SHIFT) & IRT_MASK;
|
|
|
+ data->rvo = (control >> RVO_SHIFT) & RVO_MASK;
|
|
|
+ data->exttype = (control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
|
|
|
+ data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK;
|
|
|
+ data->vidmvs = 1 << ((control >> MVS_SHIFT) & MVS_MASK);
|
|
|
+ data->vstable = (control >> VST_SHIFT) & VST_MASK;
|
|
|
+}
|
|
|
|
|
|
static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
|
|
|
{
|
|
@@ -1046,6 +1034,19 @@ static int get_transition_latency(struct powernow_k8_data *data)
|
|
|
if (cur_latency > max_latency)
|
|
|
max_latency = cur_latency;
|
|
|
}
|
|
|
+ if (max_latency == 0) {
|
|
|
+ /*
|
|
|
+ * Fam 11h always returns 0 as transition latency.
|
|
|
+ * This is intended and means "very fast". While cpufreq core
|
|
|
+ * and governors currently can handle that gracefully, better
|
|
|
+ * set it to 1 to avoid problems in the future.
|
|
|
+ * For all others it's a BIOS bug.
|
|
|
+ */
|
|
|
+ if (!boot_cpu_data.x86 == 0x11)
|
|
|
+ printk(KERN_ERR FW_WARN PFX "Invalid zero transition "
|
|
|
+ "latency\n");
|
|
|
+ max_latency = 1;
|
|
|
+ }
|
|
|
/* value in usecs, needs to be in nanoseconds */
|
|
|
return 1000 * max_latency;
|
|
|
}
|
|
@@ -1093,7 +1094,7 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
|
|
|
freqs.old = find_khz_freq_from_fid(data->currfid);
|
|
|
freqs.new = find_khz_freq_from_fid(fid);
|
|
|
|
|
|
- for_each_cpu_mask_nr(i, *(data->available_cores)) {
|
|
|
+ for_each_cpu(i, data->available_cores) {
|
|
|
freqs.cpu = i;
|
|
|
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
|
|
}
|
|
@@ -1101,7 +1102,7 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
|
|
|
res = transition_fid_vid(data, fid, vid);
|
|
|
freqs.new = find_khz_freq_from_fid(data->currfid);
|
|
|
|
|
|
- for_each_cpu_mask_nr(i, *(data->available_cores)) {
|
|
|
+ for_each_cpu(i, data->available_cores) {
|
|
|
freqs.cpu = i;
|
|
|
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
|
|
}
|
|
@@ -1126,7 +1127,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
|
|
|
data->currpstate);
|
|
|
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
|
|
|
|
|
|
- for_each_cpu_mask_nr(i, *(data->available_cores)) {
|
|
|
+ for_each_cpu(i, data->available_cores) {
|
|
|
freqs.cpu = i;
|
|
|
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
|
|
}
|
|
@@ -1134,7 +1135,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
|
|
|
res = transition_pstate(data, pstate);
|
|
|
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
|
|
|
|
|
|
- for_each_cpu_mask_nr(i, *(data->available_cores)) {
|
|
|
+ for_each_cpu(i, data->available_cores) {
|
|
|
freqs.cpu = i;
|
|
|
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
|
|
}
|
|
@@ -1235,21 +1236,47 @@ static int powernowk8_verify(struct cpufreq_policy *pol)
|
|
|
return cpufreq_frequency_table_verify(pol, data->powernow_table);
|
|
|
}
|
|
|
|
|
|
-static const char ACPI_PSS_BIOS_BUG_MSG[] =
|
|
|
- KERN_ERR FW_BUG PFX "No compatible ACPI _PSS objects found.\n"
|
|
|
- KERN_ERR FW_BUG PFX "Try again with latest BIOS.\n";
|
|
|
+struct init_on_cpu {
|
|
|
+ struct powernow_k8_data *data;
|
|
|
+ int rc;
|
|
|
+};
|
|
|
+
|
|
|
+static void __cpuinit powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
|
|
|
+{
|
|
|
+ struct init_on_cpu *init_on_cpu = _init_on_cpu;
|
|
|
+
|
|
|
+ if (pending_bit_stuck()) {
|
|
|
+ printk(KERN_ERR PFX "failing init, change pending bit set\n");
|
|
|
+ init_on_cpu->rc = -ENODEV;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (query_current_values_with_pending_wait(init_on_cpu->data)) {
|
|
|
+ init_on_cpu->rc = -ENODEV;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cpu_family == CPU_OPTERON)
|
|
|
+ fidvid_msr_init();
|
|
|
+
|
|
|
+ init_on_cpu->rc = 0;
|
|
|
+}
|
|
|
|
|
|
/* per CPU init entry point to the driver */
|
|
|
static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|
|
{
|
|
|
+ static const char ACPI_PSS_BIOS_BUG_MSG[] =
|
|
|
+ KERN_ERR FW_BUG PFX "No compatible ACPI _PSS objects found.\n"
|
|
|
+ KERN_ERR FW_BUG PFX "Try again with latest BIOS.\n";
|
|
|
struct powernow_k8_data *data;
|
|
|
- cpumask_t oldmask;
|
|
|
+ struct init_on_cpu init_on_cpu;
|
|
|
int rc;
|
|
|
|
|
|
if (!cpu_online(pol->cpu))
|
|
|
return -ENODEV;
|
|
|
|
|
|
- if (!check_supported_cpu(pol->cpu))
|
|
|
+ smp_call_function_single(pol->cpu, check_supported_cpu, &rc, 1);
|
|
|
+ if (rc)
|
|
|
return -ENODEV;
|
|
|
|
|
|
data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
|
|
@@ -1289,27 +1316,12 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|
|
pol->cpuinfo.transition_latency = get_transition_latency(data);
|
|
|
|
|
|
/* only run on specific CPU from here on */
|
|
|
- oldmask = current->cpus_allowed;
|
|
|
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(pol->cpu));
|
|
|
-
|
|
|
- if (smp_processor_id() != pol->cpu) {
|
|
|
- printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
|
|
|
- goto err_out_unmask;
|
|
|
- }
|
|
|
-
|
|
|
- if (pending_bit_stuck()) {
|
|
|
- printk(KERN_ERR PFX "failing init, change pending bit set\n");
|
|
|
- goto err_out_unmask;
|
|
|
- }
|
|
|
-
|
|
|
- if (query_current_values_with_pending_wait(data))
|
|
|
- goto err_out_unmask;
|
|
|
-
|
|
|
- if (cpu_family == CPU_OPTERON)
|
|
|
- fidvid_msr_init();
|
|
|
-
|
|
|
- /* run on any CPU again */
|
|
|
- set_cpus_allowed_ptr(current, &oldmask);
|
|
|
+ init_on_cpu.data = data;
|
|
|
+ smp_call_function_single(data->cpu, powernowk8_cpu_init_on_cpu,
|
|
|
+ &init_on_cpu, 1);
|
|
|
+ rc = init_on_cpu.rc;
|
|
|
+ if (rc != 0)
|
|
|
+ goto err_out_exit_acpi;
|
|
|
|
|
|
if (cpu_family == CPU_HW_PSTATE)
|
|
|
cpumask_copy(pol->cpus, cpumask_of(pol->cpu));
|
|
@@ -1346,8 +1358,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-err_out_unmask:
|
|
|
- set_cpus_allowed_ptr(current, &oldmask);
|
|
|
+err_out_exit_acpi:
|
|
|
powernow_k8_cpu_exit_acpi(data);
|
|
|
|
|
|
err_out:
|
|
@@ -1372,28 +1383,25 @@ static int __devexit powernowk8_cpu_exit(struct cpufreq_policy *pol)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void query_values_on_cpu(void *_err)
|
|
|
+{
|
|
|
+ int *err = _err;
|
|
|
+ struct powernow_k8_data *data = __get_cpu_var(powernow_data);
|
|
|
+
|
|
|
+ *err = query_current_values_with_pending_wait(data);
|
|
|
+}
|
|
|
+
|
|
|
static unsigned int powernowk8_get(unsigned int cpu)
|
|
|
{
|
|
|
- struct powernow_k8_data *data;
|
|
|
- cpumask_t oldmask = current->cpus_allowed;
|
|
|
+ struct powernow_k8_data *data = per_cpu(powernow_data, cpu);
|
|
|
unsigned int khz = 0;
|
|
|
- unsigned int first;
|
|
|
-
|
|
|
- first = cpumask_first(cpu_core_mask(cpu));
|
|
|
- data = per_cpu(powernow_data, first);
|
|
|
+ int err;
|
|
|
|
|
|
if (!data)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
|
|
|
- if (smp_processor_id() != cpu) {
|
|
|
- printk(KERN_ERR PFX
|
|
|
- "limiting to CPU %d failed in powernowk8_get\n", cpu);
|
|
|
- set_cpus_allowed_ptr(current, &oldmask);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- if (query_current_values_with_pending_wait(data))
|
|
|
+ smp_call_function_single(cpu, query_values_on_cpu, &err, true);
|
|
|
+ if (err)
|
|
|
goto out;
|
|
|
|
|
|
if (cpu_family == CPU_HW_PSTATE)
|
|
@@ -1404,7 +1412,6 @@ static unsigned int powernowk8_get(unsigned int cpu)
|
|
|
|
|
|
|
|
|
out:
|
|
|
- set_cpus_allowed_ptr(current, &oldmask);
|
|
|
return khz;
|
|
|
}
|
|
|
|
|
@@ -1430,7 +1437,9 @@ static int __cpuinit powernowk8_init(void)
|
|
|
unsigned int i, supported_cpus = 0;
|
|
|
|
|
|
for_each_online_cpu(i) {
|
|
|
- if (check_supported_cpu(i))
|
|
|
+ int rc;
|
|
|
+ smp_call_function_single(i, check_supported_cpu, &rc, 1);
|
|
|
+ if (rc == 0)
|
|
|
supported_cpus++;
|
|
|
}
|
|
|
|