Commit eac14729 authored by Andy Grover's avatar Andy Grover

ACPI: implement support for cpufreq interface (Dominik Brodowski)

parent 2a2eff19
...@@ -72,6 +72,11 @@ CONFIG_ACPI_PROCESSOR ...@@ -72,6 +72,11 @@ CONFIG_ACPI_PROCESSOR
ACPI C2 and C3 processor states to save power, on systems that ACPI C2 and C3 processor states to save power, on systems that
support it. support it.
CONFIG_ACPI_PROCESSOR_PERF
This driver adds support for CPU frequency scaling, if this is supported
by the hardware and the BIOS. If you are compiling for a mobile system,
say Y.
CONFIG_ACPI_THERMAL CONFIG_ACPI_THERMAL
This driver adds support for ACPI thermal zones. Most mobile and This driver adds support for ACPI thermal zones. Most mobile and
some desktop systems support ACPI thermal zones. It is HIGHLY some desktop systems support ACPI thermal zones. It is HIGHLY
......
...@@ -21,6 +21,12 @@ if [ "$CONFIG_X86" = "y" ]; then ...@@ -21,6 +21,12 @@ if [ "$CONFIG_X86" = "y" ]; then
tristate ' Button' CONFIG_ACPI_BUTTON tristate ' Button' CONFIG_ACPI_BUTTON
tristate ' Fan' CONFIG_ACPI_FAN tristate ' Fan' CONFIG_ACPI_FAN
tristate ' Processor' CONFIG_ACPI_PROCESSOR tristate ' Processor' CONFIG_ACPI_PROCESSOR
if [ "$CONFIG_ACPI_PROCESSOR" != "n" ]; then
bool ' Processor Performance States' CONFIG_ACPI_PROCESSOR_PERF
if [ "$CONFIG_CPU_FREQ" != "y" ]; then
define_bool CONFIG_CPU_FREQ $CONFIG_ACPI_PROCESSOR_PERF
fi
fi
dep_tristate ' Thermal Zone' CONFIG_ACPI_THERMAL $CONFIG_ACPI_PROCESSOR dep_tristate ' Thermal Zone' CONFIG_ACPI_THERMAL $CONFIG_ACPI_PROCESSOR
if [ "$CONFIG_NUMA" = "y" ]; then if [ "$CONFIG_NUMA" = "y" ]; then
dep_bool ' NUMA support' CONFIG_ACPI_NUMA $CONFIG_NUMA dep_bool ' NUMA support' CONFIG_ACPI_NUMA $CONFIG_NUMA
......
...@@ -43,6 +43,10 @@ ...@@ -43,6 +43,10 @@
#include "acpi_bus.h" #include "acpi_bus.h"
#include "acpi_drivers.h" #include "acpi_drivers.h"
#ifdef CONFIG_ACPI_PROCESSOR_PERF
#include <linux/cpufreq.h>
#endif
#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_COMPONENT 0x01000000
#define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_CLASS "processor"
#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver"
...@@ -224,6 +228,11 @@ static struct acpi_processor *processors[NR_CPUS]; ...@@ -224,6 +228,11 @@ static struct acpi_processor *processors[NR_CPUS];
static struct acpi_processor_errata errata; static struct acpi_processor_errata errata;
static void (*pm_idle_save)(void) = NULL; static void (*pm_idle_save)(void) = NULL;
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static unsigned int cpufreq_usage_count = 0;
static struct cpufreq_driver *acpi_cpufreq_driver;
#endif
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Errata Handling Errata Handling
...@@ -830,7 +839,7 @@ acpi_processor_get_power_info ( ...@@ -830,7 +839,7 @@ acpi_processor_get_power_info (
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Performance Management Performance Management
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static int static int
acpi_processor_get_platform_limit ( acpi_processor_get_platform_limit (
struct acpi_processor* pr) struct acpi_processor* pr)
...@@ -1037,6 +1046,7 @@ acpi_processor_set_performance ( ...@@ -1037,6 +1046,7 @@ acpi_processor_set_performance (
u16 port = 0; u16 port = 0;
u8 value = 0; u8 value = 0;
int i = 0; int i = 0;
struct cpufreq_freqs cpufreq_freqs;
ACPI_FUNCTION_TRACE("acpi_processor_set_performance"); ACPI_FUNCTION_TRACE("acpi_processor_set_performance");
...@@ -1068,6 +1078,14 @@ acpi_processor_set_performance ( ...@@ -1068,6 +1078,14 @@ acpi_processor_set_performance (
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n",
pr->performance.state, state)); pr->performance.state, state));
/* cpufreq frequency struct */
cpufreq_freqs.cpu = pr->id;
cpufreq_freqs.old = pr->performance.states[pr->performance.state].core_frequency;
cpufreq_freqs.new = pr->performance.states[state].core_frequency;
/* notify cpufreq */
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
/* /*
* First we write the target state's 'control' value to the * First we write the target state's 'control' value to the
* control_register. * control_register.
...@@ -1101,7 +1119,15 @@ acpi_processor_set_performance ( ...@@ -1101,7 +1119,15 @@ acpi_processor_set_performance (
udelay(10); udelay(10);
} }
/* notify cpufreq */
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
if (value != pr->performance.states[state].status) { if (value != pr->performance.states[state].status) {
unsigned int tmp = cpufreq_freqs.new;
cpufreq_freqs.new = cpufreq_freqs.old;
cpufreq_freqs.old = tmp;
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Transition failed\n")); ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Transition failed\n"));
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
} }
...@@ -1155,6 +1181,23 @@ acpi_processor_get_performance_info ( ...@@ -1155,6 +1181,23 @@ acpi_processor_get_performance_info (
return_VALUE(0); return_VALUE(0);
} }
#else
static int
acpi_processor_get_performance_info (
struct acpi_processor *pr)
{
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info_dummy");
if (!pr)
return_VALUE(-EINVAL);
pr->flags.performance = 0;
return_VALUE(0);
}
#endif
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
...@@ -1400,17 +1443,24 @@ acpi_processor_apply_limit ( ...@@ -1400,17 +1443,24 @@ acpi_processor_apply_limit (
if (!pr->flags.limit) if (!pr->flags.limit)
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
#ifdef CONFIG_ACPI_PROCESSOR_PERF
if (pr->flags.performance) { if (pr->flags.performance) {
px = pr->performance.platform_limit; px = pr->performance.platform_limit;
if (pr->limit.user.px > px) if (pr->limit.user.px > px)
px = pr->limit.user.px; px = pr->limit.user.px;
if (pr->limit.thermal.px > px) if (pr->limit.thermal.px > px)
px = pr->limit.thermal.px; px = pr->limit.thermal.px;
{
result = acpi_processor_set_performance(pr, px); struct cpufreq_policy policy;
policy.cpu = pr->id;
cpufreq_get_policy(&policy, pr->id);
policy.max = pr->performance.states[px].core_frequency * 1000;
result = cpufreq_set_policy(&policy);
}
if (result) if (result)
goto end; goto end;
} }
#endif
if (pr->flags.throttling) { if (pr->flags.throttling) {
if (pr->limit.user.tx > tx) if (pr->limit.user.tx > tx)
...@@ -1559,6 +1609,271 @@ acpi_processor_get_limit_info ( ...@@ -1559,6 +1609,271 @@ acpi_processor_get_limit_info (
return_VALUE(0); return_VALUE(0);
} }
/* --------------------------------------------------------------------------
cpufreq interface
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static void
acpi_cpufreq_setpolicy (
struct cpufreq_policy *policy)
{
unsigned int cpu = 0;
unsigned int i = 0;
struct acpi_processor *pr = NULL;
unsigned int next_state = 0;
unsigned int result = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy");
if (!policy)
return_VOID;
/* get a present, initialized CPU */
if (policy->cpu == CPUFREQ_ALL_CPUS)
{
for (i=0; i<NR_CPUS; i++) {
if (processors[i] != NULL) {
cpu = i;
pr = processors[cpu];
break;
}
}
}
else
{
cpu = policy->cpu;
pr = processors[cpu];
if (!pr)
return_VOID;
}
/* select appropriate P-State */
if (policy->policy == CPUFREQ_POLICY_POWERSAVE)
{
for (i=(pr->performance.state_count - 1); i>= pr->limit.state.px; i--)
{
unsigned int state_freq = pr->performance.states[i].core_frequency * 1000;
if ((policy->min <= state_freq) &&
(policy->max >= state_freq))
{
next_state = i;
break;
}
}
} else {
for (i=pr->limit.state.px; i < pr->performance.state_count; i++)
{
unsigned int state_freq = pr->performance.states[i].core_frequency * 1000;
if ((policy->min <= state_freq) &&
(policy->max >= state_freq))
{
next_state = i;
break;
}
}
}
/* set one or all CPUs to the new state */
if (policy->cpu == CPUFREQ_ALL_CPUS) {
for (i=0; i<NR_CPUS; i++)
{
pr = processors[cpu];
if (!pr || !cpu_online(cpu))
continue;
result = acpi_processor_set_performance (pr, next_state);
}
} else {
result = acpi_processor_set_performance (pr, next_state);
}
return_VOID;
}
static void
acpi_cpufreq_verify (
struct cpufreq_policy *policy)
{
unsigned int cpu = 0;
unsigned int i = 0;
struct acpi_processor *pr = NULL;
unsigned int number_states = 0;
unsigned int next_larger_state = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_verify");
if (!policy)
return_VOID;
/* get a present, initialized CPU */
if (policy->cpu == CPUFREQ_ALL_CPUS)
{
for (i=0; i<NR_CPUS; i++) {
if (processors[i] != NULL) {
cpu = i;
pr = processors[cpu];
break;
}
}
}
else
{
cpu = policy->cpu;
pr = processors[cpu];
if (!pr)
return_VOID;
}
/* first check if min and max are within valid limits */
cpufreq_verify_within_limits(
policy,
pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000,
pr->performance.states[pr->limit.state.px].core_frequency * 1000);
/* now check if at least one value is within this limit */
for (i=pr->limit.state.px; i < pr->performance.state_count; i++)
{
unsigned int state_freq = pr->performance.states[i].core_frequency * 1000;
if ((policy->min <= state_freq) &&
(policy->max >= state_freq))
number_states++;
if (state_freq > policy->max)
next_larger_state = i;
}
if (number_states)
return_VOID;
/* round up now */
policy->max = pr->performance.states[next_larger_state].core_frequency * 1000;
return_VOID;
}
static int
acpi_cpufreq_init (
struct acpi_processor *pr)
{
int result = 0;
int i = 0;
int current_state = 0;
struct cpufreq_driver *driver;
ACPI_FUNCTION_TRACE("acpi_cpufreq_init");
if (cpufreq_usage_count) {
if (pr->flags.performance == 1)
cpufreq_usage_count++;
return_VALUE(0);
}
/* test if it works */
current_state = pr->performance.state;
if (current_state == pr->limit.state.px) {
result = acpi_processor_set_performance(pr, (pr->performance.state_count - 1));
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
pr->flags.performance = 0;
return_VALUE(-ENODEV);
}
}
result = acpi_processor_set_performance(pr, pr->limit.state.px);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
pr->flags.performance = 0;
return_VALUE(-ENODEV);
}
if (current_state != 0) {
result = acpi_processor_set_performance(pr, current_state);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
pr->flags.performance = 0;
return_VALUE(-ENODEV);
}
}
/* initialization of main "cpufreq" code*/
driver = kmalloc(sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!driver)
return_VALUE(-ENOMEM);
driver->policy = (struct cpufreq_policy *) (driver + 1);
#ifdef CONFIG_CPU_FREQ_24_API
driver->cpu_min_freq = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000;
for (i=0;i<NR_CPUS;i++)
driver->cpu_cur_freq[0] = pr->performance.states[current_state].core_frequency * 1000;
#endif
driver->verify = &acpi_cpufreq_verify;
driver->setpolicy = &acpi_cpufreq_setpolicy;
for (i=0;i<NR_CPUS;i++) {
driver->policy[i].cpu = pr->id;
driver->policy[i].min = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000;
driver->policy[i].max = pr->performance.states[pr->limit.state.px].core_frequency * 1000;
driver->policy[i].max_cpu_freq = pr->performance.states[0].core_frequency * 1000;
driver->policy[i].policy = ( pr->performance.states[current_state].core_frequency * 1000 == driver->policy[i].max) ?
CPUFREQ_POLICY_PERFORMANCE : CPUFREQ_POLICY_POWERSAVE;
}
acpi_cpufreq_driver = driver;
result = cpufreq_register(driver);
if (result) {
kfree(driver);
acpi_cpufreq_driver = NULL;
return_VALUE(result);
}
cpufreq_usage_count++;
return_VALUE(0);
}
static int
acpi_cpufreq_exit (
struct acpi_processor *pr)
{
int result = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_exit");
if (!pr)
return_VALUE(-EINVAL);
if (pr->flags.performance)
cpufreq_usage_count--;
if (!cpufreq_usage_count) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Removing cpufreq driver\n"));
result = cpufreq_unregister();
}
return_VALUE(result);
}
#else
static int
acpi_cpufreq_init (
struct acpi_processor *pr)
{
ACPI_FUNCTION_TRACE("acpi_cpufreq_init_dummy");
return_VALUE(0);
}
static int
acpi_cpufreq_exit (
struct acpi_processor *pr)
{
ACPI_FUNCTION_TRACE("acpi_cpufreq_exit_dummy");
return_VALUE(0);
}
#endif
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
FS Interface (/proc) FS Interface (/proc)
...@@ -1685,7 +2000,7 @@ acpi_processor_read_power ( ...@@ -1685,7 +2000,7 @@ acpi_processor_read_power (
return_VALUE(len); return_VALUE(len);
} }
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static int static int
acpi_processor_read_performance ( acpi_processor_read_performance (
char *page, char *page,
...@@ -1736,7 +2051,6 @@ acpi_processor_read_performance ( ...@@ -1736,7 +2051,6 @@ acpi_processor_read_performance (
return_VALUE(len); return_VALUE(len);
} }
static int static int
acpi_processor_write_performance ( acpi_processor_write_performance (
struct file *file, struct file *file,
...@@ -1747,6 +2061,8 @@ acpi_processor_write_performance ( ...@@ -1747,6 +2061,8 @@ acpi_processor_write_performance (
int result = 0; int result = 0;
struct acpi_processor *pr = (struct acpi_processor *) data; struct acpi_processor *pr = (struct acpi_processor *) data;
char state_string[12] = {'\0'}; char state_string[12] = {'\0'};
unsigned int new_state = 0;
struct cpufreq_policy policy;
ACPI_FUNCTION_TRACE("acpi_processor_write_performance"); ACPI_FUNCTION_TRACE("acpi_processor_write_performance");
...@@ -1757,14 +2073,20 @@ acpi_processor_write_performance ( ...@@ -1757,14 +2073,20 @@ acpi_processor_write_performance (
return_VALUE(-EFAULT); return_VALUE(-EFAULT);
state_string[count] = '\0'; state_string[count] = '\0';
new_state = simple_strtoul(state_string, NULL, 0);
result = acpi_processor_set_performance(pr, cpufreq_get_policy(&policy, pr->id);
simple_strtoul(state_string, NULL, 0));
policy.cpu = pr->id;
policy.max = pr->performance.states[new_state].core_frequency * 1000;
result = cpufreq_set_policy(&policy);
if (result) if (result)
return_VALUE(result); return_VALUE(result);
return_VALUE(count); return_VALUE(count);
} }
#endif
static int static int
...@@ -2002,6 +2324,7 @@ acpi_processor_add_fs ( ...@@ -2002,6 +2324,7 @@ acpi_processor_add_fs (
entry->data = acpi_driver_data(device); entry->data = acpi_driver_data(device);
} }
#ifdef CONFIG_ACPI_PROCESSOR_PERF
/* 'performance' [R/W] */ /* 'performance' [R/W] */
entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
...@@ -2014,6 +2337,7 @@ acpi_processor_add_fs ( ...@@ -2014,6 +2337,7 @@ acpi_processor_add_fs (
entry->write_proc = acpi_processor_write_performance; entry->write_proc = acpi_processor_write_performance;
entry->data = acpi_driver_data(device); entry->data = acpi_driver_data(device);
} }
#endif
/* 'throttling' [R/W] */ /* 'throttling' [R/W] */
entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING, entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
...@@ -2136,6 +2460,7 @@ acpi_processor_get_info ( ...@@ -2136,6 +2460,7 @@ acpi_processor_get_info (
acpi_processor_get_power_info(pr); acpi_processor_get_power_info(pr);
acpi_processor_get_performance_info(pr); acpi_processor_get_performance_info(pr);
acpi_cpufreq_init(pr);
acpi_processor_get_throttling_info(pr); acpi_processor_get_throttling_info(pr);
acpi_processor_get_limit_info(pr); acpi_processor_get_limit_info(pr);
...@@ -2163,9 +2488,15 @@ acpi_processor_notify ( ...@@ -2163,9 +2488,15 @@ acpi_processor_notify (
switch (event) { switch (event) {
case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
#ifdef CONFIG_ACPI_PROCESSOR_PERF
result = acpi_processor_get_platform_limit(pr); result = acpi_processor_get_platform_limit(pr);
if (!result) if (!result)
acpi_processor_apply_limit(pr); acpi_processor_apply_limit(pr);
#else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Please use kernel with Processor Performance States support included!\n"));
result = 0;
#endif
acpi_bus_generate_event(device, event, acpi_bus_generate_event(device, event,
pr->performance.platform_limit); pr->performance.platform_limit);
...@@ -2285,6 +2616,7 @@ acpi_processor_remove ( ...@@ -2285,6 +2616,7 @@ acpi_processor_remove (
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
} }
acpi_cpufreq_exit(pr);
acpi_processor_remove_fs(device); acpi_processor_remove_fs(device);
processors[pr->id] = NULL; processors[pr->id] = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment