Commit c1f31696 authored by Andy Grover's avatar Andy Grover

CPUFREQ: Break out ACPI Perf code into its own module, under cpufreq

(Dominik Brodowski)
parent df47e43b
......@@ -983,6 +983,17 @@ config CPU_FREQ_24_API
If in doubt, say N.
config X86_ACPI_CPUFREQ
tristate "ACPI Processor P-States driver"
depends on CPU_FREQ && ACPI_PROCESSOR
help
This driver adds a CPUFreq driver which utilizes the ACPI
Processor Performance States.
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
config X86_POWERNOW_K6
tristate "AMD Mobile K6-2/K6-3 PowerNow!"
depends on CPU_FREQ
......
......@@ -5,3 +5,10 @@ obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
obj-$(CONFIG_X86_LONGRUN) += longrun.o
obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o
obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi.o
ifdef CONFIG_X86_ACPI_CPUFREQ
ifdef CONFIG_ACPI_DEBUG
EXTRA_CFLAGS += -DACPI_DEBUG_OUTPUT
endif
endif
/*
* acpi_processor_perf.c - ACPI Processor P-States Driver ($Revision: 71 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2002, 2003 Dominik Brodowski <linux@brodo.de>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#define ACPI_PROCESSOR_COMPONENT 0x01000000
#define ACPI_PROCESSOR_CLASS "processor"
#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor P-States Driver"
#define ACPI_PROCESSOR_DEVICE_NAME "Processor"
#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
ACPI_MODULE_NAME ("acpi_processor_perf")
MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME);
MODULE_LICENSE("GPL");
/* Performance Management */
static struct acpi_processor_performance *performance;
static struct cpufreq_driver acpi_cpufreq_driver;
static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_processor_perf_fops = {
.open = acpi_processor_perf_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int
acpi_processor_get_performance_control (
struct acpi_processor *pr)
{
int result = 0;
acpi_status status = 0;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *pct = NULL;
union acpi_object obj = {0};
struct acpi_pct_register *reg = NULL;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_control");
status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
if(ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PCT\n"));
return_VALUE(-ENODEV);
}
pct = (union acpi_object *) buffer.pointer;
if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
|| (pct->package.count != 2)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PCT data\n"));
result = -EFAULT;
goto end;
}
/*
* control_register
*/
obj = pct->package.elements[0];
if ((obj.type != ACPI_TYPE_BUFFER)
|| (obj.buffer.length < sizeof(struct acpi_pct_register))
|| (obj.buffer.pointer == NULL)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid _PCT data (control_register)\n"));
result = -EFAULT;
goto end;
}
reg = (struct acpi_pct_register *) (obj.buffer.pointer);
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported address space [%d] (control_register)\n",
(u32) reg->space_id));
result = -EFAULT;
goto end;
}
pr->performance->control_register = (u16) reg->address;
/*
* status_register
*/
obj = pct->package.elements[1];
if ((obj.type != ACPI_TYPE_BUFFER)
|| (obj.buffer.length < sizeof(struct acpi_pct_register))
|| (obj.buffer.pointer == NULL)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid _PCT data (status_register)\n"));
result = -EFAULT;
goto end;
}
reg = (struct acpi_pct_register *) (obj.buffer.pointer);
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported address space [%d] (status_register)\n",
(u32) reg->space_id));
result = -EFAULT;
goto end;
}
pr->performance->status_register = (u16) reg->address;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"control_register[0x%04x] status_register[0x%04x]\n",
pr->performance->control_register,
pr->performance->status_register));
end:
acpi_os_free(buffer.pointer);
return_VALUE(result);
}
static int
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 = 0;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_states");
status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
if(ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PSS\n"));
return_VALUE(-ENODEV);
}
pss = (union acpi_object *) buffer.pointer;
if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n"));
result = -EFAULT;
goto end;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",
pss->package.count));
if (pss->package.count > ACPI_PROCESSOR_MAX_PERFORMANCE) {
pr->performance->state_count = ACPI_PROCESSOR_MAX_PERFORMANCE;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Limiting number of states to max (%d)\n",
ACPI_PROCESSOR_MAX_PERFORMANCE));
}
else
pr->performance->state_count = pss->package.count;
if (pr->performance->state_count > 1)
pr->flags.performance = 1;
for (i = 0; i < pr->performance->state_count; i++) {
struct acpi_processor_px *px = &(pr->performance->states[i]);
state.length = sizeof(struct acpi_processor_px);
state.pointer = px;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));
status = acpi_extract_package(&(pss->package.elements[i]),
&format, &state);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n"));
result = -EFAULT;
goto end;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
i,
(u32) px->core_frequency,
(u32) px->power,
(u32) px->transition_latency,
(u32) px->bus_master_latency,
(u32) px->control,
(u32) px->status));
}
end:
acpi_os_free(buffer.pointer);
return_VALUE(result);
}
static int
acpi_processor_set_performance (
struct acpi_processor *pr,
int state)
{
u16 port = 0;
u8 value = 0;
int i = 0;
struct cpufreq_freqs cpufreq_freqs;
ACPI_FUNCTION_TRACE("acpi_processor_set_performance");
if (!pr)
return_VALUE(-EINVAL);
if (!pr->flags.performance)
return_VALUE(-ENODEV);
if (state >= pr->performance->state_count) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Invalid target state (P%d)\n", state));
return_VALUE(-ENODEV);
}
if (state < pr->performance_platform_limit) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Platform limit (P%d) overrides target state (P%d)\n",
pr->performance->platform_limit, state));
return_VALUE(-ENODEV);
}
if (state == pr->performance->state) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Already at target state (P%d)\n", state));
return_VALUE(0);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n",
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
* control_register.
*/
port = pr->performance->control_register;
value = (u16) pr->performance->states[state].control;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Writing 0x%02x to port 0x%04x\n", value, port));
outb(value, port);
/*
* Then we read the 'status_register' and compare the value with the
* target state's 'status' to make sure the transition was successful.
* Note that we'll poll for up to 1ms (100 cycles of 10us) before
* giving up.
*/
port = pr->performance->status_register;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Looking for 0x%02x from port 0x%04x\n",
(u8) pr->performance->states[state].status, port));
for (i=0; i<100; i++) {
value = inb(port);
if (value == (u8) pr->performance->states[state].status)
break;
udelay(10);
}
/* notify cpufreq */
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
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"));
return_VALUE(-ENODEV);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Transition successful after %d microseconds\n",
i * 10));
pr->performance->state = state;
return_VALUE(0);
}
static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
{
struct acpi_processor *pr = (struct acpi_processor *)seq->private;
int i = 0;
ACPI_FUNCTION_TRACE("acpi_processor_perf_seq_show");
if (!pr)
goto end;
if (!pr->flags.performance) {
seq_puts(seq, "<not supported>\n");
goto end;
}
seq_printf(seq, "state count: %d\n"
"active state: P%d\n",
pr->performance->state_count,
pr->performance->state);
seq_puts(seq, "states:\n");
for (i = 0; i < pr->performance->state_count; i++)
seq_printf(seq, " %cP%d: %d MHz, %d mW, %d uS\n",
(i == pr->performance->state?'*':' '), i,
(u32) pr->performance->states[i].core_frequency,
(u32) pr->performance->states[i].power,
(u32) pr->performance->states[i].transition_latency);
end:
return 0;
}
static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_processor_perf_seq_show,
PDE(inode)->data);
}
static int
acpi_processor_write_performance (
struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
int result = 0;
struct acpi_processor *pr = (struct acpi_processor *) data;
char state_string[12] = {'\0'};
unsigned int new_state = 0;
struct cpufreq_policy policy;
ACPI_FUNCTION_TRACE("acpi_processor_write_performance");
if (!pr || !pr->performance || (count > sizeof(state_string) - 1))
return_VALUE(-EINVAL);
if (copy_from_user(state_string, buffer, count))
return_VALUE(-EFAULT);
state_string[count] = '\0';
new_state = simple_strtoul(state_string, NULL, 0);
cpufreq_get_policy(&policy, pr->id);
policy.cpu = pr->id;
policy.max = pr->performance->states[new_state].core_frequency * 1000;
result = cpufreq_set_policy(&policy);
if (result)
return_VALUE(result);
return_VALUE(count);
}
static int
acpi_cpufreq_setpolicy (
struct cpufreq_policy *policy)
{
struct acpi_processor *pr = performance[policy->cpu].pr;
unsigned int next_state = 0;
unsigned int result = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy");
result = cpufreq_frequency_table_setpolicy(policy,
&performance[policy->cpu].freq_table[pr->limit.state.px],
&next_state);
if (result)
return_VALUE(result);
result = acpi_processor_set_performance (pr, next_state);
return_VALUE(result);
}
static int
acpi_cpufreq_verify (
struct cpufreq_policy *policy)
{
unsigned int result = 0;
unsigned int cpu = policy->cpu;
struct acpi_processor *pr = performance[policy->cpu].pr;
ACPI_FUNCTION_TRACE("acpi_cpufreq_verify");
result = cpufreq_frequency_table_verify(policy,
&performance[cpu].freq_table[pr->limit.state.px]);
cpufreq_verify_within_limits(
policy,
performance[cpu].states[performance[cpu].state_count - 1].core_frequency * 1000,
performance[cpu].states[pr->limit.state.px].core_frequency * 1000);
return_VALUE(result);
}
static int
acpi_processor_get_performance_info (
struct acpi_processor *pr)
{
int result = 0;
acpi_status status = AE_OK;
acpi_handle handle = NULL;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info");
if (!pr)
return_VALUE(-EINVAL);
status = acpi_get_handle(pr->handle, "_PCT", &handle);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"ACPI-based processor performance control unavailable\n"));
return_VALUE(-ENODEV);
}
result = acpi_processor_get_performance_control(pr);
if (result)
return_VALUE(result);
result = acpi_processor_get_performance_states(pr);
if (result)
return_VALUE(result);
result = acpi_processor_get_platform_limit(pr);
if (result)
return_VALUE(result);
return_VALUE(0);
}
static int
acpi_cpufreq_cpu_init (
struct cpufreq_policy *policy)
{
unsigned int i;
unsigned int cpu = policy->cpu;
struct acpi_processor *pr = NULL;
unsigned int result = 0;
struct proc_dir_entry *entry = NULL;
struct acpi_device *device = NULL;
ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_init");
acpi_processor_register_performance(&performance[cpu], &pr, cpu);
pr = performance[cpu].pr;
if (!pr)
return_VALUE(-ENODEV);
if (acpi_bus_get_device(pr->handle, &device))
return_VALUE(-ENODEV);
result = acpi_processor_get_performance_info(performance[cpu].pr);
if (result)
return_VALUE(-ENODEV);
/* capability check */
if (!pr->flags.performance)
return_VALUE(-ENODEV);
/* detect transition latency */
policy->cpuinfo.transition_latency = 0;
for (i=0;i<performance[cpu].state_count;i++) {
if (performance[cpu].states[i].transition_latency > policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency = performance[cpu].states[i].transition_latency;
}
policy->policy = CPUFREQ_POLICY_PERFORMANCE;
/* table init */
for (i=0; i<=performance[cpu].state_count; i++)
{
performance[cpu].freq_table[i].index = i;
if (i<performance[cpu].state_count)
performance[cpu].freq_table[i].frequency = performance[cpu].states[i].core_frequency * 1000;
else
performance[cpu].freq_table[i].frequency = CPUFREQ_TABLE_END;
}
#ifdef CONFIG_CPU_FREQ_24_API
acpi_cpufreq_driver.cpu_cur_freq[policy->cpu] = performance[cpu].states[pr->limit.state.px].core_frequency * 1000;
#endif
result = cpufreq_frequency_table_cpuinfo(policy, &performance[cpu].freq_table[0]);
/* add file 'performance' [R/W] */
entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
if (!entry)
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to create '%s' fs entry\n",
ACPI_PROCESSOR_FILE_PERFORMANCE));
else {
entry->proc_fops = &acpi_processor_perf_fops;
entry->write_proc = acpi_processor_write_performance;
entry->data = acpi_driver_data(device);
}
return_VALUE(result);
}
static int __init
acpi_cpufreq_init (void)
{
int result = 0;
int current_state = 0;
int i = 0;
struct acpi_processor *pr;
ACPI_FUNCTION_TRACE("acpi_cpufreq_init");
/* alloc memory */
if (performance)
return_VALUE(-EBUSY);
performance = kmalloc(NR_CPUS * sizeof(struct acpi_processor_performance), GFP_KERNEL);
if (!performance)
return_VALUE(-ENOMEM);
/* register struct acpi_performance performance */
for (i=0; i<NR_CPUS; i++) {
if (cpu_online(i))
acpi_processor_register_performance(&performance[i], &pr, i);
}
/* initialize */
for (i=0; i<NR_CPUS; i++) {
if (cpu_online(i) && performance[i].pr)
result = acpi_processor_get_performance_info(performance[i].pr);
}
/* test it on one CPU */
for (i=0; i<NR_CPUS; i++) {
pr = performance[i].pr;
if (pr && pr->flags.performance)
goto found_capable_cpu;
}
result = -ENODEV;
goto err;
found_capable_cpu:
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"));
result = -ENODEV;
goto err;
}
}
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"));
result = -ENODEV;
goto err;
}
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"));
result = -ENODEV;
goto err;
}
}
result = cpufreq_register_driver(&acpi_cpufreq_driver);
if (result)
goto err;
return_VALUE(0);
/* error handling */
err:
/* unregister struct acpi_performance performance */
for (i=0; i<NR_CPUS; i++) {
if (performance[i].pr) {
performance[i].pr->flags.performance = 0;
performance[i].pr->performance = NULL;
performance[i].pr = NULL;
}
}
return_VALUE(result);
}
static void __exit
acpi_cpufreq_exit (void)
{
int i = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_exit");
for (i=0; i<NR_CPUS; i++) {
if (performance[i].pr)
performance[i].pr->flags.performance = 0;
}
cpufreq_unregister_driver(&acpi_cpufreq_driver);
/* unregister struct acpi_performance performance */
for (i=0; i<NR_CPUS; i++) {
if (performance[i].pr) {
performance[i].pr->flags.performance = 0;
performance[i].pr->performance = NULL;
performance[i].pr = NULL;
}
}
kfree(performance);
return_VOID;
}
static struct cpufreq_driver acpi_cpufreq_driver = {
.verify = acpi_cpufreq_verify,
.setpolicy = acpi_cpufreq_setpolicy,
.init = acpi_cpufreq_cpu_init,
.exit = NULL,
.policy = NULL,
.name = "acpi-cpufreq",
};
late_initcall(acpi_cpufreq_init);
module_exit(acpi_cpufreq_exit);
......@@ -116,14 +116,6 @@ config ACPI_PROCESSOR
ACPI C2 and C3 processor states to save power, on systems that
support it.
config ACPI_PROCESSOR_PERF
bool "Processor Performance States"
depends on X86 && ACPI && !ACPI_HT_ONLY && ACPI_PROCESSOR && CPU_FREQ
help
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
tristate "Thermal Zone"
depends on ACPI_PROCESSOR
......
......@@ -35,18 +35,19 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/delay.h>
#include <linux/cpufreq.h>
#include <linux/compatmac.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/delay.h>
#include "acpi_bus.h"
#include "acpi_drivers.h"
#include "processor.h"
#ifdef CONFIG_ACPI_PROCESSOR_PERF
#include <linux/cpufreq.h>
#endif
#define ACPI_PROCESSOR_COMPONENT 0x01000000
#define ACPI_PROCESSOR_CLASS "processor"
......@@ -64,17 +65,6 @@
#define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */
#define C3_OVERHEAD 4 /* 1us (3.579 ticks per us) */
#define ACPI_PROCESSOR_BUSY_METRIC 10
#define ACPI_PROCESSOR_MAX_POWER ACPI_C_STATE_COUNT
#define ACPI_PROCESSOR_MAX_C2_LATENCY 100
#define ACPI_PROCESSOR_MAX_C3_LATENCY 1000
#define ACPI_PROCESSOR_MAX_PERFORMANCE 8
#define ACPI_PROCESSOR_MAX_THROTTLING 16
#define ACPI_PROCESSOR_MAX_THROTTLE 250 /* 25% */
#define ACPI_PROCESSOR_MAX_DUTY_WIDTH 4
const u32 POWER_OF_2[] = {1,2,4,8,16,32,64};
......@@ -107,118 +97,6 @@ static struct acpi_driver acpi_processor_driver = {
},
};
/* Power Management */
struct acpi_processor_cx_policy {
u32 count;
int state;
struct {
u32 time;
u32 ticks;
u32 count;
u32 bm;
} threshold;
};
struct acpi_processor_cx {
u8 valid;
u32 address;
u32 latency;
u32 latency_ticks;
u32 power;
u32 usage;
struct acpi_processor_cx_policy promotion;
struct acpi_processor_cx_policy demotion;
};
struct acpi_processor_power {
int state;
int default_state;
u32 bm_activity;
struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER];
};
/* Performance Management */
struct acpi_pct_register {
u8 descriptor;
u16 length;
u8 space_id;
u8 bit_width;
u8 bit_offset;
u8 reserved;
u64 address;
} __attribute__ ((packed));
struct acpi_processor_px {
acpi_integer core_frequency; /* megahertz */
acpi_integer power; /* milliWatts */
acpi_integer transition_latency; /* microseconds */
acpi_integer bus_master_latency; /* microseconds */
acpi_integer control; /* control value */
acpi_integer status; /* success indicator */
};
struct acpi_processor_performance {
int state;
int platform_limit;
u16 control_register;
u16 status_register;
int state_count;
struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE];
};
/* Throttling Control */
struct acpi_processor_tx {
u16 power;
u16 performance;
};
struct acpi_processor_throttling {
int state;
u32 address;
u8 duty_offset;
u8 duty_width;
int state_count;
struct acpi_processor_tx states[ACPI_PROCESSOR_MAX_THROTTLING];
};
/* Limit Interface */
struct acpi_processor_lx {
int px; /* performace state */
int tx; /* throttle level */
};
struct acpi_processor_limit {
struct acpi_processor_lx state; /* current limit */
struct acpi_processor_lx thermal; /* thermal limit */
struct acpi_processor_lx user; /* user limit */
};
struct acpi_processor_flags {
u8 power:1;
u8 performance:1;
u8 throttling:1;
u8 limit:1;
u8 bm_control:1;
u8 bm_check:1;
u8 reserved:2;
};
struct acpi_processor {
acpi_handle handle;
u32 acpi_id;
u32 id;
struct acpi_processor_flags flags;
struct acpi_processor_power power;
struct acpi_processor_performance performance;
struct acpi_processor_throttling throttling;
struct acpi_processor_limit limit;
};
struct acpi_processor_errata {
u8 smp;
......@@ -262,18 +140,6 @@ static struct acpi_processor *processors[NR_CPUS];
static struct acpi_processor_errata errata;
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;
static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file);
static struct file_operations acpi_processor_perf_fops = {
.open = acpi_processor_perf_open_fs,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
/* --------------------------------------------------------------------------
Errata Handling
......@@ -880,8 +746,7 @@ acpi_processor_get_power_info (
/* --------------------------------------------------------------------------
Performance Management
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static int
int
acpi_processor_get_platform_limit (
struct acpi_processor* pr)
{
......@@ -903,342 +768,33 @@ acpi_processor_get_platform_limit (
return_VALUE(-ENODEV);
}
pr->performance.platform_limit = (int) ppc;
pr->performance_platform_limit = (int) ppc;
return_VALUE(0);
}
EXPORT_SYMBOL(acpi_processor_get_platform_limit);
static int
acpi_processor_get_performance_control (
struct acpi_processor *pr)
{
int result = 0;
acpi_status status = 0;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *pct = NULL;
union acpi_object obj = {0};
struct acpi_pct_register *reg = NULL;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_control");
status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
if(ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PCT\n"));
return_VALUE(-ENODEV);
}
pct = (union acpi_object *) buffer.pointer;
if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
|| (pct->package.count != 2)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PCT data\n"));
result = -EFAULT;
goto end;
}
/*
* control_register
*/
obj = pct->package.elements[0];
if ((obj.type != ACPI_TYPE_BUFFER)
|| (obj.buffer.length < sizeof(struct acpi_pct_register))
|| (obj.buffer.pointer == NULL)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid _PCT data (control_register)\n"));
result = -EFAULT;
goto end;
}
reg = (struct acpi_pct_register *) (obj.buffer.pointer);
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported address space [%d] (control_register)\n",
(u32) reg->space_id));
result = -EFAULT;
goto end;
}
pr->performance.control_register = (u16) reg->address;
/*
* status_register
*/
obj = pct->package.elements[1];
if ((obj.type != ACPI_TYPE_BUFFER)
|| (obj.buffer.length < sizeof(struct acpi_pct_register))
|| (obj.buffer.pointer == NULL)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid _PCT data (status_register)\n"));
result = -EFAULT;
goto end;
}
reg = (struct acpi_pct_register *) (obj.buffer.pointer);
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unsupported address space [%d] (status_register)\n",
(u32) reg->space_id));
result = -EFAULT;
goto end;
}
pr->performance.status_register = (u16) reg->address;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"control_register[0x%04x] status_register[0x%04x]\n",
pr->performance.control_register,
pr->performance.status_register));
end:
acpi_os_free(buffer.pointer);
return_VALUE(result);
}
static int
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 = 0;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_states");
status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
if(ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PSS\n"));
return_VALUE(-ENODEV);
}
pss = (union acpi_object *) buffer.pointer;
if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n"));
result = -EFAULT;
goto end;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",
pss->package.count));
if (pss->package.count > ACPI_PROCESSOR_MAX_PERFORMANCE) {
pr->performance.state_count = ACPI_PROCESSOR_MAX_PERFORMANCE;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Limiting number of states to max (%d)\n",
ACPI_PROCESSOR_MAX_PERFORMANCE));
}
else
pr->performance.state_count = pss->package.count;
if (pr->performance.state_count > 1)
pr->flags.performance = 1;
for (i = 0; i < pr->performance.state_count; i++) {
struct acpi_processor_px *px = &(pr->performance.states[i]);
state.length = sizeof(struct acpi_processor_px);
state.pointer = px;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));
status = acpi_extract_package(&(pss->package.elements[i]),
&format, &state);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n"));
result = -EFAULT;
goto end;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
i,
(u32) px->core_frequency,
(u32) px->power,
(u32) px->transition_latency,
(u32) px->bus_master_latency,
(u32) px->control,
(u32) px->status));
}
end:
acpi_os_free(buffer.pointer);
return_VALUE(result);
}
static int
acpi_processor_set_performance (
struct acpi_processor *pr,
int state)
int
acpi_processor_register_performance (
struct acpi_processor_performance * performance,
struct acpi_processor ** pr,
unsigned int cpu)
{
u16 port = 0;
u8 value = 0;
int i = 0;
struct cpufreq_freqs cpufreq_freqs;
ACPI_FUNCTION_TRACE("acpi_processor_set_performance");
if (!pr)
return_VALUE(-EINVAL);
ACPI_FUNCTION_TRACE("acpi_processor_register_performance");
if (!pr->flags.performance)
*pr = processors[cpu];
if (!*pr)
return_VALUE(-ENODEV);
if (state >= pr->performance.state_count) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Invalid target state (P%d)\n", state));
return_VALUE(-ENODEV);
}
if ((*pr)->performance)
return_VALUE(-EBUSY);
if (state < pr->performance.platform_limit) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Platform limit (P%d) overrides target state (P%d)\n",
pr->performance.platform_limit, state));
return_VALUE(-ENODEV);
}
if (state == pr->performance.state) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Already at target state (P%d)\n", state));
return_VALUE(0);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n",
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
* control_register.
*/
port = pr->performance.control_register;
value = (u16) pr->performance.states[state].control;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Writing 0x%02x to port 0x%04x\n", value, port));
outb(value, port);
/*
* Then we read the 'status_register' and compare the value with the
* target state's 'status' to make sure the transition was successful.
* Note that we'll poll for up to 1ms (100 cycles of 10us) before
* giving up.
*/
port = pr->performance.status_register;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Looking for 0x%02x from port 0x%04x\n",
(u8) pr->performance.states[state].status, port));
for (i=0; i<100; i++) {
value = inb(port);
if (value == (u8) pr->performance.states[state].status)
break;
udelay(10);
}
/* notify cpufreq */
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
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"));
return_VALUE(-ENODEV);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Transition successful after %d microseconds\n",
i * 10));
pr->performance.state = state;
return_VALUE(0);
}
static int
acpi_processor_get_performance_info (
struct acpi_processor *pr)
{
int result = 0;
acpi_status status = AE_OK;
acpi_handle handle = NULL;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info");
if (!pr)
return_VALUE(-EINVAL);
status = acpi_get_handle(pr->handle, "_PCT", &handle);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"ACPI-based processor performance control unavailable\n"));
return_VALUE(0);
}
result = acpi_processor_get_performance_control(pr);
if (result)
return_VALUE(result);
result = acpi_processor_get_performance_states(pr);
if (result)
return_VALUE(result);
result = acpi_processor_get_platform_limit(pr);
if (result)
return_VALUE(result);
/*
* TBD: Don't trust the latency values we get from BIOS, but rather
* measure the latencies during run-time (e.g. get_latencies).
*/
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);
(*pr)->performance = performance;
return 0;
}
#endif
EXPORT_SYMBOL(acpi_processor_register_performance);
/* for the rest of it, check processor_perf.c */
/* --------------------------------------------------------------------------
......@@ -1484,9 +1040,8 @@ acpi_processor_apply_limit (
if (!pr->flags.limit)
return_VALUE(-ENODEV);
#ifdef CONFIG_ACPI_PROCESSOR_PERF
if (pr->flags.performance) {
px = pr->performance.platform_limit;
px = pr->performance_platform_limit;
if (pr->limit.user.px > px)
px = pr->limit.user.px;
if (pr->limit.thermal.px > px)
......@@ -1495,13 +1050,14 @@ acpi_processor_apply_limit (
struct cpufreq_policy policy;
policy.cpu = pr->id;
cpufreq_get_policy(&policy, pr->id);
policy.max = pr->performance.states[px].core_frequency * 1000;
policy.max = pr->performance->states[px].core_frequency * 1000; /* racy */
result = cpufreq_set_policy(&policy);
}
if (result)
goto end;
} else if (pr->performance_platform_limit) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Platform limit event detected. Consider using ACPI P-States CPUfreq driver\n"));
}
#endif
if (pr->flags.throttling) {
if (pr->limit.user.tx > tx)
......@@ -1560,7 +1116,7 @@ acpi_processor_set_thermal_limit (
/* Thermal limits are always relative to the current Px/Tx state. */
if (pr->flags.performance)
pr->limit.thermal.px = pr->performance.state;
pr->limit.thermal.px = pr->performance->state;
if (pr->flags.throttling)
pr->limit.thermal.tx = pr->throttling.state;
......@@ -1581,7 +1137,7 @@ acpi_processor_set_thermal_limit (
case ACPI_PROCESSOR_LIMIT_INCREMENT:
if (pr->flags.performance) {
if (px == (pr->performance.state_count - 1))
if (px == (pr->performance->state_count - 1))
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At maximum performance state\n"));
else {
......@@ -1600,7 +1156,7 @@ acpi_processor_set_thermal_limit (
case ACPI_PROCESSOR_LIMIT_DECREMENT:
if (pr->flags.performance) {
if (px == pr->performance.platform_limit)
if (px == pr->performance_platform_limit)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At minimum performance state\n"));
else {
......@@ -1650,247 +1206,6 @@ acpi_processor_get_limit_info (
return_VALUE(0);
}
/* --------------------------------------------------------------------------
cpufreq interface
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static int
acpi_cpufreq_setpolicy (
struct cpufreq_policy *policy)
{
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_VALUE(-EINVAL);
pr = processors[policy->cpu];
if (!pr)
return_VALUE(-EINVAL);
/* 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 */
result = acpi_processor_set_performance (pr, next_state);
return_VALUE(result);
}
static int
acpi_cpufreq_verify (
struct cpufreq_policy *policy)
{
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_VALUE(-EINVAL);
pr = processors[policy->cpu];
if (!pr)
return_VALUE(-EINVAL);
/* 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) {
/* round up now */
policy->max = pr->performance.states[next_larger_state].core_frequency * 1000;
}
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);
return_VALUE(0);
}
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 (!pr->flags.performance)
return_VALUE(0);
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
for (i=0;i<NR_CPUS;i++) {
driver->cpu_cur_freq[0] = pr->performance.states[current_state].core_frequency * 1000;
}
#endif
/* detect highest transition latency */
for (i=0;i<pr->performance.state_count;i++) {
if (pr->performance.states[i].transition_latency > driver->policy[0].cpuinfo.transition_latency)
driver->policy[0].cpuinfo.transition_latency = pr->performance.states[i].transition_latency;
}
driver->verify = &acpi_cpufreq_verify;
driver->setpolicy = &acpi_cpufreq_setpolicy;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "acpi-processor", CPUFREQ_NAME_LEN);
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].cpuinfo.max_freq = pr->performance.states[0].core_frequency * 1000;
driver->policy[i].cpuinfo.min_freq = pr->performance.states[pr->performance.state_count - 1].core_frequency * 1000;
driver->policy[i].cpuinfo.transition_latency = driver->policy[0].cpuinfo.transition_latency;
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)
......@@ -1987,82 +1302,6 @@ static int acpi_processor_power_open_fs(struct inode *inode, struct file *file)
PDE(inode)->data);
}
#ifdef CONFIG_ACPI_PROCESSOR_PERF
static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
{
struct acpi_processor *pr = (struct acpi_processor *)seq->private;
int i = 0;
ACPI_FUNCTION_TRACE("acpi_processor_perf_seq_show");
if (!pr)
goto end;
if (!pr->flags.performance) {
seq_puts(seq, "<not supported>\n");
goto end;
}
seq_printf(seq, "state count: %d\n"
"active state: P%d\n",
pr->performance.state_count,
pr->performance.state);
seq_puts(seq, "states:\n");
for (i = 0; i < pr->performance.state_count; i++)
seq_printf(seq, " %cP%d: %d MHz, %d mW, %d uS\n",
(i == pr->performance.state?'*':' '), i,
(u32) pr->performance.states[i].core_frequency,
(u32) pr->performance.states[i].power,
(u32) pr->performance.states[i].transition_latency);
end:
return 0;
}
static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_processor_perf_seq_show,
PDE(inode)->data);
}
static int
acpi_processor_write_performance (
struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
int result = 0;
struct acpi_processor *pr = (struct acpi_processor *) data;
char state_string[12] = {'\0'};
unsigned int new_state = 0;
struct cpufreq_policy policy;
ACPI_FUNCTION_TRACE("acpi_processor_write_performance");
if (!pr || (count > sizeof(state_string) - 1))
return_VALUE(-EINVAL);
if (copy_from_user(state_string, buffer, count))
return_VALUE(-EFAULT);
state_string[count] = '\0';
new_state = simple_strtoul(state_string, NULL, 0);
cpufreq_get_policy(&policy, pr->id);
policy.cpu = pr->id;
policy.max = pr->performance.states[new_state].core_frequency * 1000;
result = cpufreq_set_policy(&policy);
if (result)
return_VALUE(result);
return_VALUE(count);
}
#endif
static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset)
{
struct acpi_processor *pr = (struct acpi_processor *)seq->private;
......@@ -2155,7 +1394,7 @@ static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset)
"user limit: P%d:T%d\n"
"thermal limit: P%d:T%d\n",
pr->limit.state.px, pr->limit.state.tx,
pr->flags.performance?pr->performance.platform_limit:0,
pr->flags.performance?pr->performance_platform_limit:0,
pr->limit.user.px, pr->limit.user.tx,
pr->limit.thermal.px, pr->limit.thermal.tx);
......@@ -2202,8 +1441,8 @@ acpi_processor_write_limit (
}
if (pr->flags.performance) {
if ((px < pr->performance.platform_limit)
|| (px > (pr->performance.state_count - 1))) {
if ((px < pr->performance_platform_limit)
|| (px > (pr->performance->state_count - 1))) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid px\n"));
return_VALUE(-EINVAL);
}
......@@ -2263,21 +1502,6 @@ acpi_processor_add_fs (
entry->data = acpi_driver_data(device);
}
#ifdef CONFIG_ACPI_PROCESSOR_PERF
/* 'performance' [R/W] */
entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
if (!entry)
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to create '%s' fs entry\n",
ACPI_PROCESSOR_FILE_PERFORMANCE));
else {
entry->proc_fops = &acpi_processor_perf_fops;
entry->write_proc = acpi_processor_write_performance;
entry->data = acpi_driver_data(device);
}
#endif
/* 'throttling' [R/W] */
entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
......@@ -2397,8 +1621,9 @@ acpi_processor_get_info (
}
acpi_processor_get_power_info(pr);
acpi_processor_get_performance_info(pr);
acpi_cpufreq_init(pr);
pr->flags.performance = 0;
pr->performance_platform_limit = 0;
acpi_processor_get_platform_limit(pr);
acpi_processor_get_throttling_info(pr);
acpi_processor_get_limit_info(pr);
......@@ -2426,18 +1651,11 @@ acpi_processor_notify (
switch (event) {
case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
#ifdef CONFIG_ACPI_PROCESSOR_PERF
result = acpi_processor_get_platform_limit(pr);
if (!result)
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,
pr->performance.platform_limit);
pr->performance_platform_limit);
break;
case ACPI_PROCESSOR_NOTIFY_POWER:
/* TBD */
......@@ -2511,8 +1729,6 @@ acpi_processor_add (
for (i=1; i<ACPI_C_STATE_COUNT; i++)
if (pr->power.states[i].valid)
printk(" C%d", i);
if (pr->flags.performance)
printk(", %d performance states", pr->performance.state_count);
if (pr->flags.throttling)
printk(", %d throttling states", pr->throttling.state_count);
printk(")\n");
......@@ -2554,7 +1770,6 @@ acpi_processor_remove (
return_VALUE(-ENODEV);
}
acpi_cpufreq_exit(pr);
acpi_processor_remove_fs(device);
processors[pr->id] = NULL;
......
#ifndef __ACPI_PROCESSOR_H
#define __ACPI_PROCESSOR_H
#include <linux/kernel.h>
#define ACPI_PROCESSOR_BUSY_METRIC 10
#define ACPI_PROCESSOR_MAX_POWER ACPI_C_STATE_COUNT
#define ACPI_PROCESSOR_MAX_C2_LATENCY 100
#define ACPI_PROCESSOR_MAX_C3_LATENCY 1000
#define ACPI_PROCESSOR_MAX_PERFORMANCE 8
#define ACPI_PROCESSOR_MAX_THROTTLING 16
#define ACPI_PROCESSOR_MAX_THROTTLE 250 /* 25% */
#define ACPI_PROCESSOR_MAX_DUTY_WIDTH 4
/* Power Management */
struct acpi_processor_cx_policy {
u32 count;
int state;
struct {
u32 time;
u32 ticks;
u32 count;
u32 bm;
} threshold;
};
struct acpi_processor_cx {
u8 valid;
u32 address;
u32 latency;
u32 latency_ticks;
u32 power;
u32 usage;
struct acpi_processor_cx_policy promotion;
struct acpi_processor_cx_policy demotion;
};
struct acpi_processor_power {
int state;
int default_state;
u32 bm_activity;
struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER];
};
/* Performance Management */
struct acpi_pct_register {
u8 descriptor;
u16 length;
u8 space_id;
u8 bit_width;
u8 bit_offset;
u8 reserved;
u64 address;
} __attribute__ ((packed));
struct acpi_processor_px {
acpi_integer core_frequency; /* megahertz */
acpi_integer power; /* milliWatts */
acpi_integer transition_latency; /* microseconds */
acpi_integer bus_master_latency; /* microseconds */
acpi_integer control; /* control value */
acpi_integer status; /* success indicator */
};
struct acpi_processor_performance {
int state;
int platform_limit;
u16 control_register;
u16 status_register;
int state_count;
struct acpi_processor_px states[ACPI_PROCESSOR_MAX_PERFORMANCE];
struct cpufreq_frequency_table freq_table[ACPI_PROCESSOR_MAX_PERFORMANCE];
struct acpi_processor *pr;
};
/* Throttling Control */
struct acpi_processor_tx {
u16 power;
u16 performance;
};
struct acpi_processor_throttling {
int state;
u32 address;
u8 duty_offset;
u8 duty_width;
int state_count;
struct acpi_processor_tx states[ACPI_PROCESSOR_MAX_THROTTLING];
};
/* Limit Interface */
struct acpi_processor_lx {
int px; /* performace state */
int tx; /* throttle level */
};
struct acpi_processor_limit {
struct acpi_processor_lx state; /* current limit */
struct acpi_processor_lx thermal; /* thermal limit */
struct acpi_processor_lx user; /* user limit */
};
struct acpi_processor_flags {
u8 power:1;
u8 performance:1;
u8 throttling:1;
u8 limit:1;
u8 bm_control:1;
u8 bm_check:1;
u8 reserved:2;
};
struct acpi_processor {
acpi_handle handle;
u32 acpi_id;
u32 id;
int performance_platform_limit;
struct acpi_processor_flags flags;
struct acpi_processor_power power;
struct acpi_processor_performance *performance;
struct acpi_processor_throttling throttling;
struct acpi_processor_limit limit;
};
extern int acpi_processor_get_platform_limit (
struct acpi_processor* pr);
extern int acpi_processor_register_performance (
struct acpi_processor_performance * performance,
struct acpi_processor ** pr,
unsigned int cpu);
#endif
......@@ -32,6 +32,7 @@
#include <linux/list.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <asm/acpi.h>
......
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