Commit fdfec545 authored by Len Brown's avatar Len Brown

Merge intel.com:/home/lenb/bk/linux-2.6.3

into intel.com:/home/lenb/src/linux-acpi-test-2.6.3
parents f4d86110 f817979f
......@@ -818,6 +818,11 @@ S: 1382 Bordeaux Drive
S: Sunnyvale, CA 94087
S: USA
N: Bruno Ducrot
E: ducrot@poupinou.org
D: CPUFreq and ACPI bugfixes.
S: Mougin, France
N: Don Dugger
E: n0ano@valinux.com
D: Linux/IA-64
......
......@@ -32,6 +32,7 @@
#include <asm/io_apic.h>
#include <asm/apic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mpspec.h>
#if defined (CONFIG_X86_LOCAL_APIC)
......@@ -45,8 +46,8 @@
int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */
int acpi_ht __initdata = 1; /* enable HT */
int acpi_lapic = 0;
int acpi_ioapic = 0;
int acpi_lapic;
int acpi_ioapic;
/* --------------------------------------------------------------------------
Boot-time Configuration
......@@ -471,7 +472,7 @@ acpi_boot_init (void)
* and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value).
*/
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
return result;
......@@ -479,7 +480,8 @@ acpi_boot_init (void)
mp_register_lapic_address(acpi_lapic_addr);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic,
MAX_APICS);
if (!result) {
printk(KERN_ERR PREFIX "No LAPIC entries present\n");
/* TBD: Cleanup to allow fallback to MPS */
......@@ -491,7 +493,7 @@ acpi_boot_init (void)
return result;
}
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
/* TBD: Cleanup to allow fallback to MPS */
......@@ -528,7 +530,7 @@ acpi_boot_init (void)
return 1;
}
result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic);
result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS);
if (!result) {
printk(KERN_ERR PREFIX "No IOAPIC entries present\n");
return -ENODEV;
......@@ -541,14 +543,14 @@ acpi_boot_init (void)
/* Build a default routing table for legacy (ISA) interrupts. */
mp_config_acpi_legacy_irqs();
result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr);
result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src);
result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, NR_IRQ_VECTORS);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
/* TBD: Cleanup to allow fallback to MPS */
......
/*
* acpi_processor_perf.c - ACPI Processor P-States Driver ($Revision: 1.3 $)
* acpi-cpufreq-io.c - ACPI Processor P-States Driver ($Revision: 1.3 $)
*
* 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>
* Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
......@@ -42,7 +42,6 @@
#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")
......@@ -52,184 +51,13 @@ MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME);
MODULE_LICENSE("GPL");
static struct acpi_processor_performance *performance;
static int
acpi_processor_get_performance_control (
struct acpi_processor_performance *perf)
{
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(perf->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;
}
perf->control_register = (u16) reg->address;
perf->control_register_bit_width = reg->bit_width;
/*
* 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;
}
perf->status_register = (u16) reg->address;
perf->status_register_bit_width = reg->bit_width;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"control_register[0x%04x] status_register[0x%04x]\n",
perf->control_register,
perf->status_register));
end:
acpi_os_free(buffer.pointer);
return_VALUE(result);
}
static int
acpi_processor_get_performance_states (
struct acpi_processor_performance * perf)
{
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(perf->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) {
perf->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
perf->state_count = pss->package.count;
if (perf->state_count > 1)
perf->pr->flags.performance = 1;
for (i = 0; i < perf->state_count; i++) {
struct acpi_processor_px *px = &(perf->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;
}
if (!px->core_frequency) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data: freq is zero\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));
}
struct cpufreq_acpi_io {
struct acpi_processor_performance acpi_data;
struct cpufreq_frequency_table *freq_table;
};
end:
acpi_os_free(buffer.pointer);
static struct cpufreq_acpi_io *acpi_io_data[NR_CPUS];
return_VALUE(result);
}
static int
acpi_processor_write_port(
......@@ -270,7 +98,8 @@ acpi_processor_read_port(
static int
acpi_processor_set_performance (
struct acpi_processor_performance *perf,
struct cpufreq_acpi_io *data,
unsigned int cpu,
int state)
{
u16 port = 0;
......@@ -282,38 +111,19 @@ acpi_processor_set_performance (
ACPI_FUNCTION_TRACE("acpi_processor_set_performance");
if (!perf || !perf->pr)
return_VALUE(-EINVAL);
if (!perf->pr->flags.performance)
return_VALUE(-ENODEV);
if (state >= perf->state_count) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Invalid target state (P%d)\n", state));
return_VALUE(-ENODEV);
}
if (state < perf->pr->performance_platform_limit) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"Platform limit (P%d) overrides target state (P%d)\n",
perf->pr->performance_platform_limit, state));
return_VALUE(-ENODEV);
}
if (state == perf->state) {
if (state == data->acpi_data.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",
perf->state, state));
data->acpi_data.state, state));
/* cpufreq frequency struct */
cpufreq_freqs.cpu = perf->pr->id;
cpufreq_freqs.old = perf->states[perf->state].core_frequency;
cpufreq_freqs.new = perf->states[state].core_frequency;
cpufreq_freqs.cpu = cpu;
cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency;
cpufreq_freqs.new = data->freq_table[state].frequency;
/* notify cpufreq */
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
......@@ -323,9 +133,9 @@ acpi_processor_set_performance (
* control_register.
*/
port = perf->control_register;
bit_width = perf->control_register_bit_width;
value = (u32) perf->states[state].control;
port = data->acpi_data.control_register.address;
bit_width = data->acpi_data.control_register.bit_width;
value = (u32) data->acpi_data.states[state].control;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Writing 0x%08x to port 0x%04x\n", value, port));
......@@ -344,12 +154,12 @@ acpi_processor_set_performance (
* giving up.
*/
port = perf->status_register;
bit_width = perf->status_register_bit_width;
port = data->acpi_data.status_register.address;
bit_width = data->acpi_data.status_register.bit_width;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Looking for 0x%08x from port 0x%04x\n",
(u32) perf->states[state].status, port));
(u32) data->acpi_data.states[state].status, port));
for (i=0; i<100; i++) {
ret = acpi_processor_read_port(port, bit_width, &value);
......@@ -358,7 +168,7 @@ acpi_processor_set_performance (
"Invalid port width 0x%04x\n", bit_width));
return_VALUE(ret);
}
if (value == (u32) perf->states[state].status)
if (value == (u32) data->acpi_data.states[state].status)
break;
udelay(10);
}
......@@ -366,7 +176,7 @@ acpi_processor_set_performance (
/* notify cpufreq */
cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
if (value != (u32) perf->states[state].status) {
if (value != (u32) data->acpi_data.states[state].status) {
unsigned int tmp = cpufreq_freqs.new;
cpufreq_freqs.new = cpufreq_freqs.old;
cpufreq_freqs.old = tmp;
......@@ -380,169 +190,33 @@ acpi_processor_set_performance (
"Transition successful after %d microseconds\n",
i * 10));
perf->state = state;
data->acpi_data.state = state;
return_VALUE(0);
}
#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
/* /proc/acpi/processor/../performance interface (DEPRECATED) */
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_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 || !pr->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 __user *buffer,
size_t count,
loff_t *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 void
acpi_cpufreq_add_file (
struct acpi_processor *pr)
{
struct proc_dir_entry *entry = NULL;
struct acpi_device *device = NULL;
ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile");
if (acpi_bus_get_device(pr->handle, &device))
return_VOID;
/* 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->proc_fops->write = acpi_processor_write_performance;
entry->data = acpi_driver_data(device);
}
return_VOID;
}
static void
acpi_cpufreq_remove_file (
struct acpi_processor *pr)
{
struct acpi_device *device = NULL;
ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile");
if (acpi_bus_get_device(pr->handle, &device))
return_VOID;
/* remove file 'performance' */
remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
acpi_device_dir(device));
return_VOID;
}
#else
static void acpi_cpufreq_add_file (struct acpi_processor *pr) { return; }
static void acpi_cpufreq_remove_file (struct acpi_processor *pr) { return; }
#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
static int
acpi_cpufreq_target (
struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct acpi_processor_performance *perf = &performance[policy->cpu];
struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
unsigned int next_state = 0;
unsigned int result = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_setpolicy");
result = cpufreq_frequency_table_target(policy,
&perf->freq_table[perf->pr->limit.state.px],
data->freq_table,
target_freq,
relation,
&next_state);
if (result)
return_VALUE(result);
result = acpi_processor_set_performance (perf, next_state);
result = acpi_processor_set_performance (data, policy->cpu, next_state);
return_VALUE(result);
}
......@@ -553,119 +227,110 @@ acpi_cpufreq_verify (
struct cpufreq_policy *policy)
{
unsigned int result = 0;
struct acpi_processor_performance *perf = &performance[policy->cpu];
struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
ACPI_FUNCTION_TRACE("acpi_cpufreq_verify");
result = cpufreq_frequency_table_verify(policy,
&perf->freq_table[perf->pr->limit.state.px]);
cpufreq_verify_within_limits(
policy,
perf->states[perf->state_count - 1].core_frequency * 1000,
perf->states[perf->pr->limit.state.px].core_frequency * 1000);
data->freq_table);
return_VALUE(result);
}
static int
acpi_processor_get_performance_info (
struct acpi_processor_performance *perf)
{
int result = 0;
acpi_status status = AE_OK;
acpi_handle handle = NULL;
ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info");
if (!perf || !perf->pr || !perf->pr->handle)
return_VALUE(-EINVAL);
status = acpi_get_handle(perf->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(perf);
if (result)
return_VALUE(result);
result = acpi_processor_get_performance_states(perf);
if (result)
return_VALUE(result);
result = acpi_processor_get_platform_limit(perf->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;
struct acpi_processor_performance *perf = &performance[policy->cpu];
struct acpi_device *device;
struct cpufreq_acpi_io *data;
unsigned int result = 0;
ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_init");
acpi_processor_register_performance(perf, &pr, cpu);
data = kmalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
if (!data)
return_VALUE(-ENOMEM);
memset(data, 0, sizeof(struct cpufreq_acpi_io));
pr = performance[cpu].pr;
if (!pr)
return_VALUE(-ENODEV);
acpi_io_data[cpu] = data;
result = acpi_processor_get_performance_info(perf);
result = acpi_processor_register_performance(&data->acpi_data, cpu);
if (result)
return_VALUE(-ENODEV);
goto err_free;
/* capability check */
if (!pr->flags.performance)
return_VALUE(-ENODEV);
if (data->acpi_data.state_count <= 1) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No P-States\n"));
result = -ENODEV;
goto err_unreg;
}
if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO) ||
(data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported address space [%d, %d]\n",
(u32) (data->acpi_data.control_register.space_id),
(u32) (data->acpi_data.status_register.space_id)));
result = -ENODEV;
goto err_unreg;
}
/* alloc freq_table */
data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * (data->acpi_data.state_count + 1), GFP_KERNEL);
if (!data->freq_table) {
result = -ENOMEM;
goto err_unreg;
}
/* detect transition latency */
policy->cpuinfo.transition_latency = 0;
for (i=0;i<perf->state_count;i++) {
if ((perf->states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency = perf->states[i].transition_latency * 1000;
for (i=0; i<data->acpi_data.state_count; i++) {
if ((data->acpi_data.states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency = data->acpi_data.states[i].transition_latency * 1000;
}
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
policy->cur = perf->states[pr->limit.state.px].core_frequency * 1000;
/*
* The current speed is unknown and not detectable by ACPI... argh! Assume
* it's P0, it will be set to this value later during initialization.
*/
policy->cur = data->acpi_data.states[0].core_frequency * 1000;
/* table init */
for (i=0; i<=perf->state_count; i++)
for (i=0; i<=data->acpi_data.state_count; i++)
{
perf->freq_table[i].index = i;
if (i<perf->state_count)
perf->freq_table[i].frequency = perf->states[i].core_frequency * 1000;
data->freq_table[i].index = i;
if (i<data->acpi_data.state_count)
data->freq_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000;
else
perf->freq_table[i].frequency = CPUFREQ_TABLE_END;
data->freq_table[i].frequency = CPUFREQ_TABLE_END;
}
result = cpufreq_frequency_table_cpuinfo(policy, &perf->freq_table[0]);
acpi_cpufreq_add_file(pr);
result = cpufreq_frequency_table_cpuinfo(policy, &data->freq_table[0]);
if (result) {
goto err_freqfree;
}
if (acpi_bus_get_device(pr->handle, &device))
device = NULL;
printk(KERN_INFO "cpufreq: %s - ACPI performance management activated.\n",
device ? acpi_device_bid(device) : "CPU??");
for (i = 0; i < pr->performance->state_count; i++)
printk(KERN_INFO "cpufreq: CPU%u - ACPI performance management activated.\n",
cpu);
for (i = 0; i < data->acpi_data.state_count; i++)
printk(KERN_INFO "cpufreq: %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);
(i == data->acpi_data.state?'*':' '), i,
(u32) data->acpi_data.states[i].core_frequency,
(u32) data->acpi_data.states[i].power,
(u32) data->acpi_data.states[i].transition_latency);
return_VALUE(result);
err_freqfree:
kfree(data->freq_table);
err_unreg:
acpi_processor_unregister_performance(&data->acpi_data, cpu);
err_free:
kfree(data);
acpi_io_data[cpu] = NULL;
return_VALUE(result);
}
......@@ -674,11 +339,16 @@ static int
acpi_cpufreq_cpu_exit (
struct cpufreq_policy *policy)
{
struct acpi_processor *pr = performance[policy->cpu].pr;
struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_exit");
acpi_cpufreq_remove_file(pr);
if (data) {
acpi_io_data[policy->cpu] = NULL;
acpi_processor_unregister_performance(&data->acpi_data, policy->cpu);
kfree(data);
}
return_VALUE(0);
}
......@@ -698,97 +368,11 @@ static int __init
acpi_cpufreq_init (void)
{
int result = 0;
int current_state = 0;
int i = 0;
struct acpi_processor *pr = NULL;
struct acpi_processor_performance *perf = NULL;
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);
memset(performance, 0, NR_CPUS * sizeof(struct acpi_processor_performance));
/* register struct acpi_processor_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]);
}
/* test it on one CPU */
for (i=0; i<NR_CPUS; i++) {
if (!cpu_online(i))
continue;
pr = performance[i].pr;
if (pr && pr->flags.performance)
goto found_capable_cpu;
}
result = -ENODEV;
goto err0;
found_capable_cpu:
result = cpufreq_register_driver(&acpi_cpufreq_driver);
if (result)
goto err0;
perf = pr->performance;
current_state = perf->state;
if (current_state == pr->limit.state.px) {
result = acpi_processor_set_performance(perf, (perf->state_count - 1));
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
result = -ENODEV;
goto err1;
}
}
result = acpi_processor_set_performance(perf, pr->limit.state.px);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
result = -ENODEV;
goto err1;
}
if (current_state != 0) {
result = acpi_processor_set_performance(perf, current_state);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Disabled P-States due to failure while switching.\n"));
result = -ENODEV;
goto err1;
}
}
return_VALUE(0);
/* error handling */
err1:
cpufreq_unregister_driver(&acpi_cpufreq_driver);
err0:
/* unregister struct acpi_processor_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);
printk(KERN_INFO "cpufreq: No CPUs supporting ACPI performance management found.\n");
return_VALUE(result);
}
......@@ -796,28 +380,10 @@ acpi_cpufreq_init (void)
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_processor_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;
}
......
......@@ -191,8 +191,6 @@ acpi_parse_lsapic (acpi_table_entry_header *header)
if (!lsapic->flags.enabled)
printk(" disabled");
else if (available_cpus >= NR_CPUS)
printk(" ignored (increase NR_CPUS)");
else {
printk(" enabled");
#ifdef CONFIG_SMP
......@@ -395,12 +393,6 @@ acpi_numa_memory_affinity_init (struct acpi_table_memory_affinity *ma)
size = ma->length_hi;
size = (size << 32) | ma->length_lo;
if (num_memblks >= NR_MEMBLKS) {
printk(KERN_ERR "Too many mem chunks in SRAT. Ignoring %ld MBytes at %lx\n",
size/(1024*1024), paddr);
return;
}
/* Ignore disabled entries */
if (!ma->flags.enabled)
return;
......@@ -552,29 +544,29 @@ acpi_boot_init (void)
/* Local APIC */
if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr) < 0)
if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0) < 0)
printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic) < 1)
if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic, NR_CPUS) < 1)
printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries\n");
if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi) < 0)
if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0) < 0)
printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
/* I/O APIC */
if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic) < 1)
if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic, NR_IOSAPICS) < 1)
printk(KERN_ERR PREFIX "Error parsing MADT - no IOSAPIC entries\n");
/* System-Level Interrupt Routing */
if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src) < 0)
if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src, ACPI_MAX_PLATFORM_INTERRUPTS) < 0)
printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n");
if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr) < 0)
if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, 0) < 0)
printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src) < 0)
if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, 0) < 0)
printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
skip_madt:
......
......@@ -114,7 +114,7 @@ static struct iosapic {
char *addr; /* base address of IOSAPIC */
unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */
unsigned short num_rte; /* number of RTE in this IOSAPIC */
} iosapic_lists[256];
} iosapic_lists[NR_IOSAPICS];
static int num_iosapic;
......
......@@ -51,8 +51,8 @@
int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */
int acpi_ht __initdata = 1; /* enable HT */
int acpi_lapic = 0;
int acpi_ioapic = 0;
int acpi_lapic;
int acpi_ioapic;
/* --------------------------------------------------------------------------
Boot-time Configuration
......@@ -439,7 +439,7 @@ acpi_boot_init (void)
* and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value).
*/
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
return result;
......@@ -447,7 +447,8 @@ acpi_boot_init (void)
mp_register_lapic_address(acpi_lapic_addr);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic,
MAX_APICS);
if (!result) {
printk(KERN_ERR PREFIX "No LAPIC entries present\n");
/* TBD: Cleanup to allow fallback to MPS */
......@@ -459,7 +460,7 @@ acpi_boot_init (void)
return result;
}
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi);
result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
/* TBD: Cleanup to allow fallback to MPS */
......@@ -496,7 +497,7 @@ acpi_boot_init (void)
return 1;
}
result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic);
result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS);
if (!result) {
printk(KERN_ERR PREFIX "No IOAPIC entries present\n");
return -ENODEV;
......@@ -509,14 +510,15 @@ acpi_boot_init (void)
/* Build a default routing table for legacy (ISA) interrupts. */
mp_config_acpi_legacy_irqs();
result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr);
result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
/* TBD: Cleanup to allow fallback to MPS */
return result;
}
result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src);
result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src,
NR_IRQ_VECTORS);
if (result < 0) {
printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
/* TBD: Cleanup to allow fallback to MPS */
......
......@@ -2,7 +2,7 @@
* asus_acpi.c - Asus Laptop ACPI Extras
*
*
* Copyright (C) 2002, 2003 Julien Lerouge, Karol Kozimor
* Copyright (C) 2002, 2003, 2004 Julien Lerouge, Karol Kozimor
*
* 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
......@@ -23,16 +23,16 @@
* http://sourceforge.net/projects/acpi4asus/
*
* Credits:
* Pontus Fuchs - Helper functions, cleanup
* Johann Wiesner - Small compile fixes
* John Belmonte - ACPI code for Toshiba laptop was a good starting point.
*
* TODO:
* add Fn key status
* Add mode selection on module loading (parameter) -> still necessary?
* Complete display switching -- may require dirty hacks?
* Complete display switching -- may require dirty hacks or calling _DOS?
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
......@@ -41,12 +41,13 @@
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#define ASUS_ACPI_VERSION "0.26"
#define ASUS_ACPI_VERSION "0.27"
#define PROC_ASUS "asus" //the directory
#define PROC_MLED "mled"
#define PROC_WLED "wled"
#define PROC_INFOS "info"
#define PROC_TLED "tled"
#define PROC_INFO "info"
#define PROC_LCD "lcd"
#define PROC_BRN "brn"
#define PROC_DISP "disp"
......@@ -67,6 +68,7 @@
*/
#define MLED_ON 0x01 //is MLED ON ?
#define WLED_ON 0x02
#define TLED_ON 0x04
MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
MODULE_DESCRIPTION(ACPI_HOTK_NAME);
......@@ -81,22 +83,25 @@ MODULE_PARM(asus_gid, "i");
MODULE_PARM_DESC(gid, "GID for entries in /proc/acpi/asus.\n");
/* For each model, all features implemented */
/* For each model, all features implemented,
* those marked with R are relative to HOTK, A for absolute */
struct model_data {
char *name; //name of the laptop
char *mt_mled; //method to handle mled
char *mled_status; //node to handle mled reading
char *mt_wled; //method to handle wled
char *wled_status; //node to handle wled reading
char *mt_lcd_switch; //method to turn LCD ON/OFF
char *lcd_status; //node to read LCD panel state
char *brightness_up; //method to set brightness up
char *brightness_down; //guess what ?
char *brightness_set; //method to set absolute brightness
char *brightness_get; //method to get absolute brightness
char *brightness_status;//node to get brightness
char *display_set; //method to set video output
char *display_get; //method to get video output
char *name; //name of the laptop________________A
char *mt_mled; //method to handle mled_____________R
char *mled_status; //node to handle mled reading_______A
char *mt_wled; //method to handle wled_____________R
char *wled_status; //node to handle wled reading_______A
char *mt_tled; //method to handle tled_____________R
char *tled_status; //node to handle tled reading_______A
char *mt_lcd_switch; //method to turn LCD ON/OFF_________A
char *lcd_status; //node to read LCD panel state______A
char *brightness_up; //method to set brightness up_______A
char *brightness_down; //guess what ?______________________A
char *brightness_set; //method to set absolute brightness_R
char *brightness_get; //method to get absolute brightness_R
char *brightness_status; //node to get brightness____________A
char *display_set; //method to set video output________R
char *display_get; //method to get video output________R
};
/*
......@@ -108,87 +113,235 @@ struct asus_hotk {
acpi_handle handle; //the handle of the hotk device
char status; //status of the hotk, for LEDs, ...
struct model_data *methods; //methods available on the laptop
u8 brightness; //brighness level
u8 brightness; //brightness level
enum {
A1X=0, //A1340D, A1300F
A2X, //A2500H
D1X, //D1
L1X, //L1400B
L2X, //L2000D -> TODO check Q11 (Fn+F8)
// Calling this method simply hangs the
// computer, ISMI method hangs the laptop.
A1x = 0, //A1340D, A1300F
A2x, //A2500H
D1x, //D1
L2D, //L2000D
L3C, //L3800C
L3D, //L3400D
L3X, //L3C
L5X, //L5C TODO this model seems to have one more
// LED, add support
M2X, //M2400E
M3N, //M3700N, but also S1300N -> TODO WLED
S1X, //S1300A -> TODO special keys do not work ?
S2X, //S200 (J1 reported), Victor MP-XP7210
L3H, //L3H, but also L2000E
L5x, //L5800C
L8L, //L8400L
M1A, //M1300A
M2E, //M2400E
S1x, //S1300A, but also L1400B and M2400A (L84F)
S2x, //S200 (J1 reported), Victor MP-XP7210
//TODO A1370D does not seem to have an ATK device
// L8400 model doesn't have ATK
xxN, //M2400N, M3700N, S1300N (Centrino)
END_MODEL
} model; //Models currently supported
u16 event_count[128]; //count for each event TODO make this better
};
/* Here we go */
#define L3X_PREFIX "\\_SB.PCI0.PX40.ECD0."
#define S1X_PREFIX "\\_SB.PCI0.PX40."
#define L1X_PREFIX S1X_PREFIX
#define A1X_PREFIX "\\_SB.PCI0.ISA.EC0."
#define S2X_PREFIX A1X_PREFIX
#define M3N_PREFIX "\\_SB.PCI0.SBRG.EC0."
#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
#define S1x_PREFIX "\\_SB.PCI0.PX40."
#define S2x_PREFIX A1x_PREFIX
#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
static struct model_data model_conf[END_MODEL] = {
/*
* name| mled |mled read| wled |wled read| lcd sw |lcd read |
* br up|br down | br set | br read | br status|set disp | get disp
*
* br set and read shall be in hotk device !
* same for set disp
* Those pathnames are relative to the HOTK / ATKD device :
* - mt_mled
* - mt_wled
* - brightness_set
* - brightness_get
* - display_set
* - display_get
*
* TODO I have seen a SWBX and AIBX method on some models, like L1400B,
* it seems to be a kind of switch, but what for ?
*
*/
{"A1X", "MLED", "\\MAIL", NULL, NULL, A1X_PREFIX "_Q10", "\\BKLI",
A1X_PREFIX "_Q0E", A1X_PREFIX "_Q0F", NULL, NULL, NULL, NULL, NULL},
{"A2X", "MLED", NULL, "WLED", "\\SG66", "\\Q10", "\\BAOF",
"\\Q0E", "\\Q0F", "SPLV", "GPLV", "\\CMOD", "SDSP", "\\INFB"},
{
.name = "A1x",
.mt_mled = "MLED",
.mled_status = "\\MAIL",
.mt_lcd_switch = A1x_PREFIX "_Q10",
.lcd_status = "\\BKLI",
.brightness_up = A1x_PREFIX "_Q0E",
.brightness_down = A1x_PREFIX "_Q0F",
},
{"D1X", "MLED", NULL, NULL, NULL, "\\Q0D", "\\GP11",
"\\Q0C", "\\Q0B", NULL, NULL, "\\BLVL", "SDSP","\\INFB"},
{
.name = "A2x",
.mt_mled = "MLED",
.mt_wled = "WLED",
.wled_status = "\\SG66",
.mt_lcd_switch = "\\Q10",
.lcd_status = "\\BAOF",
.brightness_up = "\\Q0E",
.brightness_down = "\\Q0F",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.brightness_status = "\\CMOD",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"L1X", "MLED", NULL, "WLED", NULL, L1X_PREFIX "Q10", "\\PNOF",
L1X_PREFIX "Q0F", L1X_PREFIX "Q0E", "SPLV", "GPLV", "\\BRIT", NULL, NULL},
{
.name = "D1x",
.mt_mled = "MLED",
.mt_lcd_switch = "\\Q0D",
.lcd_status = "\\GP11",
.brightness_up = "\\Q0C",
.brightness_down = "\\Q0B",
.brightness_status = "\\BLVL",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"L2X", "MLED", "\\SGP6", "WLED", "\\RCP3", "\\Q10", "\\SGP0",
"\\Q0E", "\\Q0F", NULL, NULL, NULL, "SDSP", "\\INFB"},
{
.name = "L2D",
.mt_mled = "MLED",
.mled_status = "\\SGP6",
.mt_wled = "WLED",
.wled_status = "\\RCP3",
.mt_lcd_switch = "\\Q10",
.lcd_status = "\\SGP0",
.brightness_up = "\\Q0E",
.brightness_down = "\\Q0F",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"L3D", "MLED", "\\MALD", "WLED", NULL, "\\Q10", "\\BKLG",
"\\Q0E", "\\Q0F", "SPLV", "GPLV", "\\BLVL", "SDSP", "\\INFB"},
{
.name = "L3C",
.mt_mled = "MLED",
.mt_wled = "WLED",
.mt_lcd_switch = L3C_PREFIX "_Q10",
.lcd_status = "\\GL32",
.brightness_up = L3C_PREFIX "_Q0F",
.brightness_down = L3C_PREFIX "_Q0E",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.brightness_status = "\\BLVL",
.display_set = "SDSP",
.display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"
},
{"L3X", "MLED", NULL, "WLED", NULL, L3X_PREFIX "_Q10", "\\GL32",
L3X_PREFIX "_Q0F", L3X_PREFIX "_Q0E", "SPLV", "GPLV", "\\BLVL", "SDSP",
"\\_SB.PCI0.PCI1.VGAC.NMAP"},
{
.name = "L3D",
.mt_mled = "MLED",
.mled_status = "\\MALD",
.mt_wled = "WLED",
.mt_lcd_switch = "\\Q10",
.lcd_status = "\\BKLG",
.brightness_up = "\\Q0E",
.brightness_down = "\\Q0F",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.brightness_status = "\\BLVL",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"L5X", "MLED", NULL, "WLED", "WRED", "\\Q0D", "\\BAOF",
"\\Q0C","\\Q0B", "SPLV", "GPLV", NULL, "SDSP", "\\INFB"},
{
.name = "L3H",
.mt_mled = "MLED",
.mt_wled = "WLED",
.mt_lcd_switch = "EHK",
.lcd_status = "\\_SB.PCI0.PM.PBC",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{
.name = "L5x",
.mt_mled = "MLED",
// .mt_wled = "WLED",
// .wled_status = "\\WRED",
/* Present, but not controlled by ACPI */
.mt_tled = "TLED",
.mt_lcd_switch = "\\Q0D",
.lcd_status = "\\BAOF",
.brightness_up = "\\Q0C",
.brightness_down = "\\Q0B",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"M2X", "MLED", NULL, "WLED", NULL, "\\Q10", "\\GP06",
"\\Q0E","\\Q0F", "SPLV", "GPLV", NULL, "SDSP", "\\INFB"},
{
.name = "L8L"
/* No features, but at least support the hotkeys */
},
{"M3N", "MLED", NULL, "WLED", "\\PO33", M3N_PREFIX "_Q10", "\\BKLT",
M3N_PREFIX "_Q0F", M3N_PREFIX "_Q0E", "SPLV", "GPLV", "\\LBTN", "SDSP",
"\\ADVG"},
{
.name = "M1A",
.mt_mled = "MLED",
.mt_lcd_switch = M1A_PREFIX "Q10",
.lcd_status = "\\PNOF",
.brightness_up = M1A_PREFIX "Q0E",
.brightness_down = M1A_PREFIX "Q0F",
.brightness_status = "\\BRIT",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"S1X", "MLED", "\\EMLE", "WLED", NULL, S1X_PREFIX "Q10", "\\PNOF",
S1X_PREFIX "Q0F", S1X_PREFIX "Q0E", "SPLV", "GPLV", "\\BRIT", NULL, NULL},
{
.name = "M2E",
.mt_mled = "MLED",
.mt_wled = "WLED",
.mt_lcd_switch = "\\Q10",
.lcd_status = "\\GP06",
.brightness_up = "\\Q0E",
.brightness_down = "\\Q0F",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.display_set = "SDSP",
.display_get = "\\INFB"
},
{"S2X", "MLED", "\\MAIL", NULL, NULL, S2X_PREFIX "_Q10", "\\BKLI",
S2X_PREFIX "_Q0B", S2X_PREFIX "_Q0A", NULL, NULL, NULL, NULL, NULL}
{
.name = "S1x",
.mt_mled = "MLED",
.mled_status = "\\EMLE",
.mt_wled = "WLED",
.mt_lcd_switch = S1x_PREFIX "Q10" ,
.lcd_status = "\\PNOF",
.brightness_up = S1x_PREFIX "Q0F",
.brightness_down = S1x_PREFIX "Q0E",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.brightness_status = "\\BRIT",
},
{
.name = "S2x",
.mt_mled = "MLED",
.mled_status = "\\MAIL",
.mt_lcd_switch = S2x_PREFIX "_Q10",
.lcd_status = "\\BKLI",
.brightness_up = S2x_PREFIX "_Q0B",
.brightness_down = S2x_PREFIX "_Q0A",
},
{
.name = "xxN",
.mt_mled = "MLED",
// .mt_wled = "WLED",
// .wled_status = "\\PO33",
/* Present, but not controlled by ACPI */
.mt_lcd_switch = xxN_PREFIX "_Q10",
.lcd_status = "\\BKLT",
.brightness_up = xxN_PREFIX "_Q0F",
.brightness_down = xxN_PREFIX "_Q0E",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.brightness_status = "\\LBTN",
.display_set = "SDSP",
.display_get = "\\ADVG"
}
};
/* procdir we use */
......@@ -264,7 +417,7 @@ proc_read_info(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
int len = 0;
int sfun;
int temp;
struct asus_hotk *hotk = (struct asus_hotk *) data;
char buf[16]; //enough for all info
/*
......@@ -275,8 +428,23 @@ proc_read_info(char *page, char **start, off_t off, int count, int *eof,
len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
len += sprintf(page + len, "Model reference : %s\n",
hotk->methods->name);
if(read_acpi_int(hotk->handle, "SFUN", &sfun))
len += sprintf(page + len, "SFUN value : 0x%04x\n", sfun);
/*
* The SFUN method probably allows the original driver to get the list
* of features supported by a given model. For now, 0x0100 or 0x0800
* bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
* The significance of others is yet to be found.
*/
if (read_acpi_int(hotk->handle, "SFUN", &temp))
len += sprintf(page + len, "SFUN value : 0x%04x\n", temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
* battery low and 0x04 for battery critical, it's readings tend to be
* more accurate than those provided by _BST.
* Note: since not all the laptops provide this method, errors are
* silently ignored.
*/
if (read_acpi_int(hotk->handle, "ASYM", &temp))
len += sprintf(page + len, "ASYM value : 0x%04x\n", temp);
if (asus_info) {
snprintf(buf, 16, "%d", asus_info->length);
len += sprintf(page + len, "DSDT length : %s\n", buf);
......@@ -301,127 +469,178 @@ proc_read_info(char *page, char **start, off_t off, int count, int *eof,
/*
* proc file handlers
* /proc handlers
* We write our info in page, we begin at offset off and cannot write more
* than count bytes. We set eof to 1 if we handle those 2 values. We return the
* number of bytes written in page
*/
/* Generic LED functions */
static int
proc_read_mled(char *page, char **start, off_t off, int count, int *eof,
void *data)
read_led(struct asus_hotk *hotk, const char *ledname, int ledmask)
{
int len = 0;
struct asus_hotk *hotk = (struct asus_hotk *) data;
int led_status = 0;
/*
* We use the easy way, we don't care of off and count, so we don't set eof
* to 1
*/
if (hotk->methods->mled_status) {
if (read_acpi_int(NULL, hotk->methods->mled_status,
&led_status))
len = sprintf(page, "%d\n", led_status);
if (ledname) {
int led_status;
if (read_acpi_int(NULL, ledname, &led_status))
return led_status;
else
printk(KERN_WARNING "Asus ACPI: Error reading MLED "
printk(KERN_WARNING "Asus ACPI: Error reading LED "
"status\n");
} else {
len = sprintf(page, "%d\n", (hotk->status & MLED_ON) ? 1 : 0);
}
return len;
return (hotk->status & ledmask) ? 1 : 0;
}
/* FIXME: kill extraneous args so it can be called independently */
static int
proc_write_mled(struct file *file, const char *buffer,
unsigned long count, void *data)
write_led(const char *buffer, unsigned long count, struct asus_hotk *hotk,
char *ledname, int ledmask, int invert)
{
int value;
int led_out = 0;
struct asus_hotk *hotk = (struct asus_hotk *) data;
/* scan expression. Multiple expressions may be delimited with ; */
if (sscanf(buffer, "%i", &value) == 1)
led_out = ~value & 1;
led_out = value ? 1 : 0;
hotk->status =
(value) ? (hotk->status | MLED_ON) : (hotk->status & ~MLED_ON);
/* We don't have to check mt_mled exists if we are here :) */
if (!write_acpi_int(hotk->handle, hotk->methods->mt_mled, led_out,
NULL))
printk(KERN_WARNING "Asus ACPI: MLED write failed\n");
(led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
if (invert) /* invert target value */
led_out = !led_out & 0x1;
if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n", ledname);
return count;
}
/*
* We write our info in page, we begin at offset off and cannot write more
* than count bytes. We set eof to 1 if we handle those 2 values. We return the
* number of bytes written in page
* Proc handlers for MLED
*/
static int
proc_read_wled(char *page, char **start, off_t off, int count, int *eof,
proc_read_mled(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
int len = 0;
struct asus_hotk *hotk = (struct asus_hotk *) data;
int led_status;
return sprintf(page, "%d\n", read_led(hotk, hotk->methods->mled_status, MLED_ON));
}
if (hotk->methods->wled_status) {
if (read_acpi_int(NULL, hotk->methods->wled_status,
&led_status))
len = sprintf(page, "%d\n", led_status);
else
printk(KERN_WARNING "Asus ACPI: Error reading WLED "
"status\n");
} else {
len = sprintf(page, "%d\n", (hotk->status & WLED_ON) ? 1 : 0);
}
return len;
static int
proc_write_mled(struct file *file, const char *buffer,
unsigned long count, void *data)
{
struct asus_hotk *hotk = (struct asus_hotk *) data;
return write_led(buffer, count, hotk, hotk->methods->mt_mled, MLED_ON, 1);
}
/*
* Proc handlers for WLED
*/
static int
proc_read_wled(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
struct asus_hotk *hotk = (struct asus_hotk *) data;
return sprintf(page, "%d\n", read_led(hotk, hotk->methods->wled_status, WLED_ON));
}
static int
proc_write_wled(struct file *file, const char *buffer,
unsigned long count, void *data)
{
int value;
int led_out = 0;
struct asus_hotk *hotk = (struct asus_hotk *) data;
return write_led(buffer, count, hotk, hotk->methods->mt_wled, WLED_ON, 0);
}
/* scan expression. Multiple expressions may be delimited with ; */
if (sscanf(buffer, "%i", &value) == 1)
led_out = value & 1;
hotk->status =
(value) ? (hotk->status | WLED_ON) : (hotk->status & ~WLED_ON);
/* We don't have to check if mt_wled exists if we are here :) */
if (!write_acpi_int(hotk->handle, hotk->methods->mt_wled, led_out,
NULL))
printk(KERN_WARNING "Asus ACPI: WLED write failed\n");
/*
* Proc handlers for TLED
*/
static int
proc_read_tled(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
struct asus_hotk *hotk = (struct asus_hotk *) data;
return sprintf(page, "%d\n", read_led(hotk, hotk->methods->tled_status, TLED_ON));
}
return count;
static int
proc_write_tled(struct file *file, const char *buffer,
unsigned long count, void *data)
{
struct asus_hotk *hotk = (struct asus_hotk *) data;
return write_led(buffer, count, hotk, hotk->methods->mt_tled, TLED_ON, 0);
}
static int get_lcd_state(struct asus_hotk *hotk)
{
int lcd = 0;
/* We don't have to check anything, if we are here */
if (hotk->model != L3H) {
/* We don't have to check anything if we are here */
if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
printk(KERN_WARNING "Asus ACPI: Error reading LCD status\n");
if (hotk->model == L2X)
if (hotk->model == L2D)
lcd = ~lcd;
} else { /* L3H and the like have to be handled differently */
acpi_status status = 0;
struct acpi_object_list input;
union acpi_object mt_params[2];
struct acpi_buffer output;
union acpi_object out_obj;
input.count = 2;
input.pointer = mt_params;
/* Note: the following values are partly guessed up, but
otherwise they seem to work */
mt_params[0].type = ACPI_TYPE_INTEGER;
mt_params[0].integer.value = 0x02;
mt_params[1].type = ACPI_TYPE_INTEGER;
mt_params[1].integer.value = 0x02;
output.length = sizeof(out_obj);
output.pointer = &out_obj;
status = acpi_evaluate_object(NULL, hotk->methods->lcd_status, &input, &output);
if (status != AE_OK)
return -1;
if (out_obj.type == ACPI_TYPE_INTEGER)
/* That's what the AML code does */
lcd = out_obj.integer.value >> 8;
}
return (lcd & 1);
}
static int set_lcd_state(struct asus_hotk *hotk, int value)
{
int lcd = 0;
acpi_status status = 0;
lcd = value ? 1 : 0;
if (lcd != get_lcd_state(hotk)) {
/* switch */
if (hotk->model != L3H) {
status =
acpi_evaluate_object(NULL, hotk->methods->mt_lcd_switch,
NULL, NULL);
} else { /* L3H and the like have to be handled differently */
if (!write_acpi_int(hotk->handle, hotk->methods->mt_lcd_switch, 0x07, NULL))
status = AE_ERROR;
/* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
the exact behaviour is simulated here */
}
if (ACPI_FAILURE(status))
printk(KERN_WARNING "Asus ACPI: Error switching LCD\n");
}
return 0;
}
static int
proc_read_lcd(char *page, char **start, off_t off, int count, int *eof,
......@@ -436,26 +655,10 @@ proc_write_lcd(struct file *file, const char *buffer,
unsigned long count, void *data)
{
int value;
int lcd = 0;
acpi_status status = 0;
int lcd_status = 0;
struct asus_hotk *hotk = (struct asus_hotk *) data;
/* scan expression. Multiple expressions may be delimited with ; */
if (sscanf(buffer, "%i", &value) == 1)
lcd = value & 1;
lcd_status = get_lcd_state(hotk);
if (lcd_status != lcd) {
/* switch */
status =
acpi_evaluate_object(NULL, hotk->methods->mt_lcd_switch,
NULL, NULL);
if (ACPI_FAILURE(status))
printk(KERN_WARNING "Asus ACPI: Error switching LCD\n");
}
set_lcd_state(hotk, value);
return count;
}
......@@ -521,7 +724,6 @@ proc_write_brn(struct file *file, const char *buffer,
int value;
struct asus_hotk *hotk = (struct asus_hotk *) data;
/* scan expression. Multiple expressions may be delimited with ; */
if (sscanf(buffer, "%d", &value) == 1) {
value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
/* 0 <= value <= 15 */
......@@ -546,7 +748,6 @@ static void set_display(int value, struct asus_hotk *hotk)
* Now, *this* one could be more user-friendly, but so far, no-one has
* complained. The significance of bits is the same as in proc_write_disp()
*/
static int
proc_read_disp(char *page, char **start, off_t off, int count, int *eof,
void *data)
......@@ -560,12 +761,11 @@ proc_read_disp(char *page, char **start, off_t off, int count, int *eof,
}
/*
* Experimental support for display switching. As of now: 0x01 should activate
* the LCD output, 0x02 should do for CRT, and 0x04 for TV-Out. Any combination
* Experimental support for display switching. As of now: 1 should activate
* the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
* (bitwise) of these will suffice. I never actually tested 3 displays hooked up
* simultaneously, so be warned.
* simultaneously, so be warned. See the acpi4asus README for more info.
*/
static int
proc_write_disp(struct file *file, const char *buffer,
unsigned long count, void *data)
......@@ -573,7 +773,6 @@ proc_write_disp(struct file *file, const char *buffer,
int value;
struct asus_hotk *hotk = (struct asus_hotk *) data;
/* scan expression. Multiple expressions may be delimited with ; */
if (sscanf(buffer, "%d", &value) == 1)
set_display(value, hotk);
else {
......@@ -583,6 +782,31 @@ proc_write_disp(struct file *file, const char *buffer,
return count;
}
typedef int (proc_readfunc)(char *page, char **start, off_t off, int count,
int *eof, void *data);
typedef int (proc_writefunc)(struct file *file, const char *buffer,
unsigned long count, void *data);
static int
__init asus_proc_add(char *name, proc_writefunc *writefunc,
proc_readfunc *readfunc, mode_t mode,
struct acpi_device *device)
{
struct proc_dir_entry *proc = create_proc_entry(name, mode, acpi_device_dir(device));
if(!proc) {
printk(KERN_WARNING " Unable to create %s fs entry\n", name);
return -1;
}
proc->write_proc = writefunc;
proc->read_proc = readfunc;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;
return 0;
}
static int __init asus_hotk_add_fs(struct acpi_device *device)
{
struct proc_dir_entry *proc;
......@@ -605,46 +829,28 @@ static int __init asus_hotk_add_fs(struct acpi_device *device)
if (!acpi_device_dir(device))
return(-ENODEV);
proc = create_proc_entry(PROC_INFOS, mode, acpi_device_dir(device));
proc = create_proc_entry(PROC_INFO, mode, acpi_device_dir(device));
if (proc) {
proc->read_proc = proc_read_info;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;;
proc->gid = asus_gid;
} else {
printk(KERN_WARNING " Unable to create " PROC_INFOS
printk(KERN_WARNING " Unable to create " PROC_INFO
" fs entry\n");
}
if (hotk->methods->mt_wled) {
proc = create_proc_entry(PROC_WLED, mode, acpi_device_dir(device));
if (proc) {
proc->write_proc = proc_write_wled;
proc->read_proc = proc_read_wled;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;;
} else {
printk(KERN_WARNING " Unable to create " PROC_WLED
" fs entry\n");
}
asus_proc_add(PROC_WLED, &proc_write_wled, &proc_read_wled, mode, device);
}
if (hotk->methods->mt_mled) {
proc = create_proc_entry(PROC_MLED, mode, acpi_device_dir(device));
if (proc) {
proc->write_proc = proc_write_mled;
proc->read_proc = proc_read_mled;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;;
} else {
printk(KERN_WARNING " Unable to create " PROC_MLED
" fs entry\n");
asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled, mode, device);
}
if (hotk->methods->mt_tled) {
asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled, mode, device);
}
/*
......@@ -652,49 +858,17 @@ static int __init asus_hotk_add_fs(struct acpi_device *device)
* from keyboard
*/
if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
proc = create_proc_entry(PROC_LCD, mode, acpi_device_dir(device));
if (proc) {
proc->write_proc = proc_write_lcd;
proc->read_proc = proc_read_lcd;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;;
} else {
printk(KERN_WARNING " Unable to create " PROC_LCD
" fs entry\n");
}
asus_proc_add(PROC_LCD, &proc_write_lcd, &proc_read_lcd, mode, device);
}
if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
(hotk->methods->brightness_get && hotk->methods->brightness_get)) {
proc = create_proc_entry(PROC_BRN, mode, acpi_device_dir(device));
if (proc) {
proc->write_proc = proc_write_brn;
proc->read_proc = proc_read_brn;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;;
} else {
printk(KERN_WARNING " Unable to create " PROC_BRN
" fs entry\n");
}
asus_proc_add(PROC_BRN, &proc_write_brn, &proc_read_brn, mode, device);
}
if (hotk->methods->display_set) {
proc = create_proc_entry(PROC_DISP, mode, acpi_device_dir(device));
if (proc) {
proc->write_proc = proc_write_disp;
proc->read_proc = proc_read_disp;
proc->data = acpi_driver_data(device);
proc->owner = THIS_MODULE;
proc->uid = asus_uid;
proc->gid = asus_gid;;
} else {
printk(KERN_WARNING " Unable to create " PROC_DISP
" fs entry\n");
}
asus_proc_add(PROC_DISP, &proc_write_disp, &proc_read_disp, mode, device);
}
return 0;
......@@ -761,11 +935,6 @@ static int __init asus_hotk_get_info(struct asus_hotk *hotk)
else if (bsts_result)
printk(KERN_NOTICE " BSTS called, 0x%02x returned\n", bsts_result);
/*
* Here, we also use asus_info to make decision. For example, on INIT
* method, S1X and L1X models both reports to be L84F, but they don't
* have the same methods (L1X has WLED, S1X don't)
*/
model = (union acpi_object *) buffer.pointer;
if (model->type == ACPI_TYPE_STRING) {
printk(KERN_NOTICE " %s model detected, ", model->string.pointer);
......@@ -774,52 +943,63 @@ static int __init asus_hotk_get_info(struct asus_hotk *hotk)
hotk->model = END_MODEL;
if (strncmp(model->string.pointer, "L3D", 3) == 0)
hotk->model = L3D;
/*
* L2B has same settings that L3X, except for GL32, but as
* there is no node to get the LCD status, and as GL32 is never
* used anywhere else, I assume it's safe, even if lcd get is
* broken for this model (TODO fix it ?)
*/
else if (strncmp(model->string.pointer, "L3H", 3) == 0 ||
strncmp(model->string.pointer, "L2E", 3) == 0)
hotk->model = L3H;
else if (strncmp(model->string.pointer, "L3", 2) == 0 ||
strncmp(model->string.pointer, "L2B", 3) == 0)
hotk->model = L3X;
hotk->model = L3C;
else if (strncmp(model->string.pointer, "L8L", 3) == 0)
hotk->model = L8L;
else if (strncmp(model->string.pointer, "M2N", 3) == 0 ||
strncmp(model->string.pointer, "M3N", 3) == 0 ||
strncmp(model->string.pointer, "S1N", 3) == 0 ||
strncmp(model->string.pointer, "S5N", 3) == 0)
hotk->model = xxN;
else if (strncmp(model->string.pointer, "M1", 2) == 0)
hotk->model = M1A;
else if (strncmp(model->string.pointer, "M2", 2) == 0)
hotk->model = M2X;
else if (strncmp(model->string.pointer, "M3N", 3) == 0 ||
strncmp(model->string.pointer, "S1N", 3) == 0)
hotk->model = M3N; /* S1300N is similar enough */
hotk->model = M2E;
else if (strncmp(model->string.pointer, "L2", 2) == 0)
hotk->model = L2X;
else if (strncmp(model->string.pointer, "L8", 2) == 0) {
/* S1300A reports L84F, but L1400B too */
if (asus_info) {
if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
hotk->model = L1X;
} else
hotk->model = S1X;
}
hotk->model = L2D;
else if (strncmp(model->string.pointer, "L8", 2) == 0)
hotk->model = S1x;
else if (strncmp(model->string.pointer, "D1", 2) == 0)
hotk->model = D1X;
hotk->model = D1x;
else if (strncmp(model->string.pointer, "A1", 2) == 0)
hotk->model = A1X;
hotk->model = A1x;
else if (strncmp(model->string.pointer, "A2", 2) == 0)
hotk->model = A2X;
hotk->model = A2x;
else if (strncmp(model->string.pointer, "J1", 2) == 0)
hotk->model = S2X;
hotk->model = S2x;
else if (strncmp(model->string.pointer, "L5", 2) == 0)
hotk->model = L5X;
hotk->model = L5x;
if (hotk->model == END_MODEL) {
/* By default use the same values, as I don't know others */
printk("unsupported, trying default values, supply the "
"developers with your DSDT\n");
hotk->model = L2X;
hotk->model = M2E;
} else {
printk("supported\n");
}
hotk->methods = &model_conf[hotk->model];
/* Sort of per-model blacklist */
if (strncmp(model->string.pointer, "L2B", 3) == 0)
hotk->methods->lcd_status = NULL;
/* L2B is similar enough to L3C to use its settings, with this only
exception */
else if (strncmp(model->string.pointer, "S5N", 3) == 0)
hotk->methods->mt_mled = NULL;
/* S5N has no MLED */
else if (asus_info) {
if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
hotk->methods->mled_status = NULL;
/* S1300A reports L84F, but L1400B too, account for that */
}
acpi_os_free(model);
return AE_OK;
......@@ -917,8 +1097,6 @@ static int __init asus_hotk_add(struct acpi_device *device)
}
static int asus_hotk_remove(struct acpi_device *device, int type)
{
acpi_status status = 0;
......@@ -940,15 +1118,13 @@ static int asus_hotk_remove(struct acpi_device *device, int type)
}
static int __init asus_acpi_init(void)
{
int result;
asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
if (!asus_proc_dir) {
printk(KERN_ERR "Asus ACPI: Unable to create /proc entry");
printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
return(-ENODEV);
}
asus_proc_dir->owner = THIS_MODULE;
......@@ -963,7 +1139,6 @@ static int __init asus_acpi_init(void)
}
static void __exit asus_acpi_exit(void)
{
acpi_bus_unregister_driver(&asus_hotk_driver);
......
......@@ -30,8 +30,9 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/acmacros.h>
extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler);
extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler, unsigned int max_entries);
void __init
acpi_table_print_srat_entry (
......@@ -46,9 +47,9 @@ acpi_table_print_srat_entry (
{
struct acpi_table_processor_affinity *p =
(struct acpi_table_processor_affinity*) header;
printk(KERN_INFO PREFIX "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
ACPI_DEBUG_PRINT((ACPI_DB_INFO "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
p->apic_id, p->lsapic_eid, p->proximity_domain,
p->flags.enabled?"enabled":"disabled");
p->flags.enabled?"enabled":"disabled"));
}
break;
......@@ -56,11 +57,11 @@ acpi_table_print_srat_entry (
{
struct acpi_table_memory_affinity *p =
(struct acpi_table_memory_affinity*) header;
printk(KERN_INFO PREFIX "SRAT Memory (0x%08x%08x length 0x%08x%08x type 0x%x) in proximity domain %d %s%s\n",
ACPI_DEBUG_PRINT((ACPI_DB_INFO "SRAT Memory (0x%08x%08x length 0x%08x%08x type 0x%x) in proximity domain %d %s%s\n",
p->base_addr_hi, p->base_addr_lo, p->length_hi, p->length_lo,
p->memory_type, p->proximity_domain,
p->flags.enabled ? "enabled" : "disabled",
p->flags.hot_pluggable ? " hot-pluggable" : "");
p->flags.hot_pluggable ? " hot-pluggable" : ""));
}
break;
......@@ -97,7 +98,7 @@ acpi_parse_slit (unsigned long phys_addr, unsigned long size)
static int __init
acpi_parse_processor_affinity (acpi_table_entry_header *header)
{
struct acpi_table_processor_affinity *processor_affinity = NULL;
struct acpi_table_processor_affinity *processor_affinity;
processor_affinity = (struct acpi_table_processor_affinity*) header;
if (!processor_affinity)
......@@ -115,7 +116,7 @@ acpi_parse_processor_affinity (acpi_table_entry_header *header)
static int __init
acpi_parse_memory_affinity (acpi_table_entry_header *header)
{
struct acpi_table_memory_affinity *memory_affinity = NULL;
struct acpi_table_memory_affinity *memory_affinity;
memory_affinity = (struct acpi_table_memory_affinity*) header;
if (!memory_affinity)
......@@ -133,7 +134,7 @@ acpi_parse_memory_affinity (acpi_table_entry_header *header)
static int __init
acpi_parse_srat (unsigned long phys_addr, unsigned long size)
{
struct acpi_table_srat *srat = NULL;
struct acpi_table_srat *srat;
if (!phys_addr || !size)
return -EINVAL;
......@@ -149,10 +150,11 @@ acpi_parse_srat (unsigned long phys_addr, unsigned long size)
int __init
acpi_table_parse_srat (
enum acpi_srat_entry_id id,
acpi_madt_entry_handler handler)
acpi_madt_entry_handler handler,
unsigned int max_entries)
{
return acpi_table_parse_madt_family(ACPI_SRAT, sizeof(struct acpi_table_srat),
id, handler);
id, handler, max_entries);
}
......@@ -166,9 +168,11 @@ acpi_numa_init()
if (result > 0) {
result = acpi_table_parse_srat(ACPI_SRAT_PROCESSOR_AFFINITY,
acpi_parse_processor_affinity);
acpi_parse_processor_affinity,
NR_CPUS);
result = acpi_table_parse_srat(ACPI_SRAT_MEMORY_AFFINITY,
acpi_parse_memory_affinity);
acpi_parse_memory_affinity,
NR_MEMBLKS);
} else {
/* FIXME */
printk("Warning: acpi_table_parse(ACPI_SRAT) returned %d!\n",result);
......
......@@ -315,7 +315,6 @@ acpi_pci_irq_enable (
{
int irq = 0;
u8 pin = 0;
static u16 irq_mask = 0;
ACPI_FUNCTION_TRACE("acpi_pci_irq_enable");
......@@ -372,11 +371,14 @@ acpi_pci_irq_enable (
* Make sure all (legacy) PCI IRQs are set as level-triggered.
*/
#ifdef CONFIG_X86
{
static u16 irq_mask;
if ((dev->irq < 16) && !((1 << dev->irq) & irq_mask)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Setting IRQ %d as level-triggered\n", dev->irq));
irq_mask |= (1 << dev->irq);
eisa_set_level_irq(dev->irq);
}
}
#endif
#ifdef CONFIG_IOSAPIC
if (acpi_irq_model == ACPI_IRQ_MODEL_IOSAPIC)
......
......@@ -3,6 +3,7 @@
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2004 Dominik Brodowski <linux@brodo.de>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
......@@ -22,7 +23,7 @@
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* TBD:
* 1. Make # power/performance states dynamic.
* 1. Make # power states dynamic.
* 2. Support duty_cycle values that span bit 4.
* 3. Optimize by having scheduler determine business instead of
* having us try to calculate it here.
......@@ -55,9 +56,9 @@
#define ACPI_PROCESSOR_DEVICE_NAME "Processor"
#define ACPI_PROCESSOR_FILE_INFO "info"
#define ACPI_PROCESSOR_FILE_POWER "power"
#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
#define ACPI_PROCESSOR_FILE_THROTTLING "throttling"
#define ACPI_PROCESSOR_FILE_LIMIT "limit"
#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
......@@ -746,7 +747,62 @@ acpi_processor_get_power_info (
/* --------------------------------------------------------------------------
Performance Management
-------------------------------------------------------------------------- */
int
#ifdef CONFIG_CPU_FREQ
static DECLARE_MUTEX(performance_sem);
/*
* _PPC support is implemented as a CPUfreq policy notifier:
* This means each time a CPUfreq driver registered also with
* the ACPI core is asked to change the speed policy, the maximum
* value is adjusted so that it is within the platform limit.
*
* Also, when a new platform limit value is detected, the CPUfreq
* policy is adjusted accordingly.
*/
static int acpi_processor_ppc_is_init = 0;
static int acpi_processor_ppc_notifier(struct notifier_block *nb,
unsigned long event,
void *data)
{
struct cpufreq_policy *policy = data;
struct acpi_processor *pr;
unsigned int ppc = 0;
down(&performance_sem);
if (event != CPUFREQ_INCOMPATIBLE)
goto out;
pr = processors[policy->cpu];
if (!pr || !pr->performance)
goto out;
ppc = (unsigned int) pr->performance_platform_limit;
if (!ppc)
goto out;
if (ppc > pr->performance->state_count)
goto out;
cpufreq_verify_within_limits(policy, 0,
pr->performance->states[ppc].core_frequency * 1000);
out:
up(&performance_sem);
return 0;
}
static struct notifier_block acpi_ppc_notifier_block = {
.notifier_call = acpi_processor_ppc_notifier,
};
static int
acpi_processor_get_platform_limit (
struct acpi_processor* pr)
{
......@@ -770,35 +826,491 @@ acpi_processor_get_platform_limit (
pr->performance_platform_limit = (int) ppc;
acpi_processor_get_limit_info(pr);
return_VALUE(0);
}
static int acpi_processor_ppc_has_changed(
struct acpi_processor *pr)
{
int ret = acpi_processor_get_platform_limit(pr);
if (ret < 0)
return (ret);
else
return cpufreq_update_policy(pr->id);
}
static void acpi_processor_ppc_init(void) {
if (!cpufreq_register_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
acpi_processor_ppc_is_init = 1;
else
printk(KERN_DEBUG "Warning: Processor Platform Limit not supported.\n");
}
static void acpi_processor_ppc_exit(void) {
if (acpi_processor_ppc_is_init)
cpufreq_unregister_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER);
acpi_processor_ppc_is_init = 0;
}
/*
* when registering a cpufreq driver with this ACPI processor driver, the
* _PCT and _PSS structures are read out and written into struct
* acpi_processor_performance.
*/
static int acpi_processor_set_pdc (struct acpi_processor *pr)
{
acpi_status status = AE_OK;
u32 arg0_buf[3];
union acpi_object arg0 = {ACPI_TYPE_BUFFER};
struct acpi_object_list no_object = {1, &arg0};
struct acpi_object_list *pdc;
ACPI_FUNCTION_TRACE("acpi_processor_set_pdc");
arg0.buffer.length = 12;
arg0.buffer.pointer = (u8 *) arg0_buf;
arg0_buf[0] = ACPI_PDC_REVISION_ID;
arg0_buf[1] = 0;
arg0_buf[2] = 0;
pdc = (pr->performance->pdc) ? pr->performance->pdc : &no_object;
status = acpi_evaluate_object(pr->handle, "_PDC", pdc, NULL);
if ((ACPI_FAILURE(status)) && (pr->performance->pdc))
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating _PDC, using legacy perf. control...\n"));
return_VALUE(status);
}
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};
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;
}
memcpy(&pr->performance->control_register, obj.buffer.pointer, sizeof(struct acpi_pct_register));
/*
* 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;
}
memcpy(&pr->performance->status_register, obj.buffer.pointer, sizeof(struct acpi_pct_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));
pr->performance->state_count = pss->package.count;
pr->performance->states = kmalloc(sizeof(struct acpi_processor_px) * pss->package.count, GFP_KERNEL);
if (!pr->performance->states) {
result = -ENOMEM;
goto end;
}
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;
kfree(pr->performance->states);
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));
if (!px->core_frequency) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "core_frequency is 0\n"));
result = -EFAULT;
kfree(pr->performance->states);
goto end;
}
}
end:
acpi_os_free(buffer.pointer);
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 || !pr->performance || !pr->handle)
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);
}
acpi_processor_set_pdc(pr);
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);
}
EXPORT_SYMBOL(acpi_processor_get_platform_limit);
#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
/* /proc/acpi/processor/../performance interface (DEPRECATED) */
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_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->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 __user *buffer,
size_t count,
loff_t *data)
{
int result = 0;
struct seq_file *m = (struct seq_file *) file->private_data;
struct acpi_processor *pr = (struct acpi_processor *) m->private;
struct acpi_processor_performance *perf;
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);
perf = pr->performance;
if (!perf)
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);
if (new_state >= perf->state_count)
return_VALUE(-EINVAL);
cpufreq_get_policy(&policy, pr->id);
policy.cpu = pr->id;
policy.min = perf->states[new_state].core_frequency * 1000;
policy.max = perf->states[new_state].core_frequency * 1000;
result = cpufreq_set_policy(&policy);
if (result)
return_VALUE(result);
return_VALUE(count);
}
static void
acpi_cpufreq_add_file (
struct acpi_processor *pr)
{
struct proc_dir_entry *entry = NULL;
struct acpi_device *device = NULL;
ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile");
if (acpi_bus_get_device(pr->handle, &device))
return_VOID;
/* 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->proc_fops->write = acpi_processor_write_performance;
entry->data = acpi_driver_data(device);
}
return_VOID;
}
static void
acpi_cpufreq_remove_file (
struct acpi_processor *pr)
{
struct acpi_device *device = NULL;
ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile");
if (acpi_bus_get_device(pr->handle, &device))
return_VOID;
/* remove file 'performance' */
remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
acpi_device_dir(device));
return_VOID;
}
#else
static void acpi_cpufreq_add_file (struct acpi_processor *pr) { return; }
static void acpi_cpufreq_remove_file (struct acpi_processor *pr) { return; }
#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
int
acpi_processor_register_performance (
struct acpi_processor_performance * performance,
struct acpi_processor ** pr,
unsigned int cpu)
{
struct acpi_processor *pr;
ACPI_FUNCTION_TRACE("acpi_processor_register_performance");
*pr = processors[cpu];
if (!*pr)
if (!acpi_processor_ppc_is_init)
return_VALUE(-EINVAL);
down(&performance_sem);
pr = processors[cpu];
if (!pr) {
up(&performance_sem);
return_VALUE(-ENODEV);
}
if ((*pr)->performance)
if (pr->performance) {
up(&performance_sem);
return_VALUE(-EBUSY);
}
(*pr)->performance = performance;
performance->pr = *pr;
return 0;
pr->performance = performance;
if (acpi_processor_get_performance_info(pr)) {
pr->performance = NULL;
up(&performance_sem);
return_VALUE(-EIO);
}
acpi_cpufreq_add_file(pr);
up(&performance_sem);
return_VALUE(0);
}
EXPORT_SYMBOL(acpi_processor_register_performance);
/* for the rest of it, check cpufreq/acpi.c */
void
acpi_processor_unregister_performance (
struct acpi_processor_performance * performance,
unsigned int cpu)
{
struct acpi_processor *pr;
ACPI_FUNCTION_TRACE("acpi_processor_unregister_performance");
if (!acpi_processor_ppc_is_init)
return_VOID;
down(&performance_sem);
pr = processors[cpu];
if (!pr) {
up(&performance_sem);
return_VOID;
}
kfree(pr->performance->states);
pr->performance = NULL;
acpi_cpufreq_remove_file(pr);
up(&performance_sem);
return_VOID;
}
EXPORT_SYMBOL(acpi_processor_unregister_performance);
/* for the rest of it, check arch/i386/kernel/cpu/cpufreq/acpi.c */
#else /* !CONFIG_CPU_FREQ */
static void acpi_processor_ppc_init(void) { return; }
static void acpi_processor_ppc_exit(void) { return; }
static int acpi_processor_ppc_has_changed(struct acpi_processor *pr) {
static unsigned int printout = 1;
if (printout) {
printk(KERN_WARNING "Warning: Processor Platform Limit event detected, but not handled.\n");
printk(KERN_WARNING "Consider compiling CPUfreq support into your kernel.\n");
printout = 0;
}
return 0;
}
#endif /* CONFIG_CPU_FREQ */
/* --------------------------------------------------------------------------
Throttling Control
......@@ -1043,27 +1555,6 @@ acpi_processor_apply_limit (
if (!pr->flags.limit)
return_VALUE(-ENODEV);
#ifdef CONFIG_CPU_FREQ
if (pr->flags.performance) {
px = pr->performance_platform_limit;
if (pr->limit.user.px > px)
px = pr->limit.user.px;
if (pr->limit.thermal.px > px)
px = pr->limit.thermal.px;
{
struct cpufreq_policy policy;
policy.cpu = pr->id;
cpufreq_get_policy(&policy, pr->id);
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)
tx = pr->limit.user.tx;
......@@ -1091,6 +1582,113 @@ acpi_processor_apply_limit (
}
#ifdef CONFIG_CPU_FREQ
/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
* offers (in most cases) voltage scaling in addition to frequency scaling, and
* thus a cubic (instead of linear) reduction of energy. Also, we allow for
* _any_ cpufreq driver and not only the acpi-cpufreq driver.
*/
static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];
static unsigned int acpi_thermal_cpufreq_is_init = 0;
static int cpu_has_cpufreq(unsigned int cpu)
{
struct cpufreq_policy policy;
if (!acpi_thermal_cpufreq_is_init)
return -ENODEV;
if (!cpufreq_get_policy(&policy, cpu))
return -ENODEV;
return 0;
}
static int acpi_thermal_cpufreq_increase(unsigned int cpu)
{
if (!cpu_has_cpufreq)
return -ENODEV;
if (cpufreq_thermal_reduction_pctg[cpu] < 60) {
cpufreq_thermal_reduction_pctg[cpu] += 20;
cpufreq_update_policy(cpu);
return 0;
}
return -ERANGE;
}
static int acpi_thermal_cpufreq_decrease(unsigned int cpu)
{
if (!cpu_has_cpufreq)
return -ENODEV;
if (cpufreq_thermal_reduction_pctg[cpu] >= 20) {
cpufreq_thermal_reduction_pctg[cpu] -= 20;
cpufreq_update_policy(cpu);
return 0;
}
return -ERANGE;
}
static int acpi_thermal_cpufreq_notifier(
struct notifier_block *nb,
unsigned long event,
void *data)
{
struct cpufreq_policy *policy = data;
unsigned long max_freq = 0;
if (event != CPUFREQ_ADJUST)
goto out;
max_freq = (policy->cpuinfo.max_freq * (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100;
cpufreq_verify_within_limits(policy, 0, max_freq);
out:
return 0;
}
static struct notifier_block acpi_thermal_cpufreq_notifier_block = {
.notifier_call = acpi_thermal_cpufreq_notifier,
};
static void acpi_thermal_cpufreq_init(void) {
int i;
for (i=0; i<NR_CPUS; i++)
cpufreq_thermal_reduction_pctg[i] = 0;
i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER);
if (!i)
acpi_thermal_cpufreq_is_init = 1;
}
static void acpi_thermal_cpufreq_exit(void) {
if (acpi_thermal_cpufreq_is_init)
cpufreq_unregister_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER);
acpi_thermal_cpufreq_is_init = 0;
}
#else /* ! CONFIG_CPU_FREQ */
static void acpi_thermal_cpufreq_init(void) { return; }
static void acpi_thermal_cpufreq_exit(void) { return; }
static int acpi_thermal_cpufreq_increase(unsigned int cpu) { return -ENODEV; }
static int acpi_thermal_cpufreq_decrease(unsigned int cpu) { return -ENODEV; }
#endif
int
acpi_processor_set_thermal_limit (
acpi_handle handle,
......@@ -1099,7 +1697,6 @@ acpi_processor_set_thermal_limit (
int result = 0;
struct acpi_processor *pr = NULL;
struct acpi_device *device = NULL;
int px = 0;
int tx = 0;
ACPI_FUNCTION_TRACE("acpi_processor_set_thermal_limit");
......@@ -1116,12 +1713,7 @@ acpi_processor_set_thermal_limit (
if (!pr)
return_VALUE(-ENODEV);
if (!pr->flags.limit)
return_VALUE(-ENODEV);
/* Thermal limits are always relative to the current Px/Tx state. */
if (pr->flags.performance)
pr->limit.thermal.px = pr->performance->state;
if (pr->flags.throttling)
pr->limit.thermal.tx = pr->throttling.state;
......@@ -1130,26 +1722,27 @@ acpi_processor_set_thermal_limit (
* performance state.
*/
px = pr->limit.thermal.px;
tx = pr->limit.thermal.tx;
switch (type) {
case ACPI_PROCESSOR_LIMIT_NONE:
px = 0;
do {
result = acpi_thermal_cpufreq_decrease(pr->id);
} while (!result);
tx = 0;
break;
case ACPI_PROCESSOR_LIMIT_INCREMENT:
if (pr->flags.performance) {
if (px == (pr->performance->state_count - 1))
/* if going up: P-states first, T-states later */
result = acpi_thermal_cpufreq_increase(pr->id);
if (!result)
goto end;
else if (result == -ERANGE)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At maximum performance state\n"));
else {
px++;
goto end;
}
}
if (pr->flags.throttling) {
if (tx == (pr->throttling.state_count - 1))
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
......@@ -1160,27 +1753,29 @@ acpi_processor_set_thermal_limit (
break;
case ACPI_PROCESSOR_LIMIT_DECREMENT:
if (pr->flags.performance) {
if (px == pr->performance_platform_limit)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At minimum performance state\n"));
else {
px--;
goto end;
}
}
/* if going down: T-states first, P-states later */
if (pr->flags.throttling) {
if (tx == 0)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At minimum throttling state\n"));
else
else {
tx--;
goto end;
}
}
result = acpi_thermal_cpufreq_decrease(pr->id);
if (result == -ERANGE)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"At minimum performance state\n"));
break;
}
end:
pr->limit.thermal.px = px;
if (pr->flags.throttling) {
pr->limit.thermal.px = 0;
pr->limit.thermal.tx = tx;
result = acpi_processor_apply_limit(pr);
......@@ -1191,6 +1786,8 @@ acpi_processor_set_thermal_limit (
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n",
pr->limit.thermal.px,
pr->limit.thermal.tx));
} else
result = 0;
return_VALUE(result);
}
......@@ -1205,7 +1802,7 @@ acpi_processor_get_limit_info (
if (!pr)
return_VALUE(-EINVAL);
if (pr->flags.performance || pr->flags.throttling)
if (pr->flags.throttling)
pr->flags.limit = 1;
return_VALUE(0);
......@@ -1232,14 +1829,12 @@ static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset)
"bus mastering control: %s\n"
"power management: %s\n"
"throttling control: %s\n"
"performance management: %s\n"
"limit interface: %s\n",
pr->id,
pr->acpi_id,
pr->flags.bm_control ? "yes" : "no",
pr->flags.power ? "yes" : "no",
pr->flags.throttling ? "yes" : "no",
pr->flags.performance ? "yes" : "no",
pr->flags.limit ? "yes" : "no");
end:
......@@ -1396,11 +1991,9 @@ static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset)
}
seq_printf(seq, "active limit: P%d:T%d\n"
"platform limit: P%d:T0\n"
"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->limit.user.px, pr->limit.user.tx,
pr->limit.thermal.px, pr->limit.thermal.tx);
......@@ -1447,15 +2040,6 @@ acpi_processor_write_limit (
return_VALUE(-EINVAL);
}
if (pr->flags.performance) {
if ((px < pr->performance_platform_limit)
|| (px > (pr->performance->state_count - 1))) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid px\n"));
return_VALUE(-EINVAL);
}
pr->limit.user.px = px;
}
if (pr->flags.throttling) {
if ((tx < 0) || (tx > (pr->throttling.state_count - 1))) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid tx\n"));
......@@ -1635,9 +2219,9 @@ acpi_processor_get_info (
}
acpi_processor_get_power_info(pr);
pr->flags.performance = 0;
pr->performance_platform_limit = 0;
acpi_processor_get_platform_limit(pr);
#ifdef CONFIG_CPU_FREQ
acpi_processor_ppc_has_changed(pr);
#endif
acpi_processor_get_throttling_info(pr);
acpi_processor_get_limit_info(pr);
......@@ -1651,7 +2235,6 @@ acpi_processor_notify (
u32 event,
void *data)
{
int result = 0;
struct acpi_processor *pr = (struct acpi_processor *) data;
struct acpi_device *device = NULL;
......@@ -1665,9 +2248,7 @@ acpi_processor_notify (
switch (event) {
case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
result = acpi_processor_get_platform_limit(pr);
if (!result)
acpi_processor_apply_limit(pr);
acpi_processor_ppc_has_changed(pr);
acpi_bus_generate_event(device, event,
pr->performance_platform_limit);
break;
......@@ -1813,6 +2394,10 @@ acpi_processor_init (void)
return_VALUE(-ENODEV);
}
acpi_thermal_cpufreq_init();
acpi_processor_ppc_init();
return_VALUE(0);
}
......@@ -1822,6 +2407,10 @@ acpi_processor_exit (void)
{
ACPI_FUNCTION_TRACE("acpi_processor_exit");
acpi_processor_ppc_exit();
acpi_thermal_cpufreq_exit();
acpi_bus_unregister_driver(&acpi_processor_driver);
remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
......
......@@ -302,13 +302,14 @@ acpi_table_parse_madt_family (
enum acpi_table_id id,
unsigned long madt_size,
int entry_id,
acpi_madt_entry_handler handler)
acpi_madt_entry_handler handler,
unsigned int max_entries)
{
void *madt = NULL;
acpi_table_entry_header *entry = NULL;
unsigned long count = 0;
unsigned long madt_end = 0;
unsigned int i = 0;
acpi_table_entry_header *entry;
unsigned int count = 0;
unsigned long madt_end;
unsigned int i;
if (!handler)
return -EINVAL;
......@@ -342,13 +343,18 @@ acpi_table_parse_madt_family (
((unsigned long) madt + madt_size);
while (((unsigned long) entry) < madt_end) {
if (entry->type == entry_id) {
count++;
if (entry->type == entry_id &&
(!max_entries || count++ < max_entries))
handler(entry);
}
entry = (acpi_table_entry_header *)
((unsigned long) entry + entry->length);
}
if (max_entries && count > max_entries) {
printk(KERN_WARNING PREFIX "[%s:0x%02x] ignored %i entries of "
"%i found\n", acpi_table_signatures[id], entry_id,
count - max_entries, count);
}
return count;
}
......@@ -357,10 +363,11 @@ acpi_table_parse_madt_family (
int __init
acpi_table_parse_madt (
enum acpi_madt_entry_id id,
acpi_madt_entry_handler handler)
acpi_madt_entry_handler handler,
unsigned int max_entries)
{
return acpi_table_parse_madt_family(ACPI_APIC, sizeof(struct acpi_table_madt),
id, handler);
id, handler, max_entries);
}
......@@ -585,4 +592,3 @@ acpi_table_init (void)
return 0;
}
......@@ -2,7 +2,7 @@
* toshiba_acpi.c - Toshiba Laptop ACPI Extras
*
*
* Copyright (C) 2002-2003 John Belmonte
* Copyright (C) 2002-2004 John Belmonte
*
* 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
......@@ -33,7 +33,7 @@
*
*/
#define TOSHIBA_ACPI_VERSION "0.16"
#define TOSHIBA_ACPI_VERSION "0.17"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
......@@ -48,9 +48,15 @@ MODULE_AUTHOR("John Belmonte");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");
#define MY_LOGPREFIX "toshiba_acpi: "
#define MY_ERR KERN_ERR MY_LOGPREFIX
#define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
#define MY_INFO KERN_INFO MY_LOGPREFIX
/* Toshiba ACPI method paths */
#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
#define METHOD_HCI "\\_SB_.VALD.GHCI"
#define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
/* Toshiba HCI interface definitions
......@@ -120,6 +126,16 @@ snscanf(const char* str, int n, const char* format, ...)
/* acpi interface wrappers
*/
static int
is_valid_acpi_path(const char* methodName)
{
acpi_handle handle;
acpi_status status;
status = acpi_get_handle(0, (char*)methodName, &handle);
return !ACPI_FAILURE(status);
}
static int
write_acpi_int(const char* methodName, int val)
{
......@@ -154,6 +170,8 @@ read_acpi_int(const char* methodName, int* pVal)
}
#endif
static const char* method_hci /*= 0*/;
/* Perform a raw HCI call. Here we don't care about input or output buffer
* format.
*/
......@@ -177,7 +195,7 @@ hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
results.length = sizeof(out_objs);
results.pointer = out_objs;
status = acpi_evaluate_object(0, METHOD_HCI, &params,
status = acpi_evaluate_object(0, (char*)method_hci, &params,
&results);
if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
for (i = 0; i < out_objs->package.count; ++i) {
......@@ -215,7 +233,7 @@ hci_read1(u32 reg, u32* out1, u32* result)
return status;
}
static struct proc_dir_entry* toshiba_proc_dir;
static struct proc_dir_entry* toshiba_proc_dir /*= 0*/;
static int force_fan;
static int last_key_event;
static int key_event_valid;
......@@ -270,7 +288,7 @@ read_lcd(char* p)
p += sprintf(p, "brightness_levels: %d\n",
HCI_LCD_BRIGHTNESS_LEVELS);
} else {
p += sprintf(p, "ERROR\n");
printk(MY_ERR "Error reading LCD brightness\n");
}
return p;
......@@ -310,7 +328,7 @@ read_video(char* p)
p += sprintf(p, "crt_out: %d\n", is_crt);
p += sprintf(p, "tv_out: %d\n", is_tv);
} else {
p += sprintf(p, "ERROR\n");
printk(MY_ERR "Error reading video out status\n");
}
return p;
......@@ -320,25 +338,31 @@ static unsigned long
write_video(const char* buffer, unsigned long count)
{
int value;
const char* buffer_end = buffer + count;
int remain = count;
int lcd_out = -1;
int crt_out = -1;
int tv_out = -1;
u32 hci_result;
int video_out;
/* scan expression. Multiple expressions may be delimited with ; */
do {
if (snscanf(buffer, count, " lcd_out : %i", &value) == 1)
/* scan expression. Multiple expressions may be delimited with ;
*
* NOTE: to keep scanning simple, invalid fields are ignored
*/
while (remain) {
if (snscanf(buffer, remain, " lcd_out : %i", &value) == 1)
lcd_out = value & 1;
else if (snscanf(buffer, count, " crt_out : %i", &value) == 1)
else if (snscanf(buffer, remain, " crt_out : %i", &value) == 1)
crt_out = value & 1;
else if (snscanf(buffer, count, " tv_out : %i", &value) == 1)
else if (snscanf(buffer, remain, " tv_out : %i", &value) == 1)
tv_out = value & 1;
/* advance to one character past the next ; */
do ++buffer;
while ((buffer < buffer_end) && (*(buffer-1) != ';'));
} while (buffer < buffer_end);
do {
++buffer;
--remain;
}
while (remain && *(buffer-1) != ';');
}
hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
if (hci_result == HCI_SUCCESS) {
......@@ -353,6 +377,8 @@ write_video(const char* buffer, unsigned long count)
* video setting if something changed. */
if (new_video_out != video_out)
write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
} else {
return -EFAULT;
}
return count;
......@@ -369,7 +395,7 @@ read_fan(char* p)
p += sprintf(p, "running: %d\n", (value > 0));
p += sprintf(p, "force_on: %d\n", force_fan);
} else {
p += sprintf(p, "ERROR\n");
printk(MY_ERR "Error reading fan status\n");
}
return p;
......@@ -413,8 +439,9 @@ read_keys(char* p)
* some machines where system events sporadically
* become disabled. */
hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
printk(MY_NOTICE "Re-enabled hotkeys\n");
} else {
p += sprintf(p, "ERROR\n");
printk(MY_ERR "Error reading hotkey status\n");
goto end;
}
}
......@@ -465,7 +492,7 @@ ProcItem proc_items[] =
{ 0 , 0 , 0 },
};
static acpi_status
static acpi_status __init
add_device(void)
{
struct proc_dir_entry* proc;
......@@ -483,7 +510,7 @@ add_device(void)
return(AE_OK);
}
static acpi_status
static acpi_status __exit
remove_device(void)
{
ProcItem* item;
......@@ -497,15 +524,19 @@ static int __init
toshiba_acpi_init(void)
{
acpi_status status = AE_OK;
int value;
u32 hci_result;
/* simple device detection: try reading an HCI register */
hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
if (hci_result != HCI_SUCCESS)
/* simple device detection: look for HCI method */
if (is_valid_acpi_path(METHOD_HCI_1))
method_hci = METHOD_HCI_1;
else if (is_valid_acpi_path(METHOD_HCI_2))
method_hci = METHOD_HCI_2;
else
return -ENODEV;
printk("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION);
printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
TOSHIBA_ACPI_VERSION);
printk(MY_INFO " HCI method: %s\n", method_hci);
force_fan = 0;
key_event_valid = 0;
......
......@@ -9,8 +9,6 @@
#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
......@@ -67,20 +65,22 @@ struct acpi_processor_px {
acpi_integer status; /* success indicator */
};
#define ACPI_PDC_REVISION_ID 0x1
struct acpi_processor_performance {
int state;
int platform_limit;
u16 control_register;
u16 status_register;
u8 control_register_bit_width;
u8 status_register_bit_width;
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;
unsigned int state;
unsigned int platform_limit;
struct acpi_pct_register control_register;
struct acpi_pct_register status_register;
unsigned int state_count;
struct acpi_processor_px *states;
/* the _PDC objects passed by the driver, if any */
struct acpi_object_list *pdc;
};
/* Throttling Control */
struct acpi_processor_tx {
......@@ -133,11 +133,11 @@ struct acpi_processor {
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);
extern void acpi_processor_unregister_performance (
struct acpi_processor_performance * performance,
unsigned int cpu);
#endif
......@@ -52,6 +52,9 @@
#ifndef __ASSEMBLY__
#ifdef CONFIG_IOSAPIC
#define NR_IOSAPICS 256
extern void __init iosapic_system_init (int pcat_compat);
extern void __init iosapic_init (unsigned long address,
unsigned int gsi_base);
......
......@@ -355,8 +355,8 @@ int acpi_numa_init (void);
int acpi_table_init (void);
int acpi_table_parse (enum acpi_table_id id, acpi_table_handler handler);
int acpi_get_table_header_early (enum acpi_table_id id, struct acpi_table_header **header);
int acpi_table_parse_madt (enum acpi_madt_entry_id id, acpi_madt_entry_handler handler);
int acpi_table_parse_srat (enum acpi_srat_entry_id id, acpi_madt_entry_handler handler);
int acpi_table_parse_madt (enum acpi_madt_entry_id id, acpi_madt_entry_handler handler, unsigned int max_entries);
int acpi_table_parse_srat (enum acpi_srat_entry_id id, acpi_madt_entry_handler handler, unsigned int max_entries);
void acpi_table_print (struct acpi_table_header *header, unsigned long phys_addr);
void acpi_table_print_madt_entry (acpi_table_entry_header *madt);
void acpi_table_print_srat_entry (acpi_table_entry_header *srat);
......
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