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 ...@@ -983,6 +983,17 @@ config CPU_FREQ_24_API
If in doubt, say N. 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 config X86_POWERNOW_K6
tristate "AMD Mobile K6-2/K6-3 PowerNow!" tristate "AMD Mobile K6-2/K6-3 PowerNow!"
depends on CPU_FREQ depends on CPU_FREQ
......
...@@ -5,3 +5,10 @@ obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o ...@@ -5,3 +5,10 @@ obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
obj-$(CONFIG_X86_LONGRUN) += longrun.o obj-$(CONFIG_X86_LONGRUN) += longrun.o
obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.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 ...@@ -116,14 +116,6 @@ 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
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 config ACPI_THERMAL
tristate "Thermal Zone" tristate "Thermal Zone"
depends on ACPI_PROCESSOR depends on ACPI_PROCESSOR
......
...@@ -35,18 +35,19 @@ ...@@ -35,18 +35,19 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <asm/io.h> #include <linux/cpufreq.h>
#include <asm/system.h>
#include <asm/delay.h>
#include <linux/compatmac.h> #include <linux/compatmac.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/delay.h>
#include "acpi_bus.h" #include "acpi_bus.h"
#include "acpi_drivers.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_COMPONENT 0x01000000
#define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_CLASS "processor"
...@@ -64,17 +65,6 @@ ...@@ -64,17 +65,6 @@
#define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */ #define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */
#define C3_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}; const u32 POWER_OF_2[] = {1,2,4,8,16,32,64};
...@@ -107,118 +97,6 @@ static struct acpi_driver acpi_processor_driver = { ...@@ -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 { struct acpi_processor_errata {
u8 smp; u8 smp;
...@@ -262,18 +140,6 @@ static struct acpi_processor *processors[NR_CPUS]; ...@@ -262,18 +140,6 @@ 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;
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 Errata Handling
...@@ -880,8 +746,7 @@ acpi_processor_get_power_info ( ...@@ -880,8 +746,7 @@ acpi_processor_get_power_info (
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Performance Management Performance Management
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_PROCESSOR_PERF int
static int
acpi_processor_get_platform_limit ( acpi_processor_get_platform_limit (
struct acpi_processor* pr) struct acpi_processor* pr)
{ {
...@@ -903,342 +768,33 @@ acpi_processor_get_platform_limit ( ...@@ -903,342 +768,33 @@ acpi_processor_get_platform_limit (
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
} }
pr->performance.platform_limit = (int) ppc; pr->performance_platform_limit = (int) ppc;
return_VALUE(0); return_VALUE(0);
} }
EXPORT_SYMBOL(acpi_processor_get_platform_limit);
int
static int acpi_processor_register_performance (
acpi_processor_get_performance_control ( struct acpi_processor_performance * performance,
struct acpi_processor *pr) struct acpi_processor ** pr,
{ unsigned int cpu)
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; ACPI_FUNCTION_TRACE("acpi_processor_register_performance");
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) *pr = processors[cpu];
if (!*pr)
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
if (state >= pr->performance.state_count) { if ((*pr)->performance)
ACPI_DEBUG_PRINT((ACPI_DB_WARN, return_VALUE(-EBUSY);
"Invalid target state (P%d)\n", state));
return_VALUE(-ENODEV);
}
if (state < pr->performance.platform_limit) { (*pr)->performance = performance;
ACPI_DEBUG_PRINT((ACPI_DB_WARN, return 0;
"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);
} }
#endif EXPORT_SYMBOL(acpi_processor_register_performance);
/* for the rest of it, check processor_perf.c */
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
...@@ -1484,9 +1040,8 @@ acpi_processor_apply_limit ( ...@@ -1484,9 +1040,8 @@ 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)
...@@ -1495,13 +1050,14 @@ acpi_processor_apply_limit ( ...@@ -1495,13 +1050,14 @@ acpi_processor_apply_limit (
struct cpufreq_policy policy; struct cpufreq_policy policy;
policy.cpu = pr->id; policy.cpu = pr->id;
cpufreq_get_policy(&policy, 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); result = cpufreq_set_policy(&policy);
} }
if (result) if (result)
goto end; 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->flags.throttling) {
if (pr->limit.user.tx > tx) if (pr->limit.user.tx > tx)
...@@ -1560,7 +1116,7 @@ acpi_processor_set_thermal_limit ( ...@@ -1560,7 +1116,7 @@ acpi_processor_set_thermal_limit (
/* Thermal limits are always relative to the current Px/Tx state. */ /* Thermal limits are always relative to the current Px/Tx state. */
if (pr->flags.performance) if (pr->flags.performance)
pr->limit.thermal.px = pr->performance.state; pr->limit.thermal.px = pr->performance->state;
if (pr->flags.throttling) if (pr->flags.throttling)
pr->limit.thermal.tx = pr->throttling.state; pr->limit.thermal.tx = pr->throttling.state;
...@@ -1581,7 +1137,7 @@ acpi_processor_set_thermal_limit ( ...@@ -1581,7 +1137,7 @@ acpi_processor_set_thermal_limit (
case ACPI_PROCESSOR_LIMIT_INCREMENT: case ACPI_PROCESSOR_LIMIT_INCREMENT:
if (pr->flags.performance) { if (pr->flags.performance) {
if (px == (pr->performance.state_count - 1)) if (px == (pr->performance->state_count - 1))
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At maximum performance state\n")); "At maximum performance state\n"));
else { else {
...@@ -1600,7 +1156,7 @@ acpi_processor_set_thermal_limit ( ...@@ -1600,7 +1156,7 @@ acpi_processor_set_thermal_limit (
case ACPI_PROCESSOR_LIMIT_DECREMENT: case ACPI_PROCESSOR_LIMIT_DECREMENT:
if (pr->flags.performance) { if (pr->flags.performance) {
if (px == pr->performance.platform_limit) if (px == pr->performance_platform_limit)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At minimum performance state\n")); "At minimum performance state\n"));
else { else {
...@@ -1650,247 +1206,6 @@ acpi_processor_get_limit_info ( ...@@ -1650,247 +1206,6 @@ acpi_processor_get_limit_info (
return_VALUE(0); 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) FS Interface (/proc)
...@@ -1987,82 +1302,6 @@ static int acpi_processor_power_open_fs(struct inode *inode, struct file *file) ...@@ -1987,82 +1302,6 @@ static int acpi_processor_power_open_fs(struct inode *inode, struct file *file)
PDE(inode)->data); 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) static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset)
{ {
struct acpi_processor *pr = (struct acpi_processor *)seq->private; 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) ...@@ -2155,7 +1394,7 @@ static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset)
"user limit: P%d:T%d\n" "user limit: P%d:T%d\n"
"thermal limit: P%d:T%d\n", "thermal limit: P%d:T%d\n",
pr->limit.state.px, pr->limit.state.tx, 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.user.px, pr->limit.user.tx,
pr->limit.thermal.px, pr->limit.thermal.tx); pr->limit.thermal.px, pr->limit.thermal.tx);
...@@ -2202,8 +1441,8 @@ acpi_processor_write_limit ( ...@@ -2202,8 +1441,8 @@ acpi_processor_write_limit (
} }
if (pr->flags.performance) { if (pr->flags.performance) {
if ((px < pr->performance.platform_limit) if ((px < pr->performance_platform_limit)
|| (px > (pr->performance.state_count - 1))) { || (px > (pr->performance->state_count - 1))) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid px\n")); ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid px\n"));
return_VALUE(-EINVAL); return_VALUE(-EINVAL);
} }
...@@ -2263,21 +1502,6 @@ acpi_processor_add_fs ( ...@@ -2263,21 +1502,6 @@ acpi_processor_add_fs (
entry->data = acpi_driver_data(device); 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] */ /* 'throttling' [R/W] */
entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING, entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
...@@ -2397,8 +1621,9 @@ acpi_processor_get_info ( ...@@ -2397,8 +1621,9 @@ acpi_processor_get_info (
} }
acpi_processor_get_power_info(pr); acpi_processor_get_power_info(pr);
acpi_processor_get_performance_info(pr); pr->flags.performance = 0;
acpi_cpufreq_init(pr); pr->performance_platform_limit = 0;
acpi_processor_get_platform_limit(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);
...@@ -2426,18 +1651,11 @@ acpi_processor_notify ( ...@@ -2426,18 +1651,11 @@ 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);
break; break;
case ACPI_PROCESSOR_NOTIFY_POWER: case ACPI_PROCESSOR_NOTIFY_POWER:
/* TBD */ /* TBD */
...@@ -2511,8 +1729,6 @@ acpi_processor_add ( ...@@ -2511,8 +1729,6 @@ acpi_processor_add (
for (i=1; i<ACPI_C_STATE_COUNT; i++) for (i=1; i<ACPI_C_STATE_COUNT; i++)
if (pr->power.states[i].valid) if (pr->power.states[i].valid)
printk(" C%d", i); printk(" C%d", i);
if (pr->flags.performance)
printk(", %d performance states", pr->performance.state_count);
if (pr->flags.throttling) if (pr->flags.throttling)
printk(", %d throttling states", pr->throttling.state_count); printk(", %d throttling states", pr->throttling.state_count);
printk(")\n"); printk(")\n");
...@@ -2554,7 +1770,6 @@ acpi_processor_remove ( ...@@ -2554,7 +1770,6 @@ 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;
......
#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 @@ ...@@ -32,6 +32,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <acpi/acpi.h> #include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <asm/acpi.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