// SPDX-License-Identifier: GPL-2.0-only /* * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium * M (part of the Centrino chipset). * * Since the original Pentium M, most new Intel CPUs support Enhanced * SpeedStep. * * Despite the "SpeedStep" in the name, this is almost entirely unlike * traditional SpeedStep. * * Modelled on speedstep.c * * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org>
*/
/* Operating points for current CPU */ static DEFINE_PER_CPU(struct cpu_model *, centrino_model); static DEFINE_PER_CPU(conststruct cpu_id *, centrino_cpu);
staticstruct cpufreq_driver centrino_driver;
#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE
/* Computes the correct form for IA32_PERF_CTL MSR for a particular frequency/voltage operating point; frequency in MHz, volts in mV.
This is stored as "driver_data" in the structure. */ #define OP(mhz, mv) \
{ \
.frequency = (mhz) * 1000, \
.driver_data = (((mhz)/100) << 8) | ((mv - 700) / 16) \
}
/* * These voltage tables were derived from the Intel Pentium M * datasheet, document 25261202.pdf, Table 5. I have verified they * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium * M.
*/
if (model->cpu_id == NULL) { /* No match at all */
pr_debug("no support for CPU model \"%s\": " "send /proc/cpuinfo to " MAINTAINER "\n",
cpu->x86_model_id); return -ENOENT;
}
if (model->op_points == NULL) { /* Matched a non-match */
pr_debug("no table support for CPU model \"%s\"\n",
cpu->x86_model_id);
pr_debug("try using the acpi-cpufreq driver\n"); return -ENOENT;
}
per_cpu(centrino_model, policy->cpu) = model;
pr_debug("found \"%s\": max frequency: %dkHz\n",
model->model_name, model->max_freq);
/* To be called only after centrino_model is initialized */ staticunsigned extract_clock(unsigned msr, unsignedint cpu, int failsafe)
{ int i;
/* * Extract clock in kHz from PERF_CTL value * for centrino, as some DSDTs are buggy. * Ideally, this can be done using the acpi_data structure.
*/ if ((per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_BANIAS]) ||
(per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_DOTHAN_A1]) ||
(per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_DOTHAN_B0])) {
msr = (msr >> 8) & 0xff; return msr * 100000;
}
if ((!per_cpu(centrino_model, cpu)) ||
(!per_cpu(centrino_model, cpu)->op_points)) return 0;
msr &= 0xffff; for (i = 0;
per_cpu(centrino_model, cpu)->op_points[i].frequency
!= CPUFREQ_TABLE_END;
i++) { if (msr == per_cpu(centrino_model, cpu)->op_points[i].driver_data) return per_cpu(centrino_model, cpu)->
op_points[i].frequency;
} if (failsafe) return per_cpu(centrino_model, cpu)->op_points[i-1].frequency; else return 0;
}
/* Return the current CPU frequency in kHz */ staticunsignedint get_cur_freq(unsignedint cpu)
{ unsigned l, h; unsigned clock_freq;
if (unlikely(clock_freq == 0)) { /* * On some CPUs, we can see transient MSR values (which are * not present in _PSS), while CPU is doing some automatic * P-state transition (like TM2). Get the last freq set * in PERF_CTL.
*/
rdmsr_on_cpu(cpu, MSR_IA32_PERF_CTL, &l, &h);
clock_freq = extract_clock(l, cpu, 1);
} return clock_freq;
}
/* Only Intel makes Enhanced Speedstep-capable CPUs */ if (cpu->x86_vendor != X86_VENDOR_INTEL ||
!cpu_has(cpu, X86_FEATURE_EST)) return -ENODEV;
if (cpu_has(cpu, X86_FEATURE_CONSTANT_TSC))
centrino_driver.flags |= CPUFREQ_CONST_LOOPS;
if (policy->cpu != 0) return -ENODEV;
for (i = 0; i < N_IDS; i++) if (centrino_verify_cpu_id(cpu, &cpu_ids[i])) break;
if (i != N_IDS)
per_cpu(centrino_cpu, policy->cpu) = &cpu_ids[i];
if (!per_cpu(centrino_cpu, policy->cpu)) {
pr_debug("found unsupported CPU with " "Enhanced SpeedStep: send /proc/cpuinfo to "
MAINTAINER "\n"); return -ENODEV;
}
if (centrino_cpu_init_table(policy)) return -ENODEV;
/* Check to see if Enhanced SpeedStep is enabled, and try to
enable it if not. */
rdmsr(MSR_IA32_MISC_ENABLE, l, h);
if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
l |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
pr_debug("trying to enable Enhanced SpeedStep (%x)\n", l);
wrmsr(MSR_IA32_MISC_ENABLE, l, h);
/* check to see if it stuck */
rdmsr(MSR_IA32_MISC_ENABLE, l, h); if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
pr_info("couldn't enable Enhanced SpeedStep\n"); return -ENODEV;
}
}
staticvoid centrino_cpu_exit(struct cpufreq_policy *policy)
{ unsignedint cpu = policy->cpu;
if (per_cpu(centrino_model, cpu))
per_cpu(centrino_model, cpu) = NULL;
}
/** * centrino_target - set a new CPUFreq policy * @policy: new policy * @index: index of target frequency * * Sets a new CPUFreq policy.
*/ staticint centrino_target(struct cpufreq_policy *policy, unsignedint index)
{ unsignedint msr, oldmsr = 0, h = 0, cpu = policy->cpu; int retval = 0; unsignedint j, first_cpu; struct cpufreq_frequency_table *op_points;
cpumask_var_t covered_cpus;
if (unlikely(!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))) return -ENOMEM;
/* * Support for SMP systems. * Make sure we are running on CPU that wants to change freq
*/ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
good_cpu = cpumask_any_and(policy->cpus,
cpu_online_mask); else
good_cpu = j;
if (good_cpu >= nr_cpu_ids) {
pr_debug("couldn't limit to CPUs in this domain\n");
retval = -EAGAIN; if (first_cpu) { /* We haven't started the transition yet. */ goto out;
} break;
}
msr = op_points->driver_data;
if (first_cpu) {
rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h); if (msr == (oldmsr & 0xffff)) {
pr_debug("no change needed - msr was and needs " "to be %x\n", oldmsr);
retval = 0; goto out;
}
first_cpu = 0; /* all but 16 LSB are reserved, treat them with care */
oldmsr &= ~0xffff;
msr &= 0xffff;
oldmsr |= msr;
}
wrmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, oldmsr, h); if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) break;
cpumask_set_cpu(j, covered_cpus);
}
if (unlikely(retval)) { /* * We have failed halfway through the frequency change. * We have sent callbacks to policy->cpus and * MSRs have already been written on coverd_cpus. * Best effort undo..
*/
staticstruct cpufreq_driver centrino_driver = {
.name = "centrino", /* should be speedstep-centrino,
but there's a 16 char limit */
.init = centrino_cpu_init,
.exit = centrino_cpu_exit,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = centrino_target,
.get = get_cur_freq,
};
/* * This doesn't replace the detailed checks above because * the generic CPU IDs don't have a way to match for steppings * or ASCII model IDs.
*/ staticconststruct x86_cpu_id centrino_ids[] = {
X86_MATCH_VFM_FEATURE(IFM( 6, 9), X86_FEATURE_EST, NULL),
X86_MATCH_VFM_FEATURE(IFM( 6, 13), X86_FEATURE_EST, NULL),
X86_MATCH_VFM_FEATURE(IFM(15, 3), X86_FEATURE_EST, NULL),
X86_MATCH_VFM_FEATURE(IFM(15, 4), X86_FEATURE_EST, NULL),
{}
};
/** * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver * * Initializes the Enhanced SpeedStep support. Returns -ENODEV on * unsupported devices, -ENOENT if there's no voltage table for this * particular CPU model, -EINVAL on problems during initiatization, * and zero on success. * * This is quite picky. Not only does the CPU have to advertise the * "est" flag in the cpuid capability flags, we look for a specific * CPU model and stepping, and we need to have the exact model name in * our voltage tables. That is, be paranoid about not releasing * someone's valuable magic smoke.
*/ staticint __init centrino_init(void)
{ if (!x86_match_cpu(centrino_ids)) return -ENODEV; return cpufreq_register_driver(¢rino_driver);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.