Path: blob/master/drivers/acpi/processor_perflib.c
15109 views
/*1* processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $)2*3* Copyright (C) 2001, 2002 Andy Grover <[email protected]>4* Copyright (C) 2001, 2002 Paul Diefenbaugh <[email protected]>5* Copyright (C) 2004 Dominik Brodowski <[email protected]>6* Copyright (C) 2004 Anil S Keshavamurthy <[email protected]>7* - Added processor hotplug support8*9*10* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~11*12* This program is free software; you can redistribute it and/or modify13* it under the terms of the GNU General Public License as published by14* the Free Software Foundation; either version 2 of the License, or (at15* your option) any later version.16*17* This program is distributed in the hope that it will be useful, but18* WITHOUT ANY WARRANTY; without even the implied warranty of19* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU20* General Public License for more details.21*22* You should have received a copy of the GNU General Public License along23* with this program; if not, write to the Free Software Foundation, Inc.,24* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.25*26*/2728#include <linux/kernel.h>29#include <linux/module.h>30#include <linux/init.h>31#include <linux/cpufreq.h>32#include <linux/slab.h>3334#ifdef CONFIG_X8635#include <asm/cpufeature.h>36#endif3738#include <acpi/acpi_bus.h>39#include <acpi/acpi_drivers.h>40#include <acpi/processor.h>4142#define PREFIX "ACPI: "4344#define ACPI_PROCESSOR_CLASS "processor"45#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"46#define _COMPONENT ACPI_PROCESSOR_COMPONENT47ACPI_MODULE_NAME("processor_perflib");4849static DEFINE_MUTEX(performance_mutex);5051/*52* _PPC support is implemented as a CPUfreq policy notifier:53* This means each time a CPUfreq driver registered also with54* the ACPI core is asked to change the speed policy, the maximum55* value is adjusted so that it is within the platform limit.56*57* Also, when a new platform limit value is detected, the CPUfreq58* policy is adjusted accordingly.59*/6061/* ignore_ppc:62* -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet63* ignore _PPC64* 0 -> cpufreq low level drivers initialized -> consider _PPC values65* 1 -> ignore _PPC totally -> forced by user through boot param66*/67static int ignore_ppc = -1;68module_param(ignore_ppc, int, 0644);69MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \70"limited by BIOS, this should help");7172#define PPC_REGISTERED 173#define PPC_IN_USE 27475static int acpi_processor_ppc_status;7677static int acpi_processor_ppc_notifier(struct notifier_block *nb,78unsigned long event, void *data)79{80struct cpufreq_policy *policy = data;81struct acpi_processor *pr;82unsigned int ppc = 0;8384if (event == CPUFREQ_START && ignore_ppc <= 0) {85ignore_ppc = 0;86return 0;87}8889if (ignore_ppc)90return 0;9192if (event != CPUFREQ_INCOMPATIBLE)93return 0;9495mutex_lock(&performance_mutex);9697pr = per_cpu(processors, policy->cpu);98if (!pr || !pr->performance)99goto out;100101ppc = (unsigned int)pr->performance_platform_limit;102103if (ppc >= pr->performance->state_count)104goto out;105106cpufreq_verify_within_limits(policy, 0,107pr->performance->states[ppc].108core_frequency * 1000);109110out:111mutex_unlock(&performance_mutex);112113return 0;114}115116static struct notifier_block acpi_ppc_notifier_block = {117.notifier_call = acpi_processor_ppc_notifier,118};119120static int acpi_processor_get_platform_limit(struct acpi_processor *pr)121{122acpi_status status = 0;123unsigned long long ppc = 0;124125126if (!pr)127return -EINVAL;128129/*130* _PPC indicates the maximum state currently supported by the platform131* (e.g. 0 = states 0..n; 1 = states 1..n; etc.132*/133status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc);134135if (status != AE_NOT_FOUND)136acpi_processor_ppc_status |= PPC_IN_USE;137138if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {139ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC"));140return -ENODEV;141}142143pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,144(int)ppc, ppc ? "" : "not");145146pr->performance_platform_limit = (int)ppc;147148return 0;149}150151#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80152/*153* acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status154* @handle: ACPI processor handle155* @status: the status code of _PPC evaluation156* 0: success. OSPM is now using the performance state specificed.157* 1: failure. OSPM has not changed the number of P-states in use158*/159static void acpi_processor_ppc_ost(acpi_handle handle, int status)160{161union acpi_object params[2] = {162{.type = ACPI_TYPE_INTEGER,},163{.type = ACPI_TYPE_INTEGER,},164};165struct acpi_object_list arg_list = {2, params};166acpi_handle temp;167168params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE;169params[1].integer.value = status;170171/* when there is no _OST , skip it */172if (ACPI_FAILURE(acpi_get_handle(handle, "_OST", &temp)))173return;174175acpi_evaluate_object(handle, "_OST", &arg_list, NULL);176return;177}178179int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)180{181int ret;182183if (ignore_ppc) {184/*185* Only when it is notification event, the _OST object186* will be evaluated. Otherwise it is skipped.187*/188if (event_flag)189acpi_processor_ppc_ost(pr->handle, 1);190return 0;191}192193ret = acpi_processor_get_platform_limit(pr);194/*195* Only when it is notification event, the _OST object196* will be evaluated. Otherwise it is skipped.197*/198if (event_flag) {199if (ret < 0)200acpi_processor_ppc_ost(pr->handle, 1);201else202acpi_processor_ppc_ost(pr->handle, 0);203}204if (ret < 0)205return (ret);206else207return cpufreq_update_policy(pr->id);208}209210int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)211{212struct acpi_processor *pr;213214pr = per_cpu(processors, cpu);215if (!pr || !pr->performance || !pr->performance->state_count)216return -ENODEV;217*limit = pr->performance->states[pr->performance_platform_limit].218core_frequency * 1000;219return 0;220}221EXPORT_SYMBOL(acpi_processor_get_bios_limit);222223void acpi_processor_ppc_init(void)224{225if (!cpufreq_register_notifier226(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))227acpi_processor_ppc_status |= PPC_REGISTERED;228else229printk(KERN_DEBUG230"Warning: Processor Platform Limit not supported.\n");231}232233void acpi_processor_ppc_exit(void)234{235if (acpi_processor_ppc_status & PPC_REGISTERED)236cpufreq_unregister_notifier(&acpi_ppc_notifier_block,237CPUFREQ_POLICY_NOTIFIER);238239acpi_processor_ppc_status &= ~PPC_REGISTERED;240}241242static int acpi_processor_get_performance_control(struct acpi_processor *pr)243{244int result = 0;245acpi_status status = 0;246struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };247union acpi_object *pct = NULL;248union acpi_object obj = { 0 };249250251status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);252if (ACPI_FAILURE(status)) {253ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT"));254return -ENODEV;255}256257pct = (union acpi_object *)buffer.pointer;258if (!pct || (pct->type != ACPI_TYPE_PACKAGE)259|| (pct->package.count != 2)) {260printk(KERN_ERR PREFIX "Invalid _PCT data\n");261result = -EFAULT;262goto end;263}264265/*266* control_register267*/268269obj = pct->package.elements[0];270271if ((obj.type != ACPI_TYPE_BUFFER)272|| (obj.buffer.length < sizeof(struct acpi_pct_register))273|| (obj.buffer.pointer == NULL)) {274printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n");275result = -EFAULT;276goto end;277}278memcpy(&pr->performance->control_register, obj.buffer.pointer,279sizeof(struct acpi_pct_register));280281/*282* status_register283*/284285obj = pct->package.elements[1];286287if ((obj.type != ACPI_TYPE_BUFFER)288|| (obj.buffer.length < sizeof(struct acpi_pct_register))289|| (obj.buffer.pointer == NULL)) {290printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n");291result = -EFAULT;292goto end;293}294295memcpy(&pr->performance->status_register, obj.buffer.pointer,296sizeof(struct acpi_pct_register));297298end:299kfree(buffer.pointer);300301return result;302}303304static int acpi_processor_get_performance_states(struct acpi_processor *pr)305{306int result = 0;307acpi_status status = AE_OK;308struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };309struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" };310struct acpi_buffer state = { 0, NULL };311union acpi_object *pss = NULL;312int i;313314315status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);316if (ACPI_FAILURE(status)) {317ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS"));318return -ENODEV;319}320321pss = buffer.pointer;322if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {323printk(KERN_ERR PREFIX "Invalid _PSS data\n");324result = -EFAULT;325goto end;326}327328ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",329pss->package.count));330331pr->performance->state_count = pss->package.count;332pr->performance->states =333kmalloc(sizeof(struct acpi_processor_px) * pss->package.count,334GFP_KERNEL);335if (!pr->performance->states) {336result = -ENOMEM;337goto end;338}339340for (i = 0; i < pr->performance->state_count; i++) {341342struct acpi_processor_px *px = &(pr->performance->states[i]);343344state.length = sizeof(struct acpi_processor_px);345state.pointer = px;346347ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));348349status = acpi_extract_package(&(pss->package.elements[i]),350&format, &state);351if (ACPI_FAILURE(status)) {352ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data"));353result = -EFAULT;354kfree(pr->performance->states);355goto end;356}357358ACPI_DEBUG_PRINT((ACPI_DB_INFO,359"State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",360i,361(u32) px->core_frequency,362(u32) px->power,363(u32) px->transition_latency,364(u32) px->bus_master_latency,365(u32) px->control, (u32) px->status));366367/*368* Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq369*/370if (!px->core_frequency ||371((u32)(px->core_frequency * 1000) !=372(px->core_frequency * 1000))) {373printk(KERN_ERR FW_BUG PREFIX374"Invalid BIOS _PSS frequency: 0x%llx MHz\n",375px->core_frequency);376result = -EFAULT;377kfree(pr->performance->states);378goto end;379}380}381382end:383kfree(buffer.pointer);384385return result;386}387388static int acpi_processor_get_performance_info(struct acpi_processor *pr)389{390int result = 0;391acpi_status status = AE_OK;392acpi_handle handle = NULL;393394if (!pr || !pr->performance || !pr->handle)395return -EINVAL;396397status = acpi_get_handle(pr->handle, "_PCT", &handle);398if (ACPI_FAILURE(status)) {399ACPI_DEBUG_PRINT((ACPI_DB_INFO,400"ACPI-based processor performance control unavailable\n"));401return -ENODEV;402}403404result = acpi_processor_get_performance_control(pr);405if (result)406goto update_bios;407408result = acpi_processor_get_performance_states(pr);409if (result)410goto update_bios;411412/* We need to call _PPC once when cpufreq starts */413if (ignore_ppc != 1)414result = acpi_processor_get_platform_limit(pr);415416return result;417418/*419* Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that420* the BIOS is older than the CPU and does not know its frequencies421*/422update_bios:423#ifdef CONFIG_X86424if (ACPI_SUCCESS(acpi_get_handle(pr->handle, "_PPC", &handle))){425if(boot_cpu_has(X86_FEATURE_EST))426printk(KERN_WARNING FW_BUG "BIOS needs update for CPU "427"frequency support\n");428}429#endif430return result;431}432433int acpi_processor_notify_smm(struct module *calling_module)434{435acpi_status status;436static int is_done = 0;437438439if (!(acpi_processor_ppc_status & PPC_REGISTERED))440return -EBUSY;441442if (!try_module_get(calling_module))443return -EINVAL;444445/* is_done is set to negative if an error occurred,446* and to postitive if _no_ error occurred, but SMM447* was already notified. This avoids double notification448* which might lead to unexpected results...449*/450if (is_done > 0) {451module_put(calling_module);452return 0;453} else if (is_done < 0) {454module_put(calling_module);455return is_done;456}457458is_done = -EIO;459460/* Can't write pstate_control to smi_command if either value is zero */461if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) {462ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n"));463module_put(calling_module);464return 0;465}466467ACPI_DEBUG_PRINT((ACPI_DB_INFO,468"Writing pstate_control [0x%x] to smi_command [0x%x]\n",469acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));470471status = acpi_os_write_port(acpi_gbl_FADT.smi_command,472(u32) acpi_gbl_FADT.pstate_control, 8);473if (ACPI_FAILURE(status)) {474ACPI_EXCEPTION((AE_INFO, status,475"Failed to write pstate_control [0x%x] to "476"smi_command [0x%x]", acpi_gbl_FADT.pstate_control,477acpi_gbl_FADT.smi_command));478module_put(calling_module);479return status;480}481482/* Success. If there's no _PPC, we need to fear nothing, so483* we can allow the cpufreq driver to be rmmod'ed. */484is_done = 1;485486if (!(acpi_processor_ppc_status & PPC_IN_USE))487module_put(calling_module);488489return 0;490}491492EXPORT_SYMBOL(acpi_processor_notify_smm);493494static int acpi_processor_get_psd(struct acpi_processor *pr)495{496int result = 0;497acpi_status status = AE_OK;498struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};499struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};500struct acpi_buffer state = {0, NULL};501union acpi_object *psd = NULL;502struct acpi_psd_package *pdomain;503504status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer);505if (ACPI_FAILURE(status)) {506return -ENODEV;507}508509psd = buffer.pointer;510if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {511printk(KERN_ERR PREFIX "Invalid _PSD data\n");512result = -EFAULT;513goto end;514}515516if (psd->package.count != 1) {517printk(KERN_ERR PREFIX "Invalid _PSD data\n");518result = -EFAULT;519goto end;520}521522pdomain = &(pr->performance->domain_info);523524state.length = sizeof(struct acpi_psd_package);525state.pointer = pdomain;526527status = acpi_extract_package(&(psd->package.elements[0]),528&format, &state);529if (ACPI_FAILURE(status)) {530printk(KERN_ERR PREFIX "Invalid _PSD data\n");531result = -EFAULT;532goto end;533}534535if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {536printk(KERN_ERR PREFIX "Unknown _PSD:num_entries\n");537result = -EFAULT;538goto end;539}540541if (pdomain->revision != ACPI_PSD_REV0_REVISION) {542printk(KERN_ERR PREFIX "Unknown _PSD:revision\n");543result = -EFAULT;544goto end;545}546547if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&548pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&549pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {550printk(KERN_ERR PREFIX "Invalid _PSD:coord_type\n");551result = -EFAULT;552goto end;553}554end:555kfree(buffer.pointer);556return result;557}558559int acpi_processor_preregister_performance(560struct acpi_processor_performance __percpu *performance)561{562int count, count_target;563int retval = 0;564unsigned int i, j;565cpumask_var_t covered_cpus;566struct acpi_processor *pr;567struct acpi_psd_package *pdomain;568struct acpi_processor *match_pr;569struct acpi_psd_package *match_pdomain;570571if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))572return -ENOMEM;573574mutex_lock(&performance_mutex);575576/*577* Check if another driver has already registered, and abort before578* changing pr->performance if it has. Check input data as well.579*/580for_each_possible_cpu(i) {581pr = per_cpu(processors, i);582if (!pr) {583/* Look only at processors in ACPI namespace */584continue;585}586587if (pr->performance) {588retval = -EBUSY;589goto err_out;590}591592if (!performance || !per_cpu_ptr(performance, i)) {593retval = -EINVAL;594goto err_out;595}596}597598/* Call _PSD for all CPUs */599for_each_possible_cpu(i) {600pr = per_cpu(processors, i);601if (!pr)602continue;603604pr->performance = per_cpu_ptr(performance, i);605cpumask_set_cpu(i, pr->performance->shared_cpu_map);606if (acpi_processor_get_psd(pr)) {607retval = -EINVAL;608continue;609}610}611if (retval)612goto err_ret;613614/*615* Now that we have _PSD data from all CPUs, lets setup P-state616* domain info.617*/618for_each_possible_cpu(i) {619pr = per_cpu(processors, i);620if (!pr)621continue;622623if (cpumask_test_cpu(i, covered_cpus))624continue;625626pdomain = &(pr->performance->domain_info);627cpumask_set_cpu(i, pr->performance->shared_cpu_map);628cpumask_set_cpu(i, covered_cpus);629if (pdomain->num_processors <= 1)630continue;631632/* Validate the Domain info */633count_target = pdomain->num_processors;634count = 1;635if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)636pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;637else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)638pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW;639else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)640pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;641642for_each_possible_cpu(j) {643if (i == j)644continue;645646match_pr = per_cpu(processors, j);647if (!match_pr)648continue;649650match_pdomain = &(match_pr->performance->domain_info);651if (match_pdomain->domain != pdomain->domain)652continue;653654/* Here i and j are in the same domain */655656if (match_pdomain->num_processors != count_target) {657retval = -EINVAL;658goto err_ret;659}660661if (pdomain->coord_type != match_pdomain->coord_type) {662retval = -EINVAL;663goto err_ret;664}665666cpumask_set_cpu(j, covered_cpus);667cpumask_set_cpu(j, pr->performance->shared_cpu_map);668count++;669}670671for_each_possible_cpu(j) {672if (i == j)673continue;674675match_pr = per_cpu(processors, j);676if (!match_pr)677continue;678679match_pdomain = &(match_pr->performance->domain_info);680if (match_pdomain->domain != pdomain->domain)681continue;682683match_pr->performance->shared_type =684pr->performance->shared_type;685cpumask_copy(match_pr->performance->shared_cpu_map,686pr->performance->shared_cpu_map);687}688}689690err_ret:691for_each_possible_cpu(i) {692pr = per_cpu(processors, i);693if (!pr || !pr->performance)694continue;695696/* Assume no coordination on any error parsing domain info */697if (retval) {698cpumask_clear(pr->performance->shared_cpu_map);699cpumask_set_cpu(i, pr->performance->shared_cpu_map);700pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;701}702pr->performance = NULL; /* Will be set for real in register */703}704705err_out:706mutex_unlock(&performance_mutex);707free_cpumask_var(covered_cpus);708return retval;709}710EXPORT_SYMBOL(acpi_processor_preregister_performance);711712int713acpi_processor_register_performance(struct acpi_processor_performance714*performance, unsigned int cpu)715{716struct acpi_processor *pr;717718if (!(acpi_processor_ppc_status & PPC_REGISTERED))719return -EINVAL;720721mutex_lock(&performance_mutex);722723pr = per_cpu(processors, cpu);724if (!pr) {725mutex_unlock(&performance_mutex);726return -ENODEV;727}728729if (pr->performance) {730mutex_unlock(&performance_mutex);731return -EBUSY;732}733734WARN_ON(!performance);735736pr->performance = performance;737738if (acpi_processor_get_performance_info(pr)) {739pr->performance = NULL;740mutex_unlock(&performance_mutex);741return -EIO;742}743744mutex_unlock(&performance_mutex);745return 0;746}747748EXPORT_SYMBOL(acpi_processor_register_performance);749750void751acpi_processor_unregister_performance(struct acpi_processor_performance752*performance, unsigned int cpu)753{754struct acpi_processor *pr;755756mutex_lock(&performance_mutex);757758pr = per_cpu(processors, cpu);759if (!pr) {760mutex_unlock(&performance_mutex);761return;762}763764if (pr->performance)765kfree(pr->performance->states);766pr->performance = NULL;767768mutex_unlock(&performance_mutex);769770return;771}772773EXPORT_SYMBOL(acpi_processor_unregister_performance);774775776