/* * _PPC support is implemented as a CPUfreq policy notifier: * This means each time a CPUfreq driver registered also with * the ACPI core is asked to change the speed policy, the maximum * value is adjusted so that it is within the platform limit. * * Also, when a new platform limit value is detected, the CPUfreq * policy is adjusted accordingly.
*/
/* ignore_ppc: * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet * ignore _PPC * 0 -> cpufreq low level drivers initialized -> consider _PPC values * 1 -> ignore _PPC totally -> forced by user through boot param
*/ staticint ignore_ppc = -1;
module_param(ignore_ppc, int, 0644);
MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ "limited by BIOS, this should help");
staticbool acpi_processor_ppc_in_use;
staticint acpi_processor_get_platform_limit(struct acpi_processor *pr)
{
acpi_status status = 0; unsignedlonglong ppc = 0;
s32 qos_value; int index; int ret;
if (!pr) return -EINVAL;
/* * _PPC indicates the maximum state currently supported by the platform * (e.g. 0 = states 0..n; 1 = states 1..n; etc.
*/
status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc); if (status != AE_NOT_FOUND) {
acpi_processor_ppc_in_use = true;
if (ACPI_FAILURE(status)) {
acpi_evaluation_failure_warn(pr->handle, "_PPC", status); return -ENODEV;
}
}
index = ppc;
if (pr->performance_platform_limit == index ||
ppc >= pr->performance->state_count) return 0;
pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
index, index ? "is" : "is not");
pr->performance_platform_limit = index;
if (unlikely(!freq_qos_request_active(&pr->perflib_req))) return 0;
/* * If _PPC returns 0, it means that all of the available states can be * used ("no limit").
*/ if (index == 0)
qos_value = FREQ_QOS_MAX_DEFAULT_VALUE; else
qos_value = pr->performance->states[index].core_frequency * 1000;
ret = freq_qos_update_request(&pr->perflib_req, qos_value); if (ret < 0) {
pr_warn("Failed to update perflib freq constraint: CPU%d (%d)\n",
pr->id, ret);
}
return 0;
}
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 /* * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status * @handle: ACPI processor handle * @status: the status code of _PPC evaluation * 0: success. OSPM is now using the performance state specified. * 1: failure. OSPM has not changed the number of P-states in use
*/ staticvoid acpi_processor_ppc_ost(acpi_handle handle, int status)
{ if (acpi_has_method(handle, "_OST"))
acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE,
status, NULL);
}
void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
{ int ret;
if (ignore_ppc || !pr->performance) { /* * Only when it is notification event, the _OST object * will be evaluated. Otherwise it is skipped.
*/ if (event_flag)
acpi_processor_ppc_ost(pr->handle, 1); return;
}
ret = acpi_processor_get_platform_limit(pr); /* * Only when it is notification event, the _OST object * will be evaluated. Otherwise it is skipped.
*/ if (event_flag) { if (ret < 0)
acpi_processor_ppc_ost(pr->handle, 1); else
acpi_processor_ppc_ost(pr->handle, 0);
} if (ret >= 0)
cpufreq_update_limits(pr->id);
}
int acpi_processor_get_bios_limit(int cpu, unsignedint *limit)
{ struct acpi_processor *pr;
/* * Reset performance_platform_limit in case there is a stale * value in it, so as to make it match the "no limit" QoS value * below.
*/
pr->performance_platform_limit = 0;
ret = freq_qos_add_request(&policy->constraints,
&pr->perflib_req, FREQ_QOS_MAX,
FREQ_QOS_MAX_DEFAULT_VALUE); if (ret < 0)
pr_err("Failed to add freq constraint for CPU%d (%d)\n",
cpu, ret);
if (!pr->performance) continue;
ret = acpi_processor_get_platform_limit(pr); if (ret)
pr_err("Failed to update freq constraint for CPU%d (%d)\n",
cpu, ret);
}
}
/* * Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding * in their ACPI data. Calculate the real values and fix up the _PSS data.
*/ staticvoid amd_fixup_frequency(struct acpi_processor_px *px, int i)
{
u32 hi, lo, fid, did; int index = px->control & 0x00000007;
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) return;
if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) ||
boot_cpu_data.x86 == 0x11) {
rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi); /* * MSR C001_0064+: * Bit 63: PstateEn. Read-write. If set, the P-state is valid.
*/ if (!(hi & BIT(31))) return;
fid = lo & 0x3f;
did = (lo >> 6) & 7; if (boot_cpu_data.x86 == 0x10)
px->core_frequency = (100 * (fid + 0x10)) >> did; else
px->core_frequency = (100 * (fid + 8)) >> did;
}
}
staticint acpi_processor_get_performance_states(struct acpi_processor *pr)
{ int result = 0;
acpi_status status = AE_OK; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" }; struct acpi_buffer state = { 0, NULL }; union acpi_object *pss = NULL; int i; int last_invalid = -1;
status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer); if (ACPI_FAILURE(status)) {
acpi_evaluation_failure_warn(pr->handle, "_PSS", status); return -ENODEV;
}
pss = buffer.pointer; if (!pss || pss->type != ACPI_TYPE_PACKAGE) {
pr_err("Invalid _PSS data\n");
result = -EFAULT; goto end;
}
/* * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq
*/ if (!px->core_frequency ||
(u32)(px->core_frequency * 1000) != px->core_frequency * 1000) {
pr_err(FW_BUG "Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n",
pr->id, px->core_frequency); if (last_invalid == -1)
last_invalid = i;
} else { if (last_invalid != -1) { /* * Copy this valid entry over last_invalid entry
*/
memcpy(&(pr->performance->states[last_invalid]),
px, sizeof(struct acpi_processor_px));
++last_invalid;
}
}
}
if (last_invalid == 0) {
pr_err(FW_BUG "No valid BIOS _PSS frequency found for processor %d\n", pr->id);
result = -EFAULT;
kfree(pr->performance->states);
pr->performance->states = NULL;
}
if (last_invalid > 0)
pr->performance->state_count = last_invalid;
end:
kfree(buffer.pointer);
return result;
}
int acpi_processor_get_performance_info(struct acpi_processor *pr)
{ int result = 0;
if (!pr || !pr->performance || !pr->handle) return -EINVAL;
if (!acpi_has_method(pr->handle, "_PCT")) {
acpi_handle_debug(pr->handle, "ACPI-based processor performance control unavailable\n"); return -ENODEV;
}
result = acpi_processor_get_performance_control(pr); if (result) goto update_bios;
result = acpi_processor_get_performance_states(pr); if (result) goto update_bios;
/* We need to call _PPC once when cpufreq starts */ if (ignore_ppc != 1)
result = acpi_processor_get_platform_limit(pr);
return result;
/* * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that * the BIOS is older than the CPU and does not know its frequencies
*/
update_bios: if (acpi_has_method(pr->handle, "_PPC")) { if(boot_cpu_has(X86_FEATURE_EST))
pr_warn(FW_BUG "BIOS needs update for CPU " "frequency support\n");
} return result;
}
EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info);
int acpi_processor_pstate_control(void)
{
acpi_status status;
if (!acpi_gbl_FADT.smi_command || !acpi_gbl_FADT.pstate_control) return 0;
pr_debug("Writing pstate_control [0x%x] to smi_command [0x%x]\n",
acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command);
status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
(u32)acpi_gbl_FADT.pstate_control, 8); if (ACPI_SUCCESS(status)) return 1;
pr_warn("Failed to write pstate_control [0x%x] to smi_command [0x%x]: %s\n",
acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command,
acpi_format_exception(status)); return -EIO;
}
int acpi_processor_notify_smm(struct module *calling_module)
{ staticint is_done; int result = 0;
if (!acpi_processor_cpufreq_init) return -EBUSY;
if (!try_module_get(calling_module)) return -EINVAL;
/* * is_done is set to negative if an error occurs and to 1 if no error * occurrs, but SMM has been notified already. This avoids repeated * notification which might lead to unexpected results.
*/ if (is_done != 0) { if (is_done < 0)
result = is_done;
goto out_put;
}
result = acpi_processor_pstate_control(); if (result <= 0) { if (result) {
is_done = result;
} else {
pr_debug("No SMI port or pstate_control\n");
is_done = 1;
} goto out_put;
}
is_done = 1; /* * Success. If there _PPC, unloading the cpufreq driver would be risky, * so disallow it in that case.
*/ if (acpi_processor_ppc_in_use) return 0;
int acpi_processor_preregister_performance( struct acpi_processor_performance __percpu *performance)
{ int count_target; int retval = 0; unsignedint i, j;
cpumask_var_t covered_cpus; struct acpi_processor *pr; struct acpi_psd_package *pdomain; struct acpi_processor *match_pr; struct acpi_psd_package *match_pdomain;
if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) return -ENOMEM;
mutex_lock(&performance_mutex);
/* * Check if another driver has already registered, and abort before * changing pr->performance if it has. Check input data as well.
*/
for_each_possible_cpu(i) {
pr = per_cpu(processors, i); if (!pr) { /* Look only at processors in ACPI namespace */ continue;
}
if (pr->performance) {
retval = -EBUSY; goto err_out;
}
/* * Now that we have _PSD data from all CPUs, lets setup P-state * domain info.
*/
for_each_possible_cpu(i) {
pr = per_cpu(processors, i); if (!pr) continue;
/* Assume no coordination on any error parsing domain info */ if (retval) {
cpumask_clear(pr->performance->shared_cpu_map);
cpumask_set_cpu(i, pr->performance->shared_cpu_map);
pr->performance->shared_type = CPUFREQ_SHARED_TYPE_NONE;
}
pr->performance = NULL; /* Will be set for real in register */
}
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.