From 288f3ad406460f03642a41bb945826891a7b866f Mon Sep 17 00:00:00 2001 From: Marek W Date: Mon, 14 Aug 2006 22:37:20 -0700 Subject: [PATCH 01/28] ACPI: asus_acpi: W3000 support Add support for W3000 (W3V) and indirectly fixes an issue with kmilo under KDE (it was triggering excessive LCD read error messages by querying asus_acpi module) allowing people (I am probably the only one who tested this) with W3000 to run kmilo. Cc: Karol Kozimor Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/asus_acpi.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index e9ee4c52a5f..ebc033f87e7 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -138,6 +138,7 @@ struct asus_hotk { S2x, //S200 (J1 reported), Victor MP-XP7210 W1N, //W1000N W5A, //W5A + W3V, //W3030V xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N //(Centrino) END_MODEL @@ -376,6 +377,17 @@ static struct model_data model_conf[END_MODEL] = { .display_get = "\\ADVG"}, { + .name = "W3V", + .mt_mled = "MLED", + .mt_wled = "WLED", + .mt_lcd_switch = xxN_PREFIX "_Q10", + .lcd_status = "\\BKLT", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\INFB"}, + + { .name = "xxN", .mt_mled = "MLED", /* WLED present, but not controlled by ACPI */ @@ -1097,6 +1109,8 @@ static int asus_model_match(char *model) return A4G; else if (strncmp(model, "W1N", 3) == 0) return W1N; + else if (strncmp(model, "W3V", 3) == 0) + return W3V; else if (strncmp(model, "W5A", 3) == 0) return W5A; else @@ -1200,9 +1214,10 @@ static int asus_hotk_get_info(void) hotk->methods->mt_wled = NULL; /* L5D's WLED is not controlled by ACPI */ else if (strncmp(string, "M2N", 3) == 0 || + strncmp(string, "W3V", 3) == 0 || strncmp(string, "S1N", 3) == 0) hotk->methods->mt_wled = "WLED"; - /* M2N and S1N have a usable WLED */ + /* M2N, S1N and W3V have a usable WLED */ else if (asus_info) { if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) hotk->methods->mled_status = NULL; From 4d6bd5ea4ec4991901a8cf5a586babef68e1fa3f Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 22 Sep 2006 12:19:14 +0200 Subject: [PATCH 02/28] ACPI: ibm_acpi: Remove experimental status for brightness and volume. The brightness and volume features from ibm-acpi are stable. The experimental flag is no longer needed. Signed-off-by: Stefan Schmidt Acked-by: Borislav Deianov Signed-off-by: Len Brown --- drivers/acpi/ibm_acpi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 15fc12482ba..003a9876c96 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1702,13 +1702,11 @@ static struct ibm_struct ibms[] = { .name = "brightness", .read = brightness_read, .write = brightness_write, - .experimental = 1, }, { .name = "volume", .read = volume_read, .write = volume_write, - .experimental = 1, }, { .name = "fan", From 24f7ff0af855ece60064a2532d8b316df02983c6 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 22 Sep 2006 12:19:15 +0200 Subject: [PATCH 03/28] ACPI: ibm_acpi: Update documentation for brightness and volume. Document the change of the experimental flag for brightness and volume. Signed-off-by: Stefan Schmidt Acked-by: Borislav Deianov Signed-off-by: Len Brown --- Documentation/ibm-acpi.txt | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 71aa4034527..1672590d8ef 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -30,8 +30,8 @@ detailed description): - ACPI sounds - temperature sensors - Experimental: embedded controller register dump - - Experimental: LCD brightness control - - Experimental: volume control + - LCD brightness control + - Volume control - Experimental: fan speed, fan enable/disable A compatibility table by model and feature is maintained on the web @@ -523,13 +523,8 @@ registers contain the current battery capacity, etc. If you experiment with this, do send me your results (including some complete dumps with a description of the conditions when they were taken.) -EXPERIMENTAL: LCD brightness control -- /proc/acpi/ibm/brightness ------------------------------------------------------------------ - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. +LCD brightness control -- /proc/acpi/ibm/brightness +--------------------------------------------------- This feature allows software control of the LCD brightness on ThinkPad models which don't have a hardware brightness slider. The available @@ -542,13 +537,8 @@ commands are: The number range is 0 to 7, although not all of them may be distinct. The current brightness level is shown in the file. -EXPERIMENTAL: Volume control -- /proc/acpi/ibm/volume ------------------------------------------------------ - -This feature is marked EXPERIMENTAL because the implementation -directly accesses hardware registers and may not work as expected. USE -WITH CAUTION! To use this feature, you need to supply the -experimental=1 parameter when loading the module. +Volume control -- /proc/acpi/ibm/volume +--------------------------------------- This feature allows volume control on ThinkPad models which don't have a hardware volume knob. The available commands are: From 28b779d127d3038ff83f42259d135a063b7cd848 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt Date: Fri, 22 Sep 2006 12:19:16 +0200 Subject: [PATCH 04/28] ACPI: ibm_acpi: Documentation the wan feature. Document the wan feature Jeremy Fitzhardinge added to ibm_acpi. Signed-off-by: Stefan Schmidt Acked-by: Borislav Deianov Signed-off-by: Len Brown --- Documentation/ibm-acpi.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 1672590d8ef..00b8cf3999c 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -33,6 +33,7 @@ detailed description): - LCD brightness control - Volume control - Experimental: fan speed, fan enable/disable + - Experimental: WAN enable and disable A compatibility table by model and feature is maintained on the web site, http://ibm-acpi.sf.net/. I appreciate any success or failure @@ -601,6 +602,23 @@ with the following command: echo 'level ' > /proc/acpi/ibm/thermal +EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan +--------------------------------------- + +This feature is marked EXPERIMENTAL because the implementation +directly accesses hardware registers and may not work as expected. USE +WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. + +This feature shows the presence and current state of a WAN (Sierra +Wireless EV-DO) device. If WAN is installed, the following commands can +be used: + + echo enable > /proc/acpi/ibm/wan + echo disable > /proc/acpi/ibm/wan + +It was tested on a Lenovo Thinkpad X60. It should probably work on other +Thinkpad models which come with this module installed. Multiple Commands, Module Parameters ------------------------------------ From 963497c12acb4d43caa9751b9291b014eea51a1a Mon Sep 17 00:00:00 2001 From: "Lebedev, Vladimir P" Date: Tue, 5 Sep 2006 19:49:13 +0400 Subject: [PATCH 05/28] ACPI: sbs: check for NULL device pointer Signed-off-by: Len Brown --- drivers/acpi/sbs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 62bef0b3b61..79f38f03677 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -1685,10 +1685,16 @@ static int acpi_sbs_add(struct acpi_device *device) int acpi_sbs_remove(struct acpi_device *device, int type) { - struct acpi_sbs *sbs = (struct acpi_sbs *)acpi_driver_data(device); + struct acpi_sbs *sbs = NULL; int id; - if (!device || !sbs) { + if (!device) { + return -EINVAL; + } + + sbs = (struct acpi_sbs *)acpi_driver_data(device); + + if (!sbs) { return -EINVAL; } From 3cd5b87d96db503f69a5892b8f5350d356d18969 Mon Sep 17 00:00:00 2001 From: "Lebedev, Vladimir P" Date: Tue, 5 Sep 2006 19:59:22 +0400 Subject: [PATCH 06/28] ACPI: sbs: fix module_param() initializers Signed-off-by: Len Brown --- drivers/acpi/sbs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 79f38f03677..8908a975e57 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -98,11 +98,11 @@ static int update_info_mode = UPDATE_INFO_MODE; static int update_time = UPDATE_TIME; static int update_time2 = UPDATE_TIME2; -module_param(capacity_mode, int, CAPACITY_UNIT); -module_param(update_mode, int, UPDATE_MODE); -module_param(update_info_mode, int, UPDATE_INFO_MODE); -module_param(update_time, int, UPDATE_TIME); -module_param(update_time2, int, UPDATE_TIME2); +module_param(capacity_mode, int, 0); +module_param(update_mode, int, 0); +module_param(update_info_mode, int, 0); +module_param(update_time, int, 0); +module_param(update_time2, int, 0); static int acpi_sbs_add(struct acpi_device *device); static int acpi_sbs_remove(struct acpi_device *device, int type); From 991528d7348667924176f3e29addea0675298944 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Mon, 25 Sep 2006 16:28:13 -0700 Subject: [PATCH 07/28] ACPI: Processor native C-states using MWAIT Intel processors starting with the Core Duo support support processor native C-state using the MWAIT instruction. Refer: Intel Architecture Software Developer's Manual http://www.intel.com/design/Pentium4/manuals/253668.htm Platform firmware exports the support for Native C-state to OS using ACPI _PDC and _CST methods. Refer: Intel Processor Vendor-Specific ACPI: Interface Specification http://www.intel.com/technology/iapc/acpi/downloads/302223.htm With Processor Native C-state, we use 'MWAIT' instruction on the processor to enter different C-states (C1, C2, C3). We won't use the special IO ports to enter C-state and no SMM mode etc required to enter C-state. Overall this will mean better C-state support. One major advantage of using MWAIT for all C-states is, with this and "treat interrupt as break event" feature of MWAIT, we can now get accurate timing for the time spent in C1, C2, .. states. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- arch/i386/kernel/acpi/cstate.c | 122 ++++++++++++++++++++++++++++++++- arch/i386/kernel/process.c | 24 ++++--- arch/x86_64/kernel/process.c | 24 ++++--- drivers/acpi/processor_idle.c | 97 ++++++++++++++++---------- include/acpi/pdc_intel.h | 9 ++- include/acpi/processor.h | 18 +++++ include/asm-i386/processor.h | 2 + include/asm-x86_64/processor.h | 2 + 8 files changed, 242 insertions(+), 56 deletions(-) diff --git a/arch/i386/kernel/acpi/cstate.c b/arch/i386/kernel/acpi/cstate.c index 25db49ef177..20563e52c62 100644 --- a/arch/i386/kernel/acpi/cstate.c +++ b/arch/i386/kernel/acpi/cstate.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -41,5 +42,124 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, flags->bm_check = 1; } } - EXPORT_SYMBOL(acpi_processor_power_init_bm_check); + +/* The code below handles cstate entry with monitor-mwait pair on Intel*/ + +struct cstate_entry_s { + struct { + unsigned int eax; + unsigned int ecx; + } states[ACPI_PROCESSOR_MAX_POWER]; +}; +static struct cstate_entry_s *cpu_cstate_entry; /* per CPU ptr */ + +static short mwait_supported[ACPI_PROCESSOR_MAX_POWER]; + +#define MWAIT_SUBSTATE_MASK (0xf) +#define MWAIT_SUBSTATE_SIZE (4) + +#define CPUID_MWAIT_LEAF (5) +#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1) +#define CPUID5_ECX_INTERRUPT_BREAK (0x2) + +#define MWAIT_ECX_INTERRUPT_BREAK (0x1) + +#define NATIVE_CSTATE_BEYOND_HALT (2) + +int acpi_processor_ffh_cstate_probe(unsigned int cpu, + struct acpi_processor_cx *cx, struct acpi_power_register *reg) +{ + struct cstate_entry_s *percpu_entry; + struct cpuinfo_x86 *c = cpu_data + cpu; + + cpumask_t saved_mask; + int retval; + unsigned int eax, ebx, ecx, edx; + unsigned int edx_part; + unsigned int cstate_type; /* C-state type and not ACPI C-state type */ + unsigned int num_cstate_subtype; + + if (!cpu_cstate_entry || c->cpuid_level < CPUID_MWAIT_LEAF ) + return -1; + + if (reg->bit_offset != NATIVE_CSTATE_BEYOND_HALT) + return -1; + + percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); + percpu_entry->states[cx->index].eax = 0; + percpu_entry->states[cx->index].ecx = 0; + + /* Make sure we are running on right CPU */ + saved_mask = current->cpus_allowed; + retval = set_cpus_allowed(current, cpumask_of_cpu(cpu)); + if (retval) + return -1; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + /* Check whether this particular cx_type (in CST) is supported or not */ + cstate_type = (cx->address >> MWAIT_SUBSTATE_SIZE) + 1; + edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE); + num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK; + + retval = 0; + if (num_cstate_subtype < (cx->address & MWAIT_SUBSTATE_MASK)) { + retval = -1; + goto out; + } + + /* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */ + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) { + retval = -1; + goto out; + } + percpu_entry->states[cx->index].ecx = MWAIT_ECX_INTERRUPT_BREAK; + + /* Use the hint in CST */ + percpu_entry->states[cx->index].eax = cx->address; + + if (!mwait_supported[cstate_type]) { + mwait_supported[cstate_type] = 1; + printk(KERN_DEBUG "Monitor-Mwait will be used to enter C-%d " + "state\n", cx->type); + } + +out: + set_cpus_allowed(current, saved_mask); + return retval; +} +EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe); + +void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) +{ + unsigned int cpu = smp_processor_id(); + struct cstate_entry_s *percpu_entry; + + percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); + mwait_idle_with_hints(percpu_entry->states[cx->index].eax, + percpu_entry->states[cx->index].ecx); +} +EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter); + +static int __init ffh_cstate_init(void) +{ + struct cpuinfo_x86 *c = &boot_cpu_data; + if (c->x86_vendor != X86_VENDOR_INTEL) + return -1; + + cpu_cstate_entry = alloc_percpu(struct cstate_entry_s); + return 0; +} + +static void __exit ffh_cstate_exit(void) +{ + if (cpu_cstate_entry) { + free_percpu(cpu_cstate_entry); + cpu_cstate_entry = NULL; + } +} + +arch_initcall(ffh_cstate_init); +__exitcall(ffh_cstate_exit); diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index b0a07801d9d..57d375900af 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -236,18 +236,26 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait); * We execute MONITOR against need_resched and enter optimized wait state * through MWAIT. Whenever someone changes need_resched, we would be woken * up from MWAIT (without an IPI). + * + * New with Core Duo processors, MWAIT can take some hints based on CPU + * capability. */ +void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) +{ + if (!need_resched()) { + __monitor((void *)¤t_thread_info()->flags, 0, 0); + smp_mb(); + if (!need_resched()) + __mwait(eax, ecx); + } +} + +/* Default MONITOR/MWAIT with no hints, used for default C1 state */ static void mwait_idle(void) { local_irq_enable(); - - while (!need_resched()) { - __monitor((void *)¤t_thread_info()->flags, 0, 0); - smp_mb(); - if (need_resched()) - break; - __mwait(0, 0); - } + while (!need_resched()) + mwait_idle_with_hints(0, 0); } void __devinit select_idle_routine(const struct cpuinfo_x86 *c) diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 5e95b257ee2..49f7fac6229 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c @@ -238,18 +238,26 @@ void cpu_idle (void) * We execute MONITOR against need_resched and enter optimized wait state * through MWAIT. Whenever someone changes need_resched, we would be woken * up from MWAIT (without an IPI). + * + * New with Core Duo processors, MWAIT can take some hints based on CPU + * capability. */ +void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) +{ + if (!need_resched()) { + __monitor((void *)¤t_thread_info()->flags, 0, 0); + smp_mb(); + if (!need_resched()) + __mwait(eax, ecx); + } +} + +/* Default MONITOR/MWAIT with no hints, used for default C1 state */ static void mwait_idle(void) { local_irq_enable(); - - while (!need_resched()) { - __monitor((void *)¤t_thread_info()->flags, 0, 0); - smp_mb(); - if (need_resched()) - break; - __mwait(0, 0); - } + while (!need_resched()) + mwait_idle_with_hints(0,0); } void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0a395fca843..429a39dbd75 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -219,6 +219,23 @@ static void acpi_safe_halt(void) static atomic_t c3_cpu_count; +/* Common C-state entry for C2, C3, .. */ +static void acpi_cstate_enter(struct acpi_processor_cx *cstate) +{ + if (cstate->space_id == ACPI_CSTATE_FFH) { + /* Call into architectural FFH based C-state */ + acpi_processor_ffh_cstate_enter(cstate); + } else { + int unused; + /* IO port based C-state */ + inb(cstate->address); + /* Dummy wait op - must do something useless after P_LVL2 read + because chipsets cannot guarantee that STPCLK# signal + gets asserted in time to freeze execution properly. */ + unused = inl(acpi_fadt.xpm_tmr_blk.address); + } +} + static void acpi_processor_idle(void) { struct acpi_processor *pr = NULL; @@ -361,11 +378,7 @@ static void acpi_processor_idle(void) /* Get start time (ticks) */ t1 = inl(acpi_fadt.xpm_tmr_blk.address); /* Invoke C2 */ - inb(cx->address); - /* Dummy wait op - must do something useless after P_LVL2 read - because chipsets cannot guarantee that STPCLK# signal - gets asserted in time to freeze execution properly. */ - t2 = inl(acpi_fadt.xpm_tmr_blk.address); + acpi_cstate_enter(cx); /* Get end time (ticks) */ t2 = inl(acpi_fadt.xpm_tmr_blk.address); @@ -401,9 +414,7 @@ static void acpi_processor_idle(void) /* Get start time (ticks) */ t1 = inl(acpi_fadt.xpm_tmr_blk.address); /* Invoke C3 */ - inb(cx->address); - /* Dummy wait op (see above) */ - t2 = inl(acpi_fadt.xpm_tmr_blk.address); + acpi_cstate_enter(cx); /* Get end time (ticks) */ t2 = inl(acpi_fadt.xpm_tmr_blk.address); if (pr->flags.bm_check) { @@ -628,20 +639,16 @@ static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) return 0; } -static int acpi_processor_get_power_info_default_c1(struct acpi_processor *pr) +static int acpi_processor_get_power_info_default(struct acpi_processor *pr) { - - /* Zero initialize all the C-states info. */ - memset(pr->power.states, 0, sizeof(pr->power.states)); - - /* set the first C-State to C1 */ - pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; - - /* the C0 state only exists as a filler in our array, - * and all processors need to support C1 */ + if (!pr->power.states[ACPI_STATE_C1].valid) { + /* set the first C-State to C1 */ + /* all processors need to support C1 */ + pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; + pr->power.states[ACPI_STATE_C1].valid = 1; + } + /* the C0 state only exists as a filler in our array */ pr->power.states[ACPI_STATE_C0].valid = 1; - pr->power.states[ACPI_STATE_C1].valid = 1; - return 0; } @@ -658,12 +665,7 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) if (nocst) return -ENODEV; - current_count = 1; - - /* Zero initialize C2 onwards and prepare for fresh CST lookup */ - for (i = 2; i < ACPI_PROCESSOR_MAX_POWER; i++) - memset(&(pr->power.states[i]), 0, - sizeof(struct acpi_processor_cx)); + current_count = 0; status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -718,22 +720,39 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) continue; - cx.address = (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) ? - 0 : reg->address; - /* There should be an easy way to extract an integer... */ obj = (union acpi_object *)&(element->package.elements[1]); if (obj->type != ACPI_TYPE_INTEGER) continue; cx.type = obj->integer.value; + /* + * Some buggy BIOSes won't list C1 in _CST - + * Let acpi_processor_get_power_info_default() handle them later + */ + if (i == 1 && cx.type != ACPI_STATE_C1) + current_count++; - if ((cx.type != ACPI_STATE_C1) && - (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) - continue; + cx.address = reg->address; + cx.index = current_count + 1; - if ((cx.type < ACPI_STATE_C2) || (cx.type > ACPI_STATE_C3)) - continue; + cx.space_id = ACPI_CSTATE_SYSTEMIO; + if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { + if (acpi_processor_ffh_cstate_probe + (pr->id, &cx, reg) == 0) { + cx.space_id = ACPI_CSTATE_FFH; + } else if (cx.type != ACPI_STATE_C1) { + /* + * C1 is a special case where FIXED_HARDWARE + * can be handled in non-MWAIT way as well. + * In that case, save this _CST entry info. + * That is, we retain space_id of SYSTEM_IO for + * halt based C1. + * Otherwise, ignore this info and continue. + */ + continue; + } + } obj = (union acpi_object *)&(element->package.elements[2]); if (obj->type != ACPI_TYPE_INTEGER) @@ -938,12 +957,18 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr) /* NOTE: the idle thread may not be running while calling * this function */ - /* Adding C1 state */ - acpi_processor_get_power_info_default_c1(pr); + /* Zero initialize all the C-states info. */ + memset(pr->power.states, 0, sizeof(pr->power.states)); + result = acpi_processor_get_power_info_cst(pr); if (result == -ENODEV) acpi_processor_get_power_info_fadt(pr); + if (result) + return result; + + acpi_processor_get_power_info_default(pr); + pr->power.count = acpi_processor_power_verify(pr); /* diff --git a/include/acpi/pdc_intel.h b/include/acpi/pdc_intel.h index c5472be6f3a..e72bfdd887f 100644 --- a/include/acpi/pdc_intel.h +++ b/include/acpi/pdc_intel.h @@ -13,6 +13,7 @@ #define ACPI_PDC_SMP_C_SWCOORD (0x0040) #define ACPI_PDC_SMP_T_SWCOORD (0x0080) #define ACPI_PDC_C_C1_FFH (0x0100) +#define ACPI_PDC_C_C2C3_FFH (0x0200) #define ACPI_PDC_EST_CAPABILITY_SMP (ACPI_PDC_SMP_C1PT | \ ACPI_PDC_C_C1_HALT | \ @@ -23,8 +24,10 @@ ACPI_PDC_SMP_P_SWCOORD | \ ACPI_PDC_P_FFH) -#define ACPI_PDC_C_CAPABILITY_SMP (ACPI_PDC_SMP_C2C3 | \ - ACPI_PDC_SMP_C1PT | \ - ACPI_PDC_C_C1_HALT) +#define ACPI_PDC_C_CAPABILITY_SMP (ACPI_PDC_SMP_C2C3 | \ + ACPI_PDC_SMP_C1PT | \ + ACPI_PDC_C_C1_HALT | \ + ACPI_PDC_C_C1_FFH | \ + ACPI_PDC_C_C2C3_FFH) #endif /* __PDC_INTEL_H__ */ diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 9dd5b75961f..7798d2a9f79 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -29,6 +29,9 @@ #define DOMAIN_COORD_TYPE_SW_ANY 0xfd #define DOMAIN_COORD_TYPE_HW_ALL 0xfe +#define ACPI_CSTATE_SYSTEMIO (0) +#define ACPI_CSTATE_FFH (1) + /* Power Management */ struct acpi_processor_cx; @@ -58,6 +61,8 @@ struct acpi_processor_cx { u8 valid; u8 type; u32 address; + u8 space_id; + u8 index; u32 latency; u32 latency_ticks; u32 power; @@ -206,6 +211,9 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr); #ifdef ARCH_HAS_POWER_INIT void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, unsigned int cpu); +int acpi_processor_ffh_cstate_probe(unsigned int cpu, + struct acpi_processor_cx *cx, struct acpi_power_register *reg); +void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cstate); #else static inline void acpi_processor_power_init_bm_check(struct acpi_processor_flags @@ -214,6 +222,16 @@ static inline void acpi_processor_power_init_bm_check(struct flags->bm_check = 1; return; } +static inline int acpi_processor_ffh_cstate_probe(unsigned int cpu, + struct acpi_processor_cx *cx, struct acpi_power_register *reg) +{ + return -1; +} +static inline void acpi_processor_ffh_cstate_enter( + struct acpi_processor_cx *cstate) +{ + return; +} #endif /* in processor_perflib.c */ diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 2277127696d..e0ddca94d50 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -306,6 +306,8 @@ static inline void __mwait(unsigned long eax, unsigned long ecx) : :"a" (eax), "c" (ecx)); } +extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx); + /* from system description table in BIOS. Mostly for MCA use, but others may find it useful. */ extern unsigned int machine_id; diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h index de9c3147ee4..cef17e0f828 100644 --- a/include/asm-x86_64/processor.h +++ b/include/asm-x86_64/processor.h @@ -475,6 +475,8 @@ static inline void __mwait(unsigned long eax, unsigned long ecx) : :"a" (eax), "c" (ecx)); } +extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx); + #define stack_current() \ ({ \ struct thread_info *ti; \ From d7a76e4cb3b4469b1eccb6204c053e3ebcd4c196 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Sep 2006 12:12:24 -0400 Subject: [PATCH 08/28] ACPI: consolidate functions in acpi ec driver Unify the following functions: acpi_ec_poll_read() acpi_ec_poll_write() acpi_ec_poll_query() acpi_ec_intr_read() acpi_ec_intr_write() acpi_ec_intr_query() into: acpi_ec_poll_transaction() acpi_ec_intr_transaction() These new functions take as arguments an ACPI EC command, a few bytes to write to the EC data register and a buffer for a few bytes to read from the EC data register. The old _read(), _write(), _query() are just special cases of these functions. Then unified the code in acpi_ec_poll_transaction() and acpi_ec_intr_transaction() a little more. Both functions are now just wrappers around the new acpi_ec_transaction_unlocked() function. The latter contains the EC access logic, the two original function now just do their special way of locking and call the the new function for the actual work. This saves a lot of very similar code. The primary reason for doing this, however, is that my driver for MSI 270 laptops needs to issue some non-standard EC commands in a safe way. Due to this I added a new exported function similar to ec_write()/ec_write() which is called ec_transaction() and is essentially just a wrapper around acpi_ec_{poll,intr}_transaction(). Signed-off-by: Lennart Poettering Acked-by: Luming Yu Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/ec.c | 355 ++++++++++++++----------------------------- include/linux/acpi.h | 3 + 2 files changed, 113 insertions(+), 245 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e5d79636285..a0dcbad97c4 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -122,12 +122,12 @@ union acpi_ec { static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event); static int acpi_ec_intr_wait(union acpi_ec *ec, unsigned int event); -static int acpi_ec_poll_read(union acpi_ec *ec, u8 address, u32 * data); -static int acpi_ec_intr_read(union acpi_ec *ec, u8 address, u32 * data); -static int acpi_ec_poll_write(union acpi_ec *ec, u8 address, u8 data); -static int acpi_ec_intr_write(union acpi_ec *ec, u8 address, u8 data); -static int acpi_ec_poll_query(union acpi_ec *ec, u32 * data); -static int acpi_ec_intr_query(union acpi_ec *ec, u32 * data); +static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len); +static int acpi_ec_intr_transaction(union acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len); static void acpi_ec_gpe_poll_query(void *ec_cxt); static void acpi_ec_gpe_intr_query(void *ec_cxt); static u32 acpi_ec_gpe_poll_handler(void *data); @@ -302,61 +302,95 @@ end: } #endif /* ACPI_FUTURE_USAGE */ -static int acpi_ec_read(union acpi_ec *ec, u8 address, u32 * data) +static int acpi_ec_transaction(union acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { if (acpi_ec_poll_mode) - return acpi_ec_poll_read(ec, address, data); + return acpi_ec_poll_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); else - return acpi_ec_intr_read(ec, address, data); + return acpi_ec_intr_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); +} +static int acpi_ec_read(union acpi_ec *ec, u8 address, u32 * data) +{ + int result; + u8 d; + result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, &address, 1, &d, 1); + *data = d; + return result; } static int acpi_ec_write(union acpi_ec *ec, u8 address, u8 data) { - if (acpi_ec_poll_mode) - return acpi_ec_poll_write(ec, address, data); - else - return acpi_ec_intr_write(ec, address, data); + u8 wdata[2] = { address, data }; + return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, wdata, 2, NULL, 0); } -static int acpi_ec_poll_read(union acpi_ec *ec, u8 address, u32 * data) + +static int acpi_ec_transaction_unlocked(union acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) +{ + int result; + + acpi_hw_low_level_write(8, command, &ec->common.command_addr); + + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + return result; + + for (; wdata_len > 0; wdata_len --) { + + acpi_hw_low_level_write(8, *(wdata++), &ec->common.data_addr); + + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + return result; + } + + + for (; rdata_len > 0; rdata_len --) { + u32 d; + + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (result) + return result; + + acpi_hw_low_level_read(8, &d, &ec->common.data_addr); + *(rdata++) = (u8) d; + } + + return 0; +} + +static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { acpi_status status = AE_OK; - int result = 0; + int result; u32 glk = 0; - - if (!ec || !data) + if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata)) return -EINVAL; - *data = 0; + if (rdata) + memset(rdata, 0, rdata_len); if (ec->common.global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return -ENODEV; - } + } if (down_interruptible(&ec->poll.sem)) { result = -ERESTARTSYS; goto end_nosem; } - - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, - &ec->common.command_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (result) - goto end; - acpi_hw_low_level_write(8, address, &ec->common.data_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); - if (result) - goto end; - - acpi_hw_low_level_read(8, data, &ec->common.data_addr); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", - *data, address)); - - end: + result = acpi_ec_transaction_unlocked(ec, command, + wdata, wdata_len, + rdata, rdata_len); up(&ec->poll.sem); + end_nosem: if (ec->common.global_lock) acpi_release_global_lock(glk); @@ -364,65 +398,18 @@ end_nosem: return result; } -static int acpi_ec_poll_write(union acpi_ec *ec, u8 address, u8 data) +static int acpi_ec_intr_transaction(union acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { - int result = 0; - acpi_status status = AE_OK; - u32 glk = 0; - - - if (!ec) - return -EINVAL; - - if (ec->common.global_lock) { - status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); - if (ACPI_FAILURE(status)) - return -ENODEV; - } - - if (down_interruptible(&ec->poll.sem)) { - result = -ERESTARTSYS; - goto end_nosem; - } - - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, - &ec->common.command_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (result) - goto end; - - acpi_hw_low_level_write(8, address, &ec->common.data_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (result) - goto end; - - acpi_hw_low_level_write(8, data, &ec->common.data_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (result) - goto end; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", - data, address)); - - end: - up(&ec->poll.sem); -end_nosem: - if (ec->common.global_lock) - acpi_release_global_lock(glk); - - return result; -} - -static int acpi_ec_intr_read(union acpi_ec *ec, u8 address, u32 * data) -{ - int status = 0; + int status; u32 glk; - - if (!ec || !data) + if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata)) return -EINVAL; - *data = 0; + if (rdata) + memset(rdata, 0, rdata_len); if (ec->common.global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); @@ -438,72 +425,12 @@ static int acpi_ec_intr_read(union acpi_ec *ec, u8 address, u32 * data) printk(KERN_DEBUG PREFIX "read EC, IB not empty\n"); goto end; } - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, - &ec->common.command_addr); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (status) { - printk(KERN_DEBUG PREFIX "read EC, IB not empty\n"); - } - acpi_hw_low_level_write(8, address, &ec->common.data_addr); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); - if (status) { - printk(KERN_DEBUG PREFIX "read EC, OB not full\n"); - goto end; - } - acpi_hw_low_level_read(8, data, &ec->common.data_addr); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", - *data, address)); - - end: - up(&ec->intr.sem); - - if (ec->common.global_lock) - acpi_release_global_lock(glk); - - return status; -} - -static int acpi_ec_intr_write(union acpi_ec *ec, u8 address, u8 data) -{ - int status = 0; - u32 glk; - - - if (!ec) - return -EINVAL; - - if (ec->common.global_lock) { - status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); - if (ACPI_FAILURE(status)) - return -ENODEV; - } - - WARN_ON(in_interrupt()); - down(&ec->intr.sem); - - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (status) { - printk(KERN_DEBUG PREFIX "write EC, IB not empty\n"); - } - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, - &ec->common.command_addr); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (status) { - printk(KERN_DEBUG PREFIX "write EC, IB not empty\n"); - } - - acpi_hw_low_level_write(8, address, &ec->common.data_addr); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (status) { - printk(KERN_DEBUG PREFIX "write EC, IB not empty\n"); - } - - acpi_hw_low_level_write(8, data, &ec->common.data_addr); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", - data, address)); + status = acpi_ec_transaction_unlocked(ec, command, + wdata, wdata_len, + rdata, rdata_len); +end: up(&ec->intr.sem); if (ec->common.global_lock) @@ -554,106 +481,44 @@ int ec_write(u8 addr, u8 val) EXPORT_SYMBOL(ec_write); -static int acpi_ec_query(union acpi_ec *ec, u32 * data) +extern int ec_transaction(u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { - if (acpi_ec_poll_mode) - return acpi_ec_poll_query(ec, data); - else - return acpi_ec_intr_query(ec, data); + union acpi_ec *ec; + + if (!first_ec) + return -ENODEV; + + ec = acpi_driver_data(first_ec); + + return acpi_ec_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); } -static int acpi_ec_poll_query(union acpi_ec *ec, u32 * data) -{ - int result = 0; - acpi_status status = AE_OK; - u32 glk = 0; +EXPORT_SYMBOL(ec_transaction); - if (!ec || !data) - return -EINVAL; +static int acpi_ec_query(union acpi_ec *ec, u32 * data) { + int result; + u8 d; - *data = 0; + if (!ec || !data) + return -EINVAL; - if (ec->common.global_lock) { - status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); - if (ACPI_FAILURE(status)) - return -ENODEV; - } + /* + * Query the EC to find out which _Qxx method we need to evaluate. + * Note that successful completion of the query causes the ACPI_EC_SCI + * bit to be cleared (and thus clearing the interrupt source). + */ - /* - * Query the EC to find out which _Qxx method we need to evaluate. - * Note that successful completion of the query causes the ACPI_EC_SCI - * bit to be cleared (and thus clearing the interrupt source). - */ - if (down_interruptible(&ec->poll.sem)) { - result = -ERESTARTSYS; - goto end_nosem; - } - - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, - &ec->common.command_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); - if (result) - goto end; + result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1); + if (result) + return result; - acpi_hw_low_level_read(8, data, &ec->common.data_addr); - if (!*data) - result = -ENODATA; + if (!d) + return -ENODATA; - end: - up(&ec->poll.sem); -end_nosem: - if (ec->common.global_lock) - acpi_release_global_lock(glk); - - return result; -} -static int acpi_ec_intr_query(union acpi_ec *ec, u32 * data) -{ - int status = 0; - u32 glk; - - - if (!ec || !data) - return -EINVAL; - *data = 0; - - if (ec->common.global_lock) { - status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); - if (ACPI_FAILURE(status)) - return -ENODEV; - } - - down(&ec->intr.sem); - - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (status) { - printk(KERN_DEBUG PREFIX "query EC, IB not empty\n"); - goto end; - } - /* - * Query the EC to find out which _Qxx method we need to evaluate. - * Note that successful completion of the query causes the ACPI_EC_SCI - * bit to be cleared (and thus clearing the interrupt source). - */ - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, - &ec->common.command_addr); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); - if (status) { - printk(KERN_DEBUG PREFIX "query EC, OB not full\n"); - goto end; - } - - acpi_hw_low_level_read(8, data, &ec->common.data_addr); - if (!*data) - status = -ENODATA; - - end: - up(&ec->intr.sem); - - if (ec->common.global_lock) - acpi_release_global_lock(glk); - - return status; + *data = d; + return 0; } /* -------------------------------------------------------------------------- diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 88b5dfd8ee1..2b0c955590f 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -494,6 +494,9 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver); extern int ec_read(u8 addr, u8 *val); extern int ec_write(u8 addr, u8 val); +extern int ec_transaction(u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len); #endif /*CONFIG_ACPI_EC*/ From 7c6db5e51227761f42c6ac8260753f5c24dc1dde Mon Sep 17 00:00:00 2001 From: "Denis M. Sadykov" Date: Tue, 26 Sep 2006 19:50:33 +0400 Subject: [PATCH 09/28] ACPI: EC: Remove unnecessary delay added by previous transation patch. Remove unnecessary delay (50 ms) while reading data from EC in interrupt mode. Signed-off-by: Alexey Y. Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 103 ++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a0dcbad97c4..b6f935d0c3a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -100,7 +100,7 @@ union acpi_ec { struct acpi_generic_address command_addr; struct acpi_generic_address data_addr; unsigned long global_lock; - unsigned int expect_event; + u8 expect_event; atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ atomic_t pending_gpe; struct semaphore sem; @@ -121,7 +121,7 @@ union acpi_ec { }; static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event); -static int acpi_ec_intr_wait(union acpi_ec *ec, unsigned int event); +static int acpi_ec_intr_wait(union acpi_ec *ec, u8 event); static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len); @@ -161,6 +161,22 @@ static u32 acpi_ec_read_status(union acpi_ec *ec) return status; } +static int acpi_ec_check_status(u32 status, u8 event) { + + switch (event) { + case ACPI_EC_EVENT_OBF: + if (status & ACPI_EC_FLAG_OBF) + return 1; + case ACPI_EC_EVENT_IBE: + if (!(status & ACPI_EC_FLAG_IBF)) + return 1; + default: + break; + } + + return 0; +} + static int acpi_ec_wait(union acpi_ec *ec, u8 event) { if (acpi_ec_poll_mode) @@ -203,47 +219,28 @@ static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event) return -ETIME; } -static int acpi_ec_intr_wait(union acpi_ec *ec, unsigned int event) -{ - int result = 0; +static int acpi_ec_intr_wait(union acpi_ec *ec, u8 event) +{ + long time_left; ec->intr.expect_event = event; - smp_mb(); - switch (event) { - case ACPI_EC_EVENT_IBE: - if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) { + if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { ec->intr.expect_event = 0; return 0; - } - break; - default: - break; } - result = wait_event_timeout(ec->intr.wait, - !ec->intr.expect_event, - msecs_to_jiffies(ACPI_EC_DELAY)); + time_left = wait_event_timeout(ec->intr.wait, !ec->intr.expect_event, + msecs_to_jiffies(ACPI_EC_DELAY)); ec->intr.expect_event = 0; - smp_mb(); - - /* - * Verify that the event in question has actually happened by - * querying EC status. Do the check even if operation timed-out - * to make sure that we did not miss interrupt. - */ - switch (event) { - case ACPI_EC_EVENT_OBF: - if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_OBF) - return 0; - break; - - case ACPI_EC_EVENT_IBE: - if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) - return 0; - break; + if (time_left <= 0) { + if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { + return 0; + } + } else { + return 0; } return -ETIME; @@ -293,7 +290,7 @@ int acpi_ec_leave_burst_mode(union acpi_ec *ec) goto end; acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->common.command_addr); acpi_ec_wait(ec, ACPI_EC_FLAG_IBF); - } + } atomic_set(&ec->intr.leaving_burst, 1); return 0; end: @@ -333,32 +330,32 @@ static int acpi_ec_transaction_unlocked(union acpi_ec *ec, u8 command, acpi_hw_low_level_write(8, command, &ec->common.command_addr); - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (result) - return result; + for (; wdata_len > 0; wdata_len --) { + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + return result; - for (; wdata_len > 0; wdata_len --) { - - acpi_hw_low_level_write(8, *(wdata++), &ec->common.data_addr); - - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - if (result) - return result; + acpi_hw_low_level_write(8, *(wdata++), &ec->common.data_addr); } + if (command == ACPI_EC_COMMAND_WRITE) { + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + return result; + } - for (; rdata_len > 0; rdata_len --) { - u32 d; + for (; rdata_len > 0; rdata_len --) { + u32 d; - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); - if (result) - return result; + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (result) + return result; - acpi_hw_low_level_read(8, &d, &ec->common.data_addr); - *(rdata++) = (u8) d; - } + acpi_hw_low_level_read(8, &d, &ec->common.data_addr); + *(rdata++) = (u8) d; + } - return 0; + return 0; } static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, From 703959d47e887a29dc58123c05aa0ffcbbfa131d Mon Sep 17 00:00:00 2001 From: "Denis M. Sadykov" Date: Tue, 26 Sep 2006 19:50:33 +0400 Subject: [PATCH 10/28] ACPI: EC: Remove unused variables and duplicated code Signed-off-by: Alexey Y. Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 723 +++++++++++++++++----------------------------- 1 file changed, 259 insertions(+), 464 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b6f935d0c3a..c816b4eab50 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -45,131 +45,125 @@ ACPI_MODULE_NAME("acpi_ec") #define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver" #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" + +/* EC status register */ #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ #define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ -#define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */ -#define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */ -#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */ -#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ -#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ -#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ + +/* EC commands */ #define ACPI_EC_COMMAND_READ 0x80 #define ACPI_EC_COMMAND_WRITE 0x81 #define ACPI_EC_BURST_ENABLE 0x82 #define ACPI_EC_BURST_DISABLE 0x83 #define ACPI_EC_COMMAND_QUERY 0x84 -#define EC_POLL 0xFF -#define EC_INTR 0x00 + +/* EC events */ +enum { + ACPI_EC_EVENT_OBF_1 = 1, /* Output buffer full */ + ACPI_EC_EVENT_IBF_0, /* Input buffer empty */ +}; + +#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */ +#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ +#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ +#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ + +enum { + EC_INTR = 1, /* Output buffer full */ + EC_POLL, /* Input buffer empty */ +}; + static int acpi_ec_remove(struct acpi_device *device, int type); static int acpi_ec_start(struct acpi_device *device); static int acpi_ec_stop(struct acpi_device *device, int type); -static int acpi_ec_intr_add(struct acpi_device *device); -static int acpi_ec_poll_add(struct acpi_device *device); +static int acpi_ec_add(struct acpi_device *device); static struct acpi_driver acpi_ec_driver = { .name = ACPI_EC_DRIVER_NAME, .class = ACPI_EC_CLASS, .ids = ACPI_EC_HID, .ops = { - .add = acpi_ec_intr_add, + .add = acpi_ec_add, .remove = acpi_ec_remove, .start = acpi_ec_start, .stop = acpi_ec_stop, }, }; -union acpi_ec { - struct { - u32 mode; - acpi_handle handle; - unsigned long uid; - unsigned long gpe_bit; - struct acpi_generic_address status_addr; - struct acpi_generic_address command_addr; - struct acpi_generic_address data_addr; - unsigned long global_lock; - } common; - - struct { - u32 mode; - acpi_handle handle; - unsigned long uid; - unsigned long gpe_bit; - struct acpi_generic_address status_addr; - struct acpi_generic_address command_addr; - struct acpi_generic_address data_addr; - unsigned long global_lock; - u8 expect_event; - atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ - atomic_t pending_gpe; - struct semaphore sem; - wait_queue_head_t wait; - } intr; - - struct { - u32 mode; - acpi_handle handle; - unsigned long uid; - unsigned long gpe_bit; - struct acpi_generic_address status_addr; - struct acpi_generic_address command_addr; - struct acpi_generic_address data_addr; - unsigned long global_lock; - struct semaphore sem; - } poll; +struct acpi_ec { + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + struct semaphore sem; + unsigned int expect_event; + atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ + wait_queue_head_t wait; }; -static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event); -static int acpi_ec_intr_wait(union acpi_ec *ec, u8 event); -static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, +/* If we find an EC via the ECDT, we need to keep a ptr to its context */ +static struct acpi_ec *ec_ecdt; + +/* External interfaces use first EC only, so remember */ +static struct acpi_device *first_ec; +static int acpi_ec_mode = EC_INTR; + +static int acpi_ec_poll_transaction(struct acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len); -static int acpi_ec_intr_transaction(union acpi_ec *ec, u8 command, +static int acpi_ec_intr_transaction(struct acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len); static void acpi_ec_gpe_poll_query(void *ec_cxt); static void acpi_ec_gpe_intr_query(void *ec_cxt); static u32 acpi_ec_gpe_poll_handler(void *data); static u32 acpi_ec_gpe_intr_handler(void *data); -static acpi_status __init -acpi_fake_ecdt_poll_callback(acpi_handle handle, - u32 Level, void *context, void **retval); - -static acpi_status __init -acpi_fake_ecdt_intr_callback(acpi_handle handle, - u32 Level, void *context, void **retval); - -static int __init acpi_ec_poll_get_real_ecdt(void); -static int __init acpi_ec_intr_get_real_ecdt(void); -/* If we find an EC via the ECDT, we need to keep a ptr to its context */ -static union acpi_ec *ec_ecdt; - -/* External interfaces use first EC only, so remember */ -static struct acpi_device *first_ec; -static int acpi_ec_poll_mode = EC_INTR; /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ -static u32 acpi_ec_read_status(union acpi_ec *ec) +static u32 acpi_ec_read_status(struct acpi_ec *ec) { u32 status = 0; - acpi_hw_low_level_read(8, &status, &ec->common.status_addr); + acpi_hw_low_level_read(8, &status, &ec->status_addr); return status; } +static u32 acpi_ec_read_data(struct acpi_ec *ec) +{ + u32 data = 0; + + acpi_hw_low_level_read(8, &data, &ec->data_addr); + return data; +} + +static void acpi_ec_write_cmd(struct acpi_ec *ec, u32 command) +{ + acpi_hw_low_level_write(8, command, &ec->command_addr); +} + +static void acpi_ec_write_data(struct acpi_ec *ec, u32 data) +{ + acpi_hw_low_level_write(8, data, &ec->data_addr); +} + static int acpi_ec_check_status(u32 status, u8 event) { switch (event) { - case ACPI_EC_EVENT_OBF: + case ACPI_EC_EVENT_OBF_1: if (status & ACPI_EC_FLAG_OBF) return 1; - case ACPI_EC_EVENT_IBE: + break; + case ACPI_EC_EVENT_IBF_0: if (!(status & ACPI_EC_FLAG_IBF)) return 1; + break; default: break; } @@ -177,72 +171,37 @@ static int acpi_ec_check_status(u32 status, u8 event) { return 0; } -static int acpi_ec_wait(union acpi_ec *ec, u8 event) +static int acpi_ec_wait(struct acpi_ec *ec, u8 event) { - if (acpi_ec_poll_mode) - return acpi_ec_poll_wait(ec, event); - else - return acpi_ec_intr_wait(ec, event); -} - -static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event) -{ - u32 acpi_ec_status = 0; - u32 i = ACPI_EC_UDELAY_COUNT; - - if (!ec) - return -EINVAL; - - /* Poll the EC status register waiting for the event to occur. */ - switch (event) { - case ACPI_EC_EVENT_OBF: - do { - acpi_hw_low_level_read(8, &acpi_ec_status, - &ec->common.status_addr); - if (acpi_ec_status & ACPI_EC_FLAG_OBF) - return 0; - udelay(ACPI_EC_UDELAY); - } while (--i > 0); - break; - case ACPI_EC_EVENT_IBE: - do { - acpi_hw_low_level_read(8, &acpi_ec_status, - &ec->common.status_addr); - if (!(acpi_ec_status & ACPI_EC_FLAG_IBF)) - return 0; - udelay(ACPI_EC_UDELAY); - } while (--i > 0); - break; - default: - return -EINVAL; - } - - return -ETIME; -} - -static int acpi_ec_intr_wait(union acpi_ec *ec, u8 event) -{ - long time_left; - - ec->intr.expect_event = event; + int i = (acpi_ec_mode == EC_POLL) ? ACPI_EC_UDELAY_COUNT : 0; + long time_left; + ec->expect_event = event; if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { - ec->intr.expect_event = 0; - return 0; - } - - time_left = wait_event_timeout(ec->intr.wait, !ec->intr.expect_event, - msecs_to_jiffies(ACPI_EC_DELAY)); - - ec->intr.expect_event = 0; - if (time_left <= 0) { - if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { - return 0; - } - } else { + ec->expect_event = 0; return 0; } + do { + if (acpi_ec_mode == EC_POLL) { + udelay(ACPI_EC_UDELAY); + } else { + time_left = wait_event_timeout(ec->wait, + !ec->expect_event, + msecs_to_jiffies(ACPI_EC_DELAY)); + if (time_left > 0) { + ec->expect_event = 0; + return 0; + } + } + if (acpi_ec_check_status(acpi_ec_read_status(ec), event)) { + ec->expect_event = 0; + return 0; + } + } while (--i > 0); + + ec->expect_event = 0; + return -ETIME; } @@ -251,64 +210,63 @@ static int acpi_ec_intr_wait(union acpi_ec *ec, u8 event) * Note: samsung nv5000 doesn't work with ec burst mode. * http://bugzilla.kernel.org/show_bug.cgi?id=4980 */ -int acpi_ec_enter_burst_mode(union acpi_ec *ec) +int acpi_ec_enter_burst_mode(struct acpi_ec *ec) { u32 tmp = 0; - int status = 0; + u32 status = 0; status = acpi_ec_read_status(ec); if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) goto end; - acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, - &ec->common.command_addr); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); - acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr); + acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); + tmp = acpi_ec_read_data(ec); if (tmp != 0x90) { /* Burst ACK byte */ return -EINVAL; } } - atomic_set(&ec->intr.leaving_burst, 0); + atomic_set(&ec->leaving_burst, 0); return 0; - end: - ACPI_EXCEPTION ((AE_INFO, status, "EC wait, burst mode"); + end: + ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); return -1; } -int acpi_ec_leave_burst_mode(union acpi_ec *ec) +int acpi_ec_leave_burst_mode(struct acpi_ec *ec) { - int status = 0; + u32 status = 0; status = acpi_ec_read_status(ec); if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)){ - status = acpi_ec_wait(ec, ACPI_EC_FLAG_IBF); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if(status) goto end; - acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->common.command_addr); - acpi_ec_wait(ec, ACPI_EC_FLAG_IBF); + acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); + acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); } - atomic_set(&ec->intr.leaving_burst, 1); + atomic_set(&ec->leaving_burst, 1); return 0; -end: - ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode"); + end: + ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); return -1; } #endif /* ACPI_FUTURE_USAGE */ -static int acpi_ec_transaction(union acpi_ec *ec, u8 command, +static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len) { - if (acpi_ec_poll_mode) + if (acpi_ec_mode == EC_POLL) return acpi_ec_poll_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); else return acpi_ec_intr_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); } -static int acpi_ec_read(union acpi_ec *ec, u8 address, u32 * data) +static int acpi_ec_read(struct acpi_ec *ec, u8 address, u32 * data) { int result; u8 d; @@ -316,30 +274,30 @@ static int acpi_ec_read(union acpi_ec *ec, u8 address, u32 * data) *data = d; return result; } -static int acpi_ec_write(union acpi_ec *ec, u8 address, u8 data) +static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) { u8 wdata[2] = { address, data }; return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, wdata, 2, NULL, 0); } -static int acpi_ec_transaction_unlocked(union acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) +static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { int result; - acpi_hw_low_level_write(8, command, &ec->common.command_addr); + acpi_ec_write_cmd(ec, command); for (; wdata_len > 0; wdata_len --) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) return result; - acpi_hw_low_level_write(8, *(wdata++), &ec->common.data_addr); + acpi_ec_write_data(ec, *(wdata++)); } if (command == ACPI_EC_COMMAND_WRITE) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) return result; } @@ -347,18 +305,18 @@ static int acpi_ec_transaction_unlocked(union acpi_ec *ec, u8 command, for (; rdata_len > 0; rdata_len --) { u32 d; - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); if (result) return result; - acpi_hw_low_level_read(8, &d, &ec->common.data_addr); + d = acpi_ec_read_data(ec); *(rdata++) = (u8) d; } return 0; } -static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, +static int acpi_ec_poll_transaction(struct acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len) { @@ -372,13 +330,13 @@ static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, if (rdata) memset(rdata, 0, rdata_len); - if (ec->common.global_lock) { + if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return -ENODEV; } - if (down_interruptible(&ec->poll.sem)) { + if (down_interruptible(&ec->sem)) { result = -ERESTARTSYS; goto end_nosem; } @@ -386,16 +344,16 @@ static int acpi_ec_poll_transaction(union acpi_ec *ec, u8 command, result = acpi_ec_transaction_unlocked(ec, command, wdata, wdata_len, rdata, rdata_len); - up(&ec->poll.sem); + up(&ec->sem); end_nosem: - if (ec->common.global_lock) + if (ec->global_lock) acpi_release_global_lock(glk); return result; } -static int acpi_ec_intr_transaction(union acpi_ec *ec, u8 command, +static int acpi_ec_intr_transaction(struct acpi_ec *ec, u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len) { @@ -408,18 +366,18 @@ static int acpi_ec_intr_transaction(union acpi_ec *ec, u8 command, if (rdata) memset(rdata, 0, rdata_len); - if (ec->common.global_lock) { + if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return -ENODEV; } WARN_ON(in_interrupt()); - down(&ec->intr.sem); + down(&ec->sem); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) { - printk(KERN_DEBUG PREFIX "read EC, IB not empty\n"); + ACPI_EXCEPTION((AE_INFO, status, "read EC, IB not empty")); goto end; } @@ -428,9 +386,9 @@ static int acpi_ec_intr_transaction(union acpi_ec *ec, u8 command, rdata, rdata_len); end: - up(&ec->intr.sem); + up(&ec->sem); - if (ec->common.global_lock) + if (ec->global_lock) acpi_release_global_lock(glk); return status; @@ -441,7 +399,7 @@ end: */ int ec_read(u8 addr, u8 * val) { - union acpi_ec *ec; + struct acpi_ec *ec; int err; u32 temp_data; @@ -463,7 +421,7 @@ EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) { - union acpi_ec *ec; + struct acpi_ec *ec; int err; if (!first_ec) @@ -482,7 +440,7 @@ extern int ec_transaction(u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len) { - union acpi_ec *ec; + struct acpi_ec *ec; if (!first_ec) return -ENODEV; @@ -494,7 +452,7 @@ extern int ec_transaction(u8 command, EXPORT_SYMBOL(ec_transaction); -static int acpi_ec_query(union acpi_ec *ec, u32 * data) { +static int acpi_ec_query(struct acpi_ec *ec, u32 * data) { int result; u8 d; @@ -529,7 +487,7 @@ union acpi_ec_query_data { static void acpi_ec_gpe_query(void *ec_cxt) { - if (acpi_ec_poll_mode) + if (acpi_ec_mode == EC_POLL) acpi_ec_gpe_poll_query(ec_cxt); else acpi_ec_gpe_intr_query(ec_cxt); @@ -537,7 +495,7 @@ static void acpi_ec_gpe_query(void *ec_cxt) static void acpi_ec_gpe_poll_query(void *ec_cxt) { - union acpi_ec *ec = (union acpi_ec *)ec_cxt; + struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; u32 value = 0; static char object_name[5] = { '_', 'Q', '0', '0', '\0' }; const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -548,11 +506,11 @@ static void acpi_ec_gpe_poll_query(void *ec_cxt) if (!ec_cxt) goto end; - if (down_interruptible (&ec->poll.sem)) { + if (down_interruptible (&ec->sem)) { return; } - acpi_hw_low_level_read(8, &value, &ec->common.command_addr); - up(&ec->poll.sem); + value = acpi_ec_read_status(ec); + up(&ec->sem); /* TBD: Implement asynch events! * NOTE: All we care about are EC-SCI's. Other EC events are @@ -571,14 +529,14 @@ static void acpi_ec_gpe_poll_query(void *ec_cxt) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); - acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL); + acpi_evaluate_object(ec->handle, object_name, NULL, NULL); end: - acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); } static void acpi_ec_gpe_intr_query(void *ec_cxt) { - union acpi_ec *ec = (union acpi_ec *)ec_cxt; + struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; u32 value; int result = -ENODATA; static char object_name[5] = { '_', 'Q', '0', '0', '\0' }; @@ -598,15 +556,14 @@ static void acpi_ec_gpe_intr_query(void *ec_cxt) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); - acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL); + acpi_evaluate_object(ec->handle, object_name, NULL, NULL); end: - atomic_dec(&ec->intr.pending_gpe); return; } static u32 acpi_ec_gpe_handler(void *data) { - if (acpi_ec_poll_mode) + if (acpi_ec_mode == EC_POLL) return acpi_ec_gpe_poll_handler(data); else return acpi_ec_gpe_intr_handler(data); @@ -614,12 +571,12 @@ static u32 acpi_ec_gpe_handler(void *data) static u32 acpi_ec_gpe_poll_handler(void *data) { acpi_status status = AE_OK; - union acpi_ec *ec = (union acpi_ec *)data; + struct acpi_ec *ec = (struct acpi_ec *)data; if (!ec) return ACPI_INTERRUPT_NOT_HANDLED; - acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); + acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); status = acpi_os_execute(OSL_EC_POLL_HANDLER, acpi_ec_gpe_query, ec); @@ -632,39 +589,38 @@ static u32 acpi_ec_gpe_intr_handler(void *data) { acpi_status status = AE_OK; u32 value; - union acpi_ec *ec = (union acpi_ec *)data; + struct acpi_ec *ec = (struct acpi_ec *)data; if (!ec) return ACPI_INTERRUPT_NOT_HANDLED; - acpi_clear_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); + acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR); value = acpi_ec_read_status(ec); - switch (ec->intr.expect_event) { - case ACPI_EC_EVENT_OBF: + switch (ec->expect_event) { + case ACPI_EC_EVENT_OBF_1: if (!(value & ACPI_EC_FLAG_OBF)) break; - ec->intr.expect_event = 0; - wake_up(&ec->intr.wait); + ec->expect_event = 0; + wake_up(&ec->wait); break; - case ACPI_EC_EVENT_IBE: + case ACPI_EC_EVENT_IBF_0: if ((value & ACPI_EC_FLAG_IBF)) break; - ec->intr.expect_event = 0; - wake_up(&ec->intr.wait); + ec->expect_event = 0; + wake_up(&ec->wait); break; default: break; } if (value & ACPI_EC_FLAG_SCI) { - atomic_add(1, &ec->intr.pending_gpe); status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } - acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); + acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_ISR); return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } @@ -695,7 +651,7 @@ acpi_ec_space_handler(u32 function, void *handler_context, void *region_context) { int result = 0; - union acpi_ec *ec = NULL; + struct acpi_ec *ec = NULL; u64 temp = *value; acpi_integer f_v = 0; int i = 0; @@ -705,12 +661,10 @@ acpi_ec_space_handler(u32 function, return AE_BAD_PARAMETER; if (bit_width != 8 && acpi_strict) { - printk(KERN_WARNING PREFIX - "acpi_ec_space_handler: bit_width should be 8\n"); return AE_BAD_PARAMETER; } - ec = (union acpi_ec *)handler_context; + ec = (struct acpi_ec *)handler_context; next_byte: switch (function) { @@ -767,20 +721,20 @@ static struct proc_dir_entry *acpi_ec_dir; static int acpi_ec_read_info(struct seq_file *seq, void *offset) { - union acpi_ec *ec = (union acpi_ec *)seq->private; + struct acpi_ec *ec = (struct acpi_ec *)seq->private; if (!ec) goto end; seq_printf(seq, "gpe bit: 0x%02x\n", - (u32) ec->common.gpe_bit); + (u32) ec->gpe_bit); seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->common.status_addr.address, - (u32) ec->common.data_addr.address); + (u32) ec->status_addr.address, + (u32) ec->data_addr.address); seq_printf(seq, "use global lock: %s\n", - ec->common.global_lock ? "yes" : "no"); - acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + ec->global_lock ? "yes" : "no"); + acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); end: return 0; @@ -791,7 +745,7 @@ static int acpi_ec_info_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_ec_read_info, PDE(inode)->data); } -static const struct file_operations acpi_ec_info_ops = { +static struct file_operations acpi_ec_info_ops = { .open = acpi_ec_info_open_fs, .read = seq_read, .llseek = seq_lseek, @@ -840,31 +794,35 @@ static int acpi_ec_remove_fs(struct acpi_device *device) Driver Interface -------------------------------------------------------------------------- */ -static int acpi_ec_poll_add(struct acpi_device *device) +static int acpi_ec_add(struct acpi_device *device) { int result = 0; acpi_status status = AE_OK; - union acpi_ec *ec = NULL; + struct acpi_ec *ec = NULL; if (!device) return -EINVAL; - ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); + ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); if (!ec) return -ENOMEM; - memset(ec, 0, sizeof(union acpi_ec)); + memset(ec, 0, sizeof(struct acpi_ec)); - ec->common.handle = device->handle; - ec->common.uid = -1; - init_MUTEX(&ec->poll.sem); + ec->handle = device->handle; + ec->uid = -1; + init_MUTEX(&ec->sem); + if (acpi_ec_mode == EC_INTR) { + atomic_set(&ec->leaving_burst, 1); + init_waitqueue_head(&ec->wait); + } strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); acpi_driver_data(device) = ec; /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, - &ec->common.global_lock); + acpi_evaluate_integer(ec->handle, "_GLK", NULL, + &ec->global_lock); /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ @@ -873,7 +831,7 @@ static int acpi_ec_poll_add(struct acpi_device *device) ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); - acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, + acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); kfree(ec_ecdt); @@ -882,10 +840,10 @@ static int acpi_ec_poll_add(struct acpi_device *device) /* Get GPE bit assignment (EC events). */ /* TODO: Add support for _GPE returning a package */ status = - acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, - &ec->common.gpe_bit); + acpi_evaluate_integer(ec->handle, "_GPE", NULL, + &ec->gpe_bit); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit")); + ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit assignment")); result = -ENODEV; goto end; } @@ -894,84 +852,14 @@ static int acpi_ec_poll_add(struct acpi_device *device) if (result) goto end; - printk(KERN_INFO PREFIX "%s [%s] (gpe %d) polling mode.\n", + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", acpi_device_name(device), acpi_device_bid(device), - (u32) ec->common.gpe_bit); + (u32) ec->gpe_bit)); if (!first_ec) first_ec = device; - end: - if (result) - kfree(ec); - - return result; -} -static int acpi_ec_intr_add(struct acpi_device *device) -{ - int result = 0; - acpi_status status = AE_OK; - union acpi_ec *ec = NULL; - - - if (!device) - return -EINVAL; - - ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); - if (!ec) - return -ENOMEM; - memset(ec, 0, sizeof(union acpi_ec)); - - ec->common.handle = device->handle; - ec->common.uid = -1; - atomic_set(&ec->intr.pending_gpe, 0); - atomic_set(&ec->intr.leaving_burst, 1); - init_MUTEX(&ec->intr.sem); - init_waitqueue_head(&ec->intr.wait); - strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_EC_CLASS); - acpi_driver_data(device) = ec; - - /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, - &ec->common.global_lock); - - /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: - http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ - if (ec_ecdt) { - acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler); - - acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, - &acpi_ec_gpe_handler); - - kfree(ec_ecdt); - } - - /* Get GPE bit assignment (EC events). */ - /* TODO: Add support for _GPE returning a package */ - status = - acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, - &ec->common.gpe_bit); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Obtaining GPE bit assignment\n"); - result = -ENODEV; - goto end; - } - - result = acpi_ec_add_fs(device); - if (result) - goto end; - - printk(KERN_INFO PREFIX "%s [%s] (gpe %d) interrupt mode.\n", - acpi_device_name(device), acpi_device_bid(device), - (u32) ec->common.gpe_bit); - - if (!first_ec) - first_ec = device; - - end: + end: if (result) kfree(ec); @@ -980,7 +868,7 @@ static int acpi_ec_intr_add(struct acpi_device *device) static int acpi_ec_remove(struct acpi_device *device, int type) { - union acpi_ec *ec = NULL; + struct acpi_ec *ec = NULL; if (!device) @@ -998,7 +886,7 @@ static int acpi_ec_remove(struct acpi_device *device, int type) static acpi_status acpi_ec_io_ports(struct acpi_resource *resource, void *context) { - union acpi_ec *ec = (union acpi_ec *)context; + struct acpi_ec *ec = (struct acpi_ec *)context; struct acpi_generic_address *addr; if (resource->type != ACPI_RESOURCE_TYPE_IO) { @@ -1010,10 +898,10 @@ acpi_ec_io_ports(struct acpi_resource *resource, void *context) * the second address region returned is the status/command * port. */ - if (ec->common.data_addr.register_bit_width == 0) { - addr = &ec->common.data_addr; - } else if (ec->common.command_addr.register_bit_width == 0) { - addr = &ec->common.command_addr; + if (ec->data_addr.register_bit_width == 0) { + addr = &ec->data_addr; + } else if (ec->command_addr.register_bit_width == 0) { + addr = &ec->command_addr; } else { return AE_CTRL_TERMINATE; } @@ -1029,7 +917,7 @@ acpi_ec_io_ports(struct acpi_resource *resource, void *context) static int acpi_ec_start(struct acpi_device *device) { acpi_status status = AE_OK; - union acpi_ec *ec = NULL; + struct acpi_ec *ec = NULL; if (!device) @@ -1043,39 +931,40 @@ static int acpi_ec_start(struct acpi_device *device) /* * Get I/O port addresses. Convert to GAS format. */ - status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS, + status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec); if (ACPI_FAILURE(status) - || ec->common.command_addr.register_bit_width == 0) { - printk(KERN_ERR PREFIX "Error getting I/O port addresses\n"); + || ec->command_addr.register_bit_width == 0) { + ACPI_EXCEPTION((AE_INFO, status, + "Error getting I/O port addresses")); return -ENODEV; } - ec->common.status_addr = ec->common.command_addr; + ec->status_addr = ec->command_addr; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n", - (u32) ec->common.gpe_bit, - (u32) ec->common.command_addr.address, - (u32) ec->common.data_addr.address)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x", + (u32) ec->gpe_bit, + (u32) ec->command_addr.address, + (u32) ec->data_addr.address)); /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit, + status = acpi_install_gpe_handler(NULL, ec->gpe_bit, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); if (ACPI_FAILURE(status)) { return -ENODEV; } - acpi_set_gpe_type(NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type(NULL, ec->gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); - status = acpi_install_address_space_handler(ec->common.handle, + status = acpi_install_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, &acpi_ec_space_setup, ec); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, + acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); return -ENODEV; } @@ -1086,7 +975,7 @@ static int acpi_ec_start(struct acpi_device *device) static int acpi_ec_stop(struct acpi_device *device, int type) { acpi_status status = AE_OK; - union acpi_ec *ec = NULL; + struct acpi_ec *ec = NULL; if (!device) @@ -1094,14 +983,14 @@ static int acpi_ec_stop(struct acpi_device *device, int type) ec = acpi_driver_data(device); - status = acpi_remove_address_space_handler(ec->common.handle, + status = acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); if (ACPI_FAILURE(status)) return -ENODEV; status = - acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, + acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); if (ACPI_FAILURE(status)) return -ENODEV; @@ -1113,76 +1002,33 @@ static acpi_status __init acpi_fake_ecdt_callback(acpi_handle handle, u32 Level, void *context, void **retval) { - - if (acpi_ec_poll_mode) - return acpi_fake_ecdt_poll_callback(handle, - Level, context, retval); - else - return acpi_fake_ecdt_intr_callback(handle, - Level, context, retval); -} - -static acpi_status __init -acpi_fake_ecdt_poll_callback(acpi_handle handle, - u32 Level, void *context, void **retval) -{ acpi_status status; + init_MUTEX(&ec_ecdt->sem); + if (acpi_ec_mode == EC_INTR) { + init_waitqueue_head(&ec_ecdt->wait); + } status = acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec_ecdt); if (ACPI_FAILURE(status)) return status; - ec_ecdt->common.status_addr = ec_ecdt->common.command_addr; + ec_ecdt->status_addr = ec_ecdt->command_addr; - ec_ecdt->common.uid = -1; - acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid); + ec_ecdt->uid = -1; + acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); status = acpi_evaluate_integer(handle, "_GPE", NULL, - &ec_ecdt->common.gpe_bit); + &ec_ecdt->gpe_bit); if (ACPI_FAILURE(status)) return status; - init_MUTEX(&ec_ecdt->poll.sem); - ec_ecdt->common.global_lock = TRUE; - ec_ecdt->common.handle = handle; + ec_ecdt->global_lock = TRUE; + ec_ecdt->handle = handle; - printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", - (u32) ec_ecdt->common.gpe_bit, - (u32) ec_ecdt->common.command_addr.address, - (u32) ec_ecdt->common.data_addr.address); - - return AE_CTRL_TERMINATE; -} - -static acpi_status __init -acpi_fake_ecdt_intr_callback(acpi_handle handle, - u32 Level, void *context, void **retval) -{ - acpi_status status; - - init_MUTEX(&ec_ecdt->intr.sem); - init_waitqueue_head(&ec_ecdt->intr.wait); - status = acpi_walk_resources(handle, METHOD_NAME__CRS, - acpi_ec_io_ports, ec_ecdt); - if (ACPI_FAILURE(status)) - return status; - ec_ecdt->common.status_addr = ec_ecdt->common.command_addr; - - ec_ecdt->common.uid = -1; - acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid); - - status = - acpi_evaluate_integer(handle, "_GPE", NULL, - &ec_ecdt->common.gpe_bit); - if (ACPI_FAILURE(status)) - return status; - ec_ecdt->common.global_lock = TRUE; - ec_ecdt->common.handle = handle; - - printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", - (u32) ec_ecdt->common.gpe_bit, - (u32) ec_ecdt->common.command_addr.address, - (u32) ec_ecdt->common.data_addr.address); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02x, ports=0x%2x, 0x%2x", + (u32) ec_ecdt->gpe_bit, + (u32) ec_ecdt->command_addr.address, + (u32) ec_ecdt->data_addr.address)); return AE_CTRL_TERMINATE; } @@ -1202,14 +1048,14 @@ static int __init acpi_ec_fake_ecdt(void) acpi_status status; int ret = 0; - printk(KERN_INFO PREFIX "Try to make an fake ECDT\n"); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT")); - ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); + ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); if (!ec_ecdt) { ret = -ENOMEM; goto error; } - memset(ec_ecdt, 0, sizeof(union acpi_ec)); + memset(ec_ecdt, 0, sizeof(struct acpi_ec)); status = acpi_get_devices(ACPI_EC_HID, acpi_fake_ecdt_callback, NULL, NULL); @@ -1217,23 +1063,15 @@ static int __init acpi_ec_fake_ecdt(void) kfree(ec_ecdt); ec_ecdt = NULL; ret = -ENODEV; + ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT")); goto error; } return 0; - error: - printk(KERN_ERR PREFIX "Can't make an fake ECDT\n"); + error: return ret; } static int __init acpi_ec_get_real_ecdt(void) -{ - if (acpi_ec_poll_mode) - return acpi_ec_poll_get_real_ecdt(); - else - return acpi_ec_intr_get_real_ecdt(); -} - -static int __init acpi_ec_poll_get_real_ecdt(void) { acpi_status status; struct acpi_table_ecdt *ecdt_ptr; @@ -1244,80 +1082,37 @@ static int __init acpi_ec_poll_get_real_ecdt(void) if (ACPI_FAILURE(status)) return -ENODEV; - printk(KERN_INFO PREFIX "Found ECDT\n"); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); /* * Generate a temporary ec context to use until the namespace is scanned */ - ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); + ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); if (!ec_ecdt) return -ENOMEM; - memset(ec_ecdt, 0, sizeof(union acpi_ec)); + memset(ec_ecdt, 0, sizeof(struct acpi_ec)); - ec_ecdt->common.command_addr = ecdt_ptr->ec_control; - ec_ecdt->common.status_addr = ecdt_ptr->ec_control; - ec_ecdt->common.data_addr = ecdt_ptr->ec_data; - ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit; - init_MUTEX(&ec_ecdt->poll.sem); + init_MUTEX(&ec_ecdt->sem); + if (acpi_ec_mode == EC_INTR) { + init_waitqueue_head(&ec_ecdt->wait); + } + ec_ecdt->command_addr = ecdt_ptr->ec_control; + ec_ecdt->status_addr = ecdt_ptr->ec_control; + ec_ecdt->data_addr = ecdt_ptr->ec_data; + ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; /* use the GL just to be safe */ - ec_ecdt->common.global_lock = TRUE; - ec_ecdt->common.uid = ecdt_ptr->uid; + ec_ecdt->global_lock = TRUE; + ec_ecdt->uid = ecdt_ptr->uid; status = - acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle); + acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); if (ACPI_FAILURE(status)) { goto error; } return 0; - error: - printk(KERN_ERR PREFIX "Could not use ECDT\n"); - kfree(ec_ecdt); - ec_ecdt = NULL; - - return -ENODEV; -} - -static int __init acpi_ec_intr_get_real_ecdt(void) -{ - acpi_status status; - struct acpi_table_ecdt *ecdt_ptr; - - status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, - (struct acpi_table_header **) - &ecdt_ptr); - if (ACPI_FAILURE(status)) - return -ENODEV; - - printk(KERN_INFO PREFIX "Found ECDT\n"); - - /* - * Generate a temporary ec context to use until the namespace is scanned - */ - ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); - if (!ec_ecdt) - return -ENOMEM; - memset(ec_ecdt, 0, sizeof(union acpi_ec)); - - init_MUTEX(&ec_ecdt->intr.sem); - init_waitqueue_head(&ec_ecdt->intr.wait); - ec_ecdt->common.command_addr = ecdt_ptr->ec_control; - ec_ecdt->common.status_addr = ecdt_ptr->ec_control; - ec_ecdt->common.data_addr = ecdt_ptr->ec_data; - ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit; - /* use the GL just to be safe */ - ec_ecdt->common.global_lock = TRUE; - ec_ecdt->common.uid = ecdt_ptr->uid; - - status = - acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle); - if (ACPI_FAILURE(status)) { - goto error; - } - - return 0; - error: - printk(KERN_ERR PREFIX "Could not use ECDT\n"); + error: + ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); kfree(ec_ecdt); ec_ecdt = NULL; @@ -1342,14 +1137,14 @@ int __init acpi_ec_ecdt_probe(void) /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit, + status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec_ecdt); if (ACPI_FAILURE(status)) { goto error; } - acpi_set_gpe_type(NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec_ecdt->common.gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type(NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe(NULL, ec_ecdt->gpe_bit, ACPI_NOT_ISR); status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_EC, @@ -1357,7 +1152,7 @@ int __init acpi_ec_ecdt_probe(void) &acpi_ec_space_setup, ec_ecdt); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, + acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); goto error; } @@ -1365,7 +1160,7 @@ int __init acpi_ec_ecdt_probe(void) return 0; error: - printk(KERN_ERR PREFIX "Could not use ECDT\n"); + ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); kfree(ec_ecdt); ec_ecdt = NULL; @@ -1424,13 +1219,13 @@ static int __init acpi_ec_set_intr_mode(char *str) return 0; if (intr) { - acpi_ec_poll_mode = EC_INTR; - acpi_ec_driver.ops.add = acpi_ec_intr_add; + acpi_ec_mode = EC_INTR; } else { - acpi_ec_poll_mode = EC_POLL; - acpi_ec_driver.ops.add = acpi_ec_poll_add; + acpi_ec_mode = EC_POLL; } - printk(KERN_INFO PREFIX "EC %s mode.\n", intr ? "interrupt" : "polling"); + acpi_ec_driver.ops.add = acpi_ec_add; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "EC %s mode.\n", intr ? "interrupt" : "polling")); + return 1; } From 3576cf619b73d850f5b21375609645f221e6270f Mon Sep 17 00:00:00 2001 From: "Denis M. Sadykov" Date: Tue, 26 Sep 2006 19:50:33 +0400 Subject: [PATCH 11/28] ACPI: EC: Unify poll and interrupt mode transaction functions Signed-off-by: Alexey Y. Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 110 ++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 81 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index c816b4eab50..9c7fce6a42e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -112,12 +112,6 @@ static struct acpi_ec *ec_ecdt; static struct acpi_device *first_ec; static int acpi_ec_mode = EC_INTR; -static int acpi_ec_poll_transaction(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len); -static int acpi_ec_intr_transaction(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len); static void acpi_ec_gpe_poll_query(void *ec_cxt); static void acpi_ec_gpe_intr_query(void *ec_cxt); static u32 acpi_ec_gpe_poll_handler(void *data); @@ -257,32 +251,9 @@ int acpi_ec_leave_burst_mode(struct acpi_ec *ec) } #endif /* ACPI_FUTURE_USAGE */ -static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) -{ - if (acpi_ec_mode == EC_POLL) - return acpi_ec_poll_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); - else - return acpi_ec_intr_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); -} -static int acpi_ec_read(struct acpi_ec *ec, u8 address, u32 * data) -{ - int result; - u8 d; - result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, &address, 1, &d, 1); - *data = d; - return result; -} -static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) -{ - u8 wdata[2] = { address, data }; - return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, wdata, 2, NULL, 0); -} - static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { int result; @@ -292,9 +263,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (result) return result; - acpi_ec_write_data(ec, *(wdata++)); - } + } if (command == ACPI_EC_COMMAND_WRITE) { result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); @@ -316,46 +286,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, return 0; } -static int acpi_ec_poll_transaction(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) -{ - acpi_status status = AE_OK; - int result; - u32 glk = 0; - - if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata)) - return -EINVAL; - - if (rdata) - memset(rdata, 0, rdata_len); - - if (ec->global_lock) { - status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); - if (ACPI_FAILURE(status)) - return -ENODEV; - } - - if (down_interruptible(&ec->sem)) { - result = -ERESTARTSYS; - goto end_nosem; - } - - result = acpi_ec_transaction_unlocked(ec, command, - wdata, wdata_len, - rdata, rdata_len); - up(&ec->sem); - -end_nosem: - if (ec->global_lock) - acpi_release_global_lock(glk); - - return result; -} - -static int acpi_ec_intr_transaction(struct acpi_ec *ec, u8 command, - const u8 *wdata, unsigned wdata_len, - u8 *rdata, unsigned rdata_len) +static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) { int status; u32 glk; @@ -371,13 +304,11 @@ static int acpi_ec_intr_transaction(struct acpi_ec *ec, u8 command, if (ACPI_FAILURE(status)) return -ENODEV; } - - WARN_ON(in_interrupt()); down(&ec->sem); status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) { - ACPI_EXCEPTION((AE_INFO, status, "read EC, IB not empty")); + printk(KERN_DEBUG PREFIX "read EC, IB not empty\n"); goto end; } @@ -394,6 +325,23 @@ end: return status; } +static int acpi_ec_read(struct acpi_ec *ec, u8 address, u32 * data) +{ + int result; + u8 d; + + result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, + &address, 1, &d, 1); + *data = d; + return result; +} +static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) +{ + u8 wdata[2] = { address, data }; + return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, + wdata, 2, NULL, 0); +} + /* * Externally callable EC access functions. For now, assume 1 EC only */ @@ -447,13 +395,13 @@ extern int ec_transaction(u8 command, ec = acpi_driver_data(first_ec); - return acpi_ec_transaction(ec, command, wdata, wdata_len, rdata, rdata_len); + return acpi_ec_transaction(ec, command, wdata, + wdata_len, rdata, rdata_len); } -EXPORT_SYMBOL(ec_transaction); - -static int acpi_ec_query(struct acpi_ec *ec, u32 * data) { - int result; +static int acpi_ec_query(struct acpi_ec *ec, u32 * data) +{ + int result; u8 d; if (!ec || !data) From 8e0341ba791cc72c643340b0d8119141ae5a80c5 Mon Sep 17 00:00:00 2001 From: "Denis M. Sadykov" Date: Tue, 26 Sep 2006 19:50:33 +0400 Subject: [PATCH 12/28] ACPI: EC: Unify poll and interrupt gpe handlers Signed-off-by: Alexey Y. Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 107 ++++++---------------------------------------- 1 file changed, 12 insertions(+), 95 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 9c7fce6a42e..0f232e719da 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -112,11 +112,6 @@ static struct acpi_ec *ec_ecdt; static struct acpi_device *first_ec; static int acpi_ec_mode = EC_INTR; -static void acpi_ec_gpe_poll_query(void *ec_cxt); -static void acpi_ec_gpe_intr_query(void *ec_cxt); -static u32 acpi_ec_gpe_poll_handler(void *data); -static u32 acpi_ec_gpe_intr_handler(void *data); - /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ @@ -428,20 +423,12 @@ static int acpi_ec_query(struct acpi_ec *ec, u32 * data) Event Management -------------------------------------------------------------------------- */ -union acpi_ec_query_data { +struct acpi_ec_query_data { acpi_handle handle; u8 data; }; static void acpi_ec_gpe_query(void *ec_cxt) -{ - if (acpi_ec_mode == EC_POLL) - acpi_ec_gpe_poll_query(ec_cxt); - else - acpi_ec_gpe_intr_query(ec_cxt); -} - -static void acpi_ec_gpe_poll_query(void *ec_cxt) { struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; u32 value = 0; @@ -454,18 +441,8 @@ static void acpi_ec_gpe_poll_query(void *ec_cxt) if (!ec_cxt) goto end; - if (down_interruptible (&ec->sem)) { - return; - } value = acpi_ec_read_status(ec); - up(&ec->sem); - /* TBD: Implement asynch events! - * NOTE: All we care about are EC-SCI's. Other EC events are - * handled via polling (yuck!). This is because some systems - * treat EC-SCIs as level (versus EDGE!) triggered, preventing - * a purely interrupt-driven approach (grumble, grumble). - */ if (!(value & ACPI_EC_FLAG_SCI)) goto end; @@ -475,96 +452,36 @@ static void acpi_ec_gpe_poll_query(void *ec_cxt) object_name[2] = hex[((value >> 4) & 0x0F)]; object_name[3] = hex[(value & 0x0F)]; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name)); acpi_evaluate_object(ec->handle, object_name, NULL, NULL); end: acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); } -static void acpi_ec_gpe_intr_query(void *ec_cxt) -{ - struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; - u32 value; - int result = -ENODATA; - static char object_name[5] = { '_', 'Q', '0', '0', '\0' }; - const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; - - - if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI) - result = acpi_ec_query(ec, &value); - - if (result) - goto end; - - object_name[2] = hex[((value >> 4) & 0x0F)]; - object_name[3] = hex[(value & 0x0F)]; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); - - acpi_evaluate_object(ec->handle, object_name, NULL, NULL); - end: - return; -} static u32 acpi_ec_gpe_handler(void *data) -{ - if (acpi_ec_mode == EC_POLL) - return acpi_ec_gpe_poll_handler(data); - else - return acpi_ec_gpe_intr_handler(data); -} -static u32 acpi_ec_gpe_poll_handler(void *data) -{ - acpi_status status = AE_OK; - struct acpi_ec *ec = (struct acpi_ec *)data; - - if (!ec) - return ACPI_INTERRUPT_NOT_HANDLED; - - acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); - - status = acpi_os_execute(OSL_EC_POLL_HANDLER, acpi_ec_gpe_query, ec); - - if (status == AE_OK) - return ACPI_INTERRUPT_HANDLED; - else - return ACPI_INTERRUPT_NOT_HANDLED; -} -static u32 acpi_ec_gpe_intr_handler(void *data) { acpi_status status = AE_OK; u32 value; + u8 exec_mode; struct acpi_ec *ec = (struct acpi_ec *)data; - if (!ec) - return ACPI_INTERRUPT_NOT_HANDLED; - acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR); value = acpi_ec_read_status(ec); - switch (ec->expect_event) { - case ACPI_EC_EVENT_OBF_1: - if (!(value & ACPI_EC_FLAG_OBF)) - break; - ec->expect_event = 0; - wake_up(&ec->wait); - break; - case ACPI_EC_EVENT_IBF_0: - if ((value & ACPI_EC_FLAG_IBF)) - break; - ec->expect_event = 0; - wake_up(&ec->wait); - break; - default: - break; + if (acpi_ec_mode == EC_INTR) { + if (acpi_ec_check_status(value, ec->expect_event)) { + ec->expect_event = 0; + wake_up(&ec->wait); + } + exec_mode = OSL_EC_BURST_HANDLER; + } else { + exec_mode = OSL_EC_POLL_HANDLER; } if (value & ACPI_EC_FLAG_SCI) { - status = acpi_os_execute(OSL_EC_BURST_HANDLER, - acpi_ec_gpe_query, ec); + status = acpi_os_execute(exec_mode, acpi_ec_gpe_query, ec); return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } From 6ffb221a82de962f31034b45d945e203a0f0500f Mon Sep 17 00:00:00 2001 From: "Denis M. Sadykov" Date: Tue, 26 Sep 2006 19:50:33 +0400 Subject: [PATCH 13/28] ACPI: EC: Simplify acpi_hw_low_level*() with inb()/outb(). Simplify acpi_hw_low_level_xxx() functions to inb() and outb(). Signed-off-by: Alexey Y. Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/ec.c | 124 +++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 79 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 0f232e719da..ae05e8c1148 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -72,7 +72,7 @@ enum { enum { EC_INTR = 1, /* Output buffer full */ - EC_POLL, /* Input buffer empty */ + EC_POLL, /* Input buffer empty */ }; static int acpi_ec_remove(struct acpi_device *device, int type); @@ -91,22 +91,20 @@ static struct acpi_driver acpi_ec_driver = { .stop = acpi_ec_stop, }, }; + +/* If we find an EC via the ECDT, we need to keep a ptr to its context */ struct acpi_ec { acpi_handle handle; unsigned long uid; unsigned long gpe_bit; - struct acpi_generic_address status_addr; - struct acpi_generic_address command_addr; - struct acpi_generic_address data_addr; + unsigned long command_addr; + unsigned long data_addr; unsigned long global_lock; struct semaphore sem; unsigned int expect_event; atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ wait_queue_head_t wait; -}; - -/* If we find an EC via the ECDT, we need to keep a ptr to its context */ -static struct acpi_ec *ec_ecdt; +} *ec_ecdt; /* External interfaces use first EC only, so remember */ static struct acpi_device *first_ec; @@ -116,34 +114,28 @@ static int acpi_ec_mode = EC_INTR; Transaction Management -------------------------------------------------------------------------- */ -static u32 acpi_ec_read_status(struct acpi_ec *ec) +static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { - u32 status = 0; - - acpi_hw_low_level_read(8, &status, &ec->status_addr); - return status; + return inb(ec->command_addr); } -static u32 acpi_ec_read_data(struct acpi_ec *ec) +static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { - u32 data = 0; - - acpi_hw_low_level_read(8, &data, &ec->data_addr); - return data; + return inb(ec->data_addr); } -static void acpi_ec_write_cmd(struct acpi_ec *ec, u32 command) +static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { - acpi_hw_low_level_write(8, command, &ec->command_addr); + outb(command, ec->command_addr); } -static void acpi_ec_write_data(struct acpi_ec *ec, u32 data) +static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { - acpi_hw_low_level_write(8, data, &ec->data_addr); + outb(data, ec->data_addr); } -static int acpi_ec_check_status(u32 status, u8 event) { - +static int acpi_ec_check_status(u8 status, u8 event) +{ switch (event) { case ACPI_EC_EVENT_OBF_1: if (status & ACPI_EC_FLAG_OBF) @@ -201,8 +193,8 @@ static int acpi_ec_wait(struct acpi_ec *ec, u8 event) */ int acpi_ec_enter_burst_mode(struct acpi_ec *ec) { - u32 tmp = 0; - u32 status = 0; + u8 tmp = 0; + u8 status = 0; status = acpi_ec_read_status(ec); @@ -227,7 +219,7 @@ int acpi_ec_enter_burst_mode(struct acpi_ec *ec) int acpi_ec_leave_burst_mode(struct acpi_ec *ec) { - u32 status = 0; + u8 status = 0; status = acpi_ec_read_status(ec); @@ -268,14 +260,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, } for (; rdata_len > 0; rdata_len --) { - u32 d; - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); if (result) return result; - d = acpi_ec_read_data(ec); - *(rdata++) = (u8) d; + *(rdata++) = acpi_ec_read_data(ec); } return 0; @@ -320,7 +309,7 @@ end: return status; } -static int acpi_ec_read(struct acpi_ec *ec, u8 address, u32 * data) +static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) { int result; u8 d; @@ -330,6 +319,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u32 * data) *data = d; return result; } + static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) { u8 wdata[2] = { address, data }; @@ -340,11 +330,11 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) /* * Externally callable EC access functions. For now, assume 1 EC only */ -int ec_read(u8 addr, u8 * val) +int ec_read(u8 addr, u8 *val) { struct acpi_ec *ec; int err; - u32 temp_data; + u8 temp_data; if (!first_ec) return -ENODEV; @@ -394,7 +384,7 @@ extern int ec_transaction(u8 command, wdata_len, rdata, rdata_len); } -static int acpi_ec_query(struct acpi_ec *ec, u32 * data) +static int acpi_ec_query(struct acpi_ec *ec, u8 *data) { int result; u8 d; @@ -431,14 +421,10 @@ struct acpi_ec_query_data { static void acpi_ec_gpe_query(void *ec_cxt) { struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; - u32 value = 0; - static char object_name[5] = { '_', 'Q', '0', '0', '\0' }; - const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' - }; + u8 value = 0; + static char object_name[8]; - - if (!ec_cxt) + if (!ec) goto end; value = acpi_ec_read_status(ec); @@ -449,8 +435,7 @@ static void acpi_ec_gpe_query(void *ec_cxt) if (acpi_ec_query(ec, &value)) goto end; - object_name[2] = hex[((value >> 4) & 0x0F)]; - object_name[3] = hex[(value & 0x0F)]; + snprintf(object_name, 8, "_Q%2.2X", value); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s", object_name)); @@ -463,8 +448,7 @@ static void acpi_ec_gpe_query(void *ec_cxt) static u32 acpi_ec_gpe_handler(void *data) { acpi_status status = AE_OK; - u32 value; - u8 exec_mode; + u8 value; struct acpi_ec *ec = (struct acpi_ec *)data; acpi_clear_gpe(NULL, ec->gpe_bit, ACPI_ISR); @@ -475,13 +459,10 @@ static u32 acpi_ec_gpe_handler(void *data) ec->expect_event = 0; wake_up(&ec->wait); } - exec_mode = OSL_EC_BURST_HANDLER; - } else { - exec_mode = OSL_EC_POLL_HANDLER; } if (value & ACPI_EC_FLAG_SCI) { - status = acpi_os_execute(exec_mode, acpi_ec_gpe_query, ec); + status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } @@ -535,7 +516,7 @@ acpi_ec_space_handler(u32 function, switch (function) { case ACPI_READ: temp = 0; - result = acpi_ec_read(ec, (u8) address, (u32 *) & temp); + result = acpi_ec_read(ec, (u8) address, (u8 *) &temp); break; case ACPI_WRITE: result = acpi_ec_write(ec, (u8) address, (u8) temp); @@ -595,8 +576,8 @@ static int acpi_ec_read_info(struct seq_file *seq, void *offset) seq_printf(seq, "gpe bit: 0x%02x\n", (u32) ec->gpe_bit); seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->status_addr.address, - (u32) ec->data_addr.address); + (u32) ec->command_addr, + (u32) ec->data_addr); seq_printf(seq, "use global lock: %s\n", ec->global_lock ? "yes" : "no"); acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); @@ -752,7 +733,6 @@ static acpi_status acpi_ec_io_ports(struct acpi_resource *resource, void *context) { struct acpi_ec *ec = (struct acpi_ec *)context; - struct acpi_generic_address *addr; if (resource->type != ACPI_RESOURCE_TYPE_IO) { return AE_OK; @@ -763,19 +743,14 @@ acpi_ec_io_ports(struct acpi_resource *resource, void *context) * the second address region returned is the status/command * port. */ - if (ec->data_addr.register_bit_width == 0) { - addr = &ec->data_addr; - } else if (ec->command_addr.register_bit_width == 0) { - addr = &ec->command_addr; + if (ec->data_addr == 0) { + ec->data_addr = resource->data.io.minimum; + } else if (ec->command_addr == 0) { + ec->command_addr = resource->data.io.minimum; } else { return AE_CTRL_TERMINATE; } - addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO; - addr->register_bit_width = 8; - addr->register_bit_offset = 0; - addr->address = resource->data.io.minimum; - return AE_OK; } @@ -798,19 +773,14 @@ static int acpi_ec_start(struct acpi_device *device) */ status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec); - if (ACPI_FAILURE(status) - || ec->command_addr.register_bit_width == 0) { + if (ACPI_FAILURE(status) || ec->command_addr == 0) { ACPI_EXCEPTION((AE_INFO, status, "Error getting I/O port addresses")); return -ENODEV; } - ec->status_addr = ec->command_addr; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x", - (u32) ec->gpe_bit, - (u32) ec->command_addr.address, - (u32) ec->data_addr.address)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", + ec->gpe_bit, ec->command_addr, ec->data_addr)); /* * Install GPE handler @@ -877,7 +847,6 @@ acpi_fake_ecdt_callback(acpi_handle handle, acpi_ec_io_ports, ec_ecdt); if (ACPI_FAILURE(status)) return status; - ec_ecdt->status_addr = ec_ecdt->command_addr; ec_ecdt->uid = -1; acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); @@ -890,10 +859,8 @@ acpi_fake_ecdt_callback(acpi_handle handle, ec_ecdt->global_lock = TRUE; ec_ecdt->handle = handle; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02x, ports=0x%2x, 0x%2x", - (u32) ec_ecdt->gpe_bit, - (u32) ec_ecdt->command_addr.address, - (u32) ec_ecdt->data_addr.address)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", + ec_ecdt->gpe_bit, ec_ecdt->command_addr, ec_ecdt->data_addr)); return AE_CTRL_TERMINATE; } @@ -961,9 +928,8 @@ static int __init acpi_ec_get_real_ecdt(void) if (acpi_ec_mode == EC_INTR) { init_waitqueue_head(&ec_ecdt->wait); } - ec_ecdt->command_addr = ecdt_ptr->ec_control; - ec_ecdt->status_addr = ecdt_ptr->ec_control; - ec_ecdt->data_addr = ecdt_ptr->ec_data; + ec_ecdt->command_addr = ecdt_ptr->ec_control.address; + ec_ecdt->data_addr = ecdt_ptr->ec_data.address; ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; /* use the GL just to be safe */ ec_ecdt->global_lock = TRUE; From ab9e43c640b2b7d6e296fc39dd8cbcb96f9ae393 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 3 Oct 2006 22:49:00 -0400 Subject: [PATCH 14/28] ACPI: EC: export ec_transaction() for msi-laptop driver Signed-off-by: Lennart Poettering Signed-off-by: Len Brown --- drivers/acpi/ec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index ae05e8c1148..e6d4b084dca 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -384,6 +384,8 @@ extern int ec_transaction(u8 command, wdata_len, rdata, rdata_len); } +EXPORT_SYMBOL(ec_transaction); + static int acpi_ec_query(struct acpi_ec *ec, u8 *data) { int result; From 8c4c731a89ea6458001f48033f8988447736fb38 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Oct 2006 01:27:02 -0400 Subject: [PATCH 15/28] MSI S270 Laptop support: backlight, wlan, bluetooth states Create a driver to support the platform-specific features of MSI S270 laptops (and maybe other MSI laptops). This driver implements a backlight device for controlling LCD brightness (/sys/class/backlight/msi-laptop-bl/). In addition it allows access to the WLAN and Bluetooth states through a platform driver (/sys/devices/platform/msi-laptop-pf/). Signed-off-by: Lennart Poettering Cc: Dmitry Torokhov Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- MAINTAINERS | 7 + drivers/misc/Kconfig | 19 ++ drivers/misc/Makefile | 1 + drivers/misc/msi-laptop.c | 395 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 422 insertions(+) create mode 100644 drivers/misc/msi-laptop.c diff --git a/MAINTAINERS b/MAINTAINERS index 1b5430a49fd..a2b6d9fa350 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1998,6 +1998,13 @@ M: rubini@ipvvis.unipv.it L: linux-kernel@vger.kernel.org S: Maintained +MSI LAPTOP SUPPORT +P: Lennart Poettering +M: mzxreary@0pointer.de +L: https://tango.0pointer.de/mailman/listinfo/s270-linux +W: http://0pointer.de/lennart/tchibo.html +S: Maintained + MTRR AND SIMILAR SUPPORT [i386] P: Richard Gooch M: rgooch@atnf.csiro.au diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3df0e7a07c4..fa7acc2c5c6 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -57,4 +57,23 @@ config TIFM_7XX1 To compile this driver as a module, choose M here: the module will be called tifm_7xx1. +config MSI_LAPTOP + tristate "MSI Laptop Extras" + depends on X86 + depends on ACPI_EC + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is a driver for laptops built by MSI (MICRO-STAR + INTERNATIONAL): + + MSI MegaBook S270 (MS-1013) + Cytron/TCM/Medion/Tchibo MD96100/SAM2000 + + It adds support for Bluetooth, WLAN and LCD brightness control. + + More information about this driver is available at + . + + If you have an MSI S270 laptop, say Y or M here. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d65ece76095..9a91c1ee849 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -5,6 +5,7 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ +obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c new file mode 100644 index 00000000000..fdb7153f442 --- /dev/null +++ b/drivers/misc/msi-laptop.c @@ -0,0 +1,395 @@ +/*-*-linux-c-*-*/ + +/* + Copyright (C) 2006 Lennart Poettering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +/* + * msi-laptop.c - MSI S270 laptop support. This laptop is sold under + * various brands, including "Cytron/TCM/Medion/Tchibo MD96100". + * + * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/: + * + * lcd_level - Screen brightness: contains a single integer in the + * range 0..8. (rw) + * + * auto_brightness - Enable automatic brightness control: contains + * either 0 or 1. If set to 1 the hardware adjusts the screen + * brightness automatically when the power cord is + * plugged/unplugged. (rw) + * + * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro) + * + * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1 + * Please note that this file is constantly 0 if no Bluetooth + * hardware is available. (ro) + * + * In addition to these platform device attributes the driver + * registers itself in the Linux backlight control subsystem and is + * available to userspace under /sys/class/backlight/msi-laptop-bl/. + * + * This driver might work on other laptops produced by MSI. If you + * want to try it you can pass force=1 as argument to the module which + * will force it to load even when the DMI data doesn't identify the + * laptop as MSI S270. YMMV. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSI_DRIVER_VERSION "0.5" + +#define MSI_LCD_LEVEL_MAX 9 + +#define MSI_EC_COMMAND_WIRELESS 0x10 +#define MSI_EC_COMMAND_LCD_LEVEL 0x11 + +static int force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static int auto_brightness; +module_param(auto_brightness, int, 0); +MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); + +/* Hardware access */ + +static int set_lcd_level(int level) +{ + u8 buf[2]; + + if (level < 0 || level >= MSI_LCD_LEVEL_MAX) + return -EINVAL; + + buf[0] = 0x80; + buf[1] = (u8) (level*31); + + return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0); +} + +static int get_lcd_level(void) +{ + u8 wdata = 0, rdata; + int result; + + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1); + if (result < 0) + return result; + + return (int) rdata / 31; +} + +static int get_auto_brightness(void) +{ + u8 wdata = 4, rdata; + int result; + + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1); + if (result < 0) + return result; + + return !!(rdata & 8); +} + +static int set_auto_brightness(int enable) +{ + u8 wdata[2], rdata; + int result; + + wdata[0] = 4; + + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1); + if (result < 0) + return result; + + wdata[0] = 0x84; + wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); + + return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0); +} + +static int get_wireless_state(int *wlan, int *bluetooth) +{ + u8 wdata = 0, rdata; + int result; + + result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); + if (result < 0) + return -1; + + if (wlan) + *wlan = !!(rdata & 8); + + if (bluetooth) + *bluetooth = !!(rdata & 128); + + return 0; +} + +/* Backlight device stuff */ + +static int bl_get_brightness(struct backlight_device *b) +{ + return get_lcd_level(); +} + + +static int bl_update_status(struct backlight_device *b) +{ + return set_lcd_level(b->props->brightness); +} + +static struct backlight_properties msibl_props = { + .owner = THIS_MODULE, + .get_brightness = bl_get_brightness, + .update_status = bl_update_status, + .max_brightness = MSI_LCD_LEVEL_MAX-1, +}; + +static struct backlight_device *msibl_device; + +/* Platform device */ + +static ssize_t show_wlan(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret, enabled; + + ret = get_wireless_state(&enabled, NULL); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", enabled); +} + +static ssize_t show_bluetooth(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret, enabled; + + ret = get_wireless_state(NULL, &enabled); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", enabled); +} + +static ssize_t show_lcd_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret; + + ret = get_lcd_level(); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t store_lcd_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + + int level, ret; + + if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX)) + return -EINVAL; + + ret = set_lcd_level(level); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t show_auto_brightness(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + int ret; + + ret = get_auto_brightness(); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", ret); +} + +static ssize_t store_auto_brightness(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + + int enable, ret; + + if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1))) + return -EINVAL; + + ret = set_auto_brightness(enable); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); +static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); +static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); +static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); + +static struct attribute *msipf_attributes[] = { + &dev_attr_lcd_level.attr, + &dev_attr_auto_brightness.attr, + &dev_attr_bluetooth.attr, + &dev_attr_wlan.attr, + NULL +}; + +static struct attribute_group msipf_attribute_group = { + .attrs = msipf_attributes +}; + +static struct platform_driver msipf_driver = { + .driver = { + .name = "msi-laptop-pf", + .owner = THIS_MODULE, + } +}; + +static struct platform_device *msipf_device; + +/* Initialization */ + +static struct dmi_system_id __initdata msi_dmi_table[] = { + { + .ident = "MSI S270", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), + } + }, + { + .ident = "Medion MD96100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), + DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), + } + }, + { } +}; + + +static int __init msi_init(void) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + + if (!force && !dmi_check_system(msi_dmi_table)) + return -ENODEV; + + if (auto_brightness < 0 || auto_brightness > 2) + return -EINVAL; + + /* Register backlight stuff */ + + msibl_device = backlight_device_register("msi-laptop-bl", NULL, &msibl_props); + if (IS_ERR(msibl_device)) + return PTR_ERR(msibl_device); + + ret = platform_driver_register(&msipf_driver); + if (ret) + goto fail_backlight; + + /* Register platform stuff */ + + msipf_device = platform_device_alloc("msi-laptop-pf", -1); + if (!msipf_device) { + ret = -ENOMEM; + goto fail_platform_driver; + } + + ret = platform_device_add(msipf_device); + if (ret) + goto fail_platform_device1; + + ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); + if (ret) + goto fail_platform_device2; + + /* Disable automatic brightness control by default because + * this module was probably loaded to do brightness control in + * software. */ + + if (auto_brightness != 2) + set_auto_brightness(auto_brightness); + + printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n"); + + return 0; + +fail_platform_device2: + + platform_device_del(msipf_device); + +fail_platform_device1: + + platform_device_put(msipf_device); + +fail_platform_driver: + + platform_driver_unregister(&msipf_driver); + +fail_backlight: + + backlight_device_unregister(msibl_device); + + return ret; +} + +static void __exit msi_cleanup(void) +{ + + sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); + platform_device_unregister(msipf_device); + platform_driver_unregister(&msipf_driver); + backlight_device_unregister(msibl_device); + + /* Enable automatic brightness control again */ + if (auto_brightness != 2) + set_auto_brightness(1); + + printk(KERN_INFO "msi-laptop: driver unloaded.\n"); +} + +module_init(msi_init); +module_exit(msi_cleanup); + +MODULE_AUTHOR("Lennart Poettering"); +MODULE_DESCRIPTION("MSI Laptop Support"); +MODULE_VERSION(MSI_DRIVER_VERSION); +MODULE_LICENSE("GPL"); From fcfc638c6b1345b6646523dbab0065b36a868ffc Mon Sep 17 00:00:00 2001 From: "Alexey Y. Starikovskiy" Date: Tue, 26 Sep 2006 04:18:16 -0400 Subject: [PATCH 16/28] ACPI: Remove deferred execution from global lock acquire wakeup path On acquiring the ACPI global lock, if there were sleepers on the lock, we used to use acpi_os_execute() to defer a thread which would signal sleepers. Now just signal the semaphore directly. http://bugzilla.kernel.org/show_bug.cgi?id=5534#c159 Signed-off-by: Len Brown --- drivers/acpi/events/evmisc.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c index 6eef4efddcf..ee2a10bf907 100644 --- a/drivers/acpi/events/evmisc.c +++ b/drivers/acpi/events/evmisc.c @@ -342,20 +342,8 @@ static u32 acpi_ev_global_lock_handler(void *context) if (acquired) { /* Got the lock, now wake all threads waiting for it */ - acpi_gbl_global_lock_acquired = TRUE; - - /* Run the Global Lock thread which will signal all waiting threads */ - - status = - acpi_os_execute(OSL_GLOBAL_LOCK_HANDLER, - acpi_ev_global_lock_thread, context); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Could not queue Global Lock thread")); - - return (ACPI_INTERRUPT_NOT_HANDLED); - } + acpi_ev_global_lock_thread(context); } return (ACPI_INTERRUPT_HANDLED); From 37605a6900f6b4d886d995751fcfeef88c4e462c Mon Sep 17 00:00:00 2001 From: "Alexey Y. Starikovskiy" Date: Tue, 26 Sep 2006 04:20:47 -0400 Subject: [PATCH 17/28] ACPI: created a dedicated workqueue for notify() execution http://bugzilla.kernel.org/show_bug.cgi?id=5534#c160 Signed-off-by: Len Brown --- drivers/acpi/osl.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 068fe4f100b..c84286cbbe2 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -73,6 +73,7 @@ static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; +static struct workqueue_struct *kacpi_notify_wq; acpi_status acpi_os_initialize(void) { @@ -91,8 +92,9 @@ acpi_status acpi_os_initialize1(void) return AE_NULL_ENTRY; } kacpid_wq = create_singlethread_workqueue("kacpid"); + kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify"); BUG_ON(!kacpid_wq); - + BUG_ON(!kacpi_notify_wq); return AE_OK; } @@ -104,6 +106,7 @@ acpi_status acpi_os_terminate(void) } destroy_workqueue(kacpid_wq); + destroy_workqueue(kacpi_notify_wq); return AE_OK; } @@ -566,10 +569,7 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */ static void acpi_os_execute_deferred(void *context) { - struct acpi_os_dpc *dpc = NULL; - - - dpc = (struct acpi_os_dpc *)context; + struct acpi_os_dpc *dpc = (struct acpi_os_dpc *)context; if (!dpc) { printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); return; @@ -604,14 +604,12 @@ acpi_status acpi_os_execute(acpi_execute_type type, struct acpi_os_dpc *dpc; struct work_struct *task; - ACPI_FUNCTION_TRACE("os_queue_for_execution"); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); if (!function) - return_ACPI_STATUS(AE_BAD_PARAMETER); + return AE_BAD_PARAMETER; /* * Allocate/initialize DPC structure. Note that this memory will be @@ -624,26 +622,20 @@ acpi_status acpi_os_execute(acpi_execute_type type, * from the same memory. */ - dpc = - kmalloc(sizeof(struct acpi_os_dpc) + sizeof(struct work_struct), - GFP_ATOMIC); + dpc = kmalloc(sizeof(struct acpi_os_dpc) + + sizeof(struct work_struct), GFP_ATOMIC); if (!dpc) - return_ACPI_STATUS(AE_NO_MEMORY); - + return AE_NO_MEMORY; dpc->function = function; dpc->context = context; - task = (void *)(dpc + 1); INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc); - - if (!queue_work(kacpid_wq, task)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Call to queue_work() failed.\n")); - kfree(dpc); + if (!queue_work((type == OSL_NOTIFY_HANDLER)? + kacpi_notify_wq : kacpid_wq, task)) { status = AE_ERROR; + kfree(dpc); } - - return_ACPI_STATUS(status); + return status; } EXPORT_SYMBOL(acpi_os_execute); From 2fe6dffabb06bfa0591c8c490b092b458fba1f06 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 31 Aug 2006 14:15:54 +0200 Subject: [PATCH 18/28] ACPI: ibm_acpi: delete obsolete documentation As this module is now part of the kernel tree, there is no need for instructions on how to download it and build an external module. Signed-off-by: Pavel Machek Signed-off-by: Len Brown --- Documentation/ibm-acpi.txt | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 00b8cf3999c..e50595bfd8e 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -53,40 +53,7 @@ Installation If you are compiling this driver as included in the Linux kernel sources, simply enable the CONFIG_ACPI_IBM option (Power Management / -ACPI / IBM ThinkPad Laptop Extras). The rest of this section describes -how to install this driver when downloaded from the web site. - -First, you need to get a kernel with ACPI support up and running. -Please refer to http://acpi.sourceforge.net/ for help with this -step. How successful you will be depends a lot on you ThinkPad model, -the kernel you are using and any additional patches applied. The -kernel provided with your distribution may not be good enough. I -needed to compile a 2.6.7 kernel with the 20040715 ACPI patch to get -ACPI working reliably on my ThinkPad X40. Old ThinkPad models may not -be supported at all. - -Assuming you have the basic ACPI support working (e.g. you can see the -/proc/acpi directory), follow the following steps to install this -driver: - - - unpack the archive: - - tar xzvf ibm-acpi-x.y.tar.gz; cd ibm-acpi-x.y - - - compile the driver: - - make - - - install the module in your kernel modules directory: - - make install - - - load the module: - - modprobe ibm_acpi - -After loading the module, check the "dmesg" output for any error messages. - +ACPI / IBM ThinkPad Laptop Extras). Features -------- From 786f18c666d7202a86a8aa42a98783b115fe8739 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Aug 2006 23:18:06 -0400 Subject: [PATCH 19/28] ACPI: fix potential OOPS in power driver with CONFIG_ACPI_DEBUG device was set to null and used before set in a debug printk Signed-off-by: Dmitry Torokhov Signed-off-by: Len Brown --- drivers/acpi/power.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index fec225d1b6b..fe67a8af520 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -216,10 +216,8 @@ static int acpi_power_off_device(acpi_handle handle) { int result = 0; acpi_status status = AE_OK; - struct acpi_device *device = NULL; struct acpi_power_resource *resource = NULL; - result = acpi_power_get_context(handle, &resource); if (result) return result; @@ -230,13 +228,13 @@ static int acpi_power_off_device(acpi_handle handle) if (resource->references) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is still in use, dereferencing\n", - device->pnp.bus_id)); + resource->device->pnp.bus_id)); return 0; } if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", - device->pnp.bus_id)); + resource->device->pnp.bus_id)); return 0; } @@ -251,8 +249,7 @@ static int acpi_power_off_device(acpi_handle handle) return -ENOEXEC; /* Update the power resource's _device_ power state */ - device = resource->device; - device->power.state = ACPI_STATE_D3; + resource->device->power.state = ACPI_STATE_D3; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", resource->name)); From 168a328f30e9d1a8bc6ff55c0501e0bdc08bee60 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 24 Aug 2006 00:36:19 -0400 Subject: [PATCH 20/28] ACPI: acpi_pci_link_set() can allocate with either GFP_ATOMIC or GFP_KERNEL acpi_pci_link_set() allocates both with interrupts on and with interrupts off (resume-time), so check interrupts and decide on GFP_ATOMIC or GFP_KERNEL at run-time. Signed-off-by: Jiri Kosina Signed-off-by: Len Brown --- drivers/acpi/pci_link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 7f3e7e77e79..d53bd9878ca 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -307,7 +307,7 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) if (!link || !irq) return -EINVAL; - resource = kmalloc(sizeof(*resource) + 1, GFP_ATOMIC); + resource = kmalloc(sizeof(*resource) + 1, irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL); if (!resource) return -ENOMEM; From f4d2e2d87eac0338884b2c26f6bafed115dbac5e Mon Sep 17 00:00:00 2001 From: Len Brown Date: Thu, 14 Sep 2006 17:16:22 -0400 Subject: [PATCH 21/28] ACPI: update comments in motherboard.c Signed-off-by: Len Brown --- drivers/acpi/motherboard.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/acpi/motherboard.c b/drivers/acpi/motherboard.c index ec6b7f9ede3..2e17ec75af0 100644 --- a/drivers/acpi/motherboard.c +++ b/drivers/acpi/motherboard.c @@ -48,6 +48,12 @@ ACPI_MODULE_NAME("acpi_motherboard") * the io ports if they really know they can use it, while * still preventing hotplug PCI devices from using it. */ + +/* + * When CONFIG_PNP is enabled, pnp/system.c binds to PNP0C01 + * and PNP0C02, redundant with acpi_reserve_io_ranges(). + * But acpi_reserve_io_ranges() is necessary for !CONFIG_PNP. + */ static acpi_status acpi_reserve_io_ranges(struct acpi_resource *res, void *data) { struct resource *requested_res = NULL; From 7af8b66004fa827958b4871112e59a07db5b3f6b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 10 Oct 2006 14:20:31 -0700 Subject: [PATCH 22/28] ACPI: fix section for CPU init functions The ACPI processor init functions should be marked as __cpuinit as they use structures marked with __cpuinitdata. Signed-off-by: Pierre Ossman Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/processor_core.c | 2 +- drivers/acpi/processor_idle.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index b13d64415b7..1908e0d2022 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -519,7 +519,7 @@ static int acpi_processor_get_info(struct acpi_processor *pr) static void *processor_device_array[NR_CPUS]; -static int acpi_processor_start(struct acpi_device *device) +static int __cpuinit acpi_processor_start(struct acpi_device *device) { int result = 0; acpi_status status = AE_OK; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0a395fca843..8537c429a02 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1105,7 +1105,7 @@ static struct notifier_block acpi_processor_latency_notifier = { .notifier_call = acpi_processor_latency_notify, }; -int acpi_processor_power_init(struct acpi_processor *pr, +int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device) { acpi_status status = 0; From a790b323fb1b73f9388426bf3b96f153d1c90d2c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 10 Oct 2006 14:20:32 -0700 Subject: [PATCH 23/28] ACPI: fix printk format warnings Fix printk format warnings in drivers/acpi: drivers/acpi/tables/tbget.c:326: warning: format '%X' expects type 'unsigned int', but argument 5 has type 'long unsigned int' drivers/acpi/tables/tbrsdt.c:189: warning: format '%X' expects type 'unsigned int', but argument 5 has type 'long unsigned int' Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/tables/tbget.c | 2 +- drivers/acpi/tables/tbrsdt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/tables/tbget.c b/drivers/acpi/tables/tbget.c index 7856db759af..11e2d4454e0 100644 --- a/drivers/acpi/tables/tbget.c +++ b/drivers/acpi/tables/tbget.c @@ -324,7 +324,7 @@ acpi_tb_get_this_table(struct acpi_pointer *address, if (header->length < sizeof(struct acpi_table_header)) { ACPI_ERROR((AE_INFO, - "Table length (%X) is smaller than minimum (%X)", + "Table length (%X) is smaller than minimum (%zX)", header->length, sizeof(struct acpi_table_header))); return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); diff --git a/drivers/acpi/tables/tbrsdt.c b/drivers/acpi/tables/tbrsdt.c index 0ad3dbb9ebc..86a5fca9b73 100644 --- a/drivers/acpi/tables/tbrsdt.c +++ b/drivers/acpi/tables/tbrsdt.c @@ -187,7 +187,7 @@ acpi_status acpi_tb_validate_rsdt(struct acpi_table_header *table_ptr) if (table_ptr->length < sizeof(struct acpi_table_header)) { ACPI_ERROR((AE_INFO, - "RSDT/XSDT length (%X) is smaller than minimum (%X)", + "RSDT/XSDT length (%X) is smaller than minimum (%zX)", table_ptr->length, sizeof(struct acpi_table_header))); From 281ea49b0c294649a6de47a6f8fbe5611137726b Mon Sep 17 00:00:00 2001 From: Kimball Murray Date: Tue, 10 Oct 2006 14:20:33 -0700 Subject: [PATCH 24/28] ACPI: SCI interrupt source override The Linux group at Stratus Technologies has come across an issue with SCI routing under ACPI. We were bitten by this when we made an x86_64 platform whose BIOS provides an Interrupt Source Override for the SCI itself. Apparently the override has no effect for the System Control Interrupt, and this appears to be because of the way the SCI is setup in the ACPI code. It does not handle the case where busirq != gsi. The code that sets up the SCI routing assumes that bus irq == global irq. So there is simply no provision for telling it otherwise. The attached patch provides this mechanism. This patch provided by David Bulkow, was tested on an i386 platform, which does not use the SCI override, and also on an x86_64 platform which does use an override. Signed-off-by: David Bulkow Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- arch/i386/kernel/acpi/boot.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index 92f79cdd9a4..ab974ff9707 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -332,7 +332,7 @@ acpi_parse_ioapic(acpi_table_entry_header * header, const unsigned long end) /* * Parse Interrupt Source Override for the ACPI SCI */ -static void acpi_sci_ioapic_setup(u32 gsi, u16 polarity, u16 trigger) +static void acpi_sci_ioapic_setup(u32 bus_irq, u32 gsi, u16 polarity, u16 trigger) { if (trigger == 0) /* compatible SCI trigger is level */ trigger = 3; @@ -352,13 +352,13 @@ static void acpi_sci_ioapic_setup(u32 gsi, u16 polarity, u16 trigger) * If GSI is < 16, this will update its flags, * else it will create a new mp_irqs[] entry. */ - mp_override_legacy_irq(gsi, polarity, trigger, gsi); + mp_override_legacy_irq(bus_irq, polarity, trigger, gsi); /* * stash over-ride to indicate we've been here * and for later update of acpi_fadt */ - acpi_sci_override_gsi = gsi; + acpi_sci_override_gsi = bus_irq; return; } @@ -376,7 +376,7 @@ acpi_parse_int_src_ovr(acpi_table_entry_header * header, acpi_table_print_madt_entry(header); if (intsrc->bus_irq == acpi_fadt.sci_int) { - acpi_sci_ioapic_setup(intsrc->global_irq, + acpi_sci_ioapic_setup(intsrc->bus_irq, intsrc->global_irq, intsrc->flags.polarity, intsrc->flags.trigger); return 0; @@ -879,7 +879,7 @@ static int __init acpi_parse_madt_ioapic_entries(void) * pretend we got one so we can set the SCI flags. */ if (!acpi_sci_override_gsi) - acpi_sci_ioapic_setup(acpi_fadt.sci_int, 0, 0); + acpi_sci_ioapic_setup(acpi_fadt.sci_int, acpi_fadt.sci_int, 0, 0); /* Fill in identity legacy mapings where no override */ mp_config_acpi_legacy_irqs(); From 6311f0dac006032b82e3556874a1e18b31e80de2 Mon Sep 17 00:00:00 2001 From: Darren Jenkins Date: Tue, 10 Oct 2006 14:20:35 -0700 Subject: [PATCH 25/28] ACPI: asus_acpi: fix proc files parsing ICC complains about a "Pointless comparsion of unsigned interger with zero" @ line 760 & 808 of asus_acpi.c parse_arg() mentioned below returns -E but it's copied into unsigned variable... Signed-off-by: Darren Jenkins Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/asus_acpi.c | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index ebc033f87e7..33eaca108f4 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -567,11 +567,11 @@ static int write_led(const char __user * buffer, unsigned long count, char *ledname, int ledmask, int invert) { - int value; + int rv, value; int led_out = 0; - count = parse_arg(buffer, count, &value); - if (count > 0) + rv = parse_arg(buffer, count, &value); + if (rv > 0) led_out = value ? 1 : 0; hotk->status = @@ -584,7 +584,7 @@ write_led(const char __user * buffer, unsigned long count, printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n", ledname); - return count; + return rv; } /* @@ -619,20 +619,20 @@ static int proc_write_ledd(struct file *file, const char __user * buffer, unsigned long count, void *data) { - int value; + int rv, value; - count = parse_arg(buffer, count, &value); - if (count > 0) { + rv = parse_arg(buffer, count, &value); + if (rv > 0) { if (!write_acpi_int (hotk->handle, hotk->methods->mt_ledd, value, NULL)) printk(KERN_WARNING "Asus ACPI: LED display write failed\n"); else hotk->ledd_status = (u32) value; - } else if (count < 0) + } else if (rv < 0) printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); - return count; + return rv; } /* @@ -773,12 +773,12 @@ static int proc_write_lcd(struct file *file, const char __user * buffer, unsigned long count, void *data) { - int value; + int rv, value; - count = parse_arg(buffer, count, &value); - if (count > 0) + rv = parse_arg(buffer, count, &value); + if (rv > 0) set_lcd_state(value); - return count; + return rv; } static int read_brightness(void) @@ -842,18 +842,18 @@ static int proc_write_brn(struct file *file, const char __user * buffer, unsigned long count, void *data) { - int value; + int rv, value; - count = parse_arg(buffer, count, &value); - if (count > 0) { + rv = parse_arg(buffer, count, &value); + if (rv > 0) { value = (0 < value) ? ((15 < value) ? 15 : value) : 0; /* 0 <= value <= 15 */ set_brightness(value); - } else if (count < 0) { + } else if (rv < 0) { printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); } - return count; + return rv; } static void set_display(int value) @@ -892,15 +892,15 @@ static int proc_write_disp(struct file *file, const char __user * buffer, unsigned long count, void *data) { - int value; + int rv, value; - count = parse_arg(buffer, count, &value); - if (count > 0) + rv = parse_arg(buffer, count, &value); + if (rv > 0) set_display(value); - else if (count < 0) + else if (rv < 0) printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); - return count; + return rv; } typedef int (proc_readfunc) (char *page, char **start, off_t off, int count, From 6df05702f97f99e038ab817f4466386f6255f58d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 10 Oct 2006 14:20:36 -0700 Subject: [PATCH 26/28] ACPI: asus_acpi: don't printk on writing garbage to proc files This reporting is useless (we errno anyway). Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/asus_acpi.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index 33eaca108f4..c7ac9297a20 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -629,9 +629,7 @@ proc_write_ledd(struct file *file, const char __user * buffer, "Asus ACPI: LED display write failed\n"); else hotk->ledd_status = (u32) value; - } else if (rv < 0) - printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); - + } return rv; } @@ -849,10 +847,7 @@ proc_write_brn(struct file *file, const char __user * buffer, value = (0 < value) ? ((15 < value) ? 15 : value) : 0; /* 0 <= value <= 15 */ set_brightness(value); - } else if (rv < 0) { - printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); } - return rv; } @@ -897,9 +892,6 @@ proc_write_disp(struct file *file, const char __user * buffer, rv = parse_arg(buffer, count, &value); if (rv > 0) set_display(value); - else if (rv < 0) - printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); - return rv; } From aeb1104814e1177b865eec4f4b364602f5fcb6d1 Mon Sep 17 00:00:00 2001 From: Eiichiro Oiwa Date: Mon, 2 Oct 2006 19:18:03 +0400 Subject: [PATCH 27/28] ACPICA: Fix incorrect handling of PCI Express Root Bridge _HID I could not get correct PCI Express bus number from the structure of acpi_object_extra. I always get zero as bus number regardless of bus location. I found that there is incorrect comparison with _HID (PNP0A08) in acpi/events/evrgnini.c and PCI Express _BBN method always fail. Therefore, we always get zero as PCI Express bus number. http://bugzilla.kernel.org/show_bug.cgi?id=7145 Signed-off-by: Bob Moore Signed-off-by: Alexey Starikovskiy Signed-off-by: Len Brown --- drivers/acpi/events/evrgnini.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c index 5b3c7a85eb9..203d1359190 100644 --- a/drivers/acpi/events/evrgnini.c +++ b/drivers/acpi/events/evrgnini.c @@ -225,13 +225,12 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, if (! (ACPI_STRNCMP (object_hID.value, PCI_ROOT_HID_STRING, - sizeof(PCI_ROOT_HID_STRING)) - || - !(ACPI_STRNCMP - (object_hID.value, - PCI_EXPRESS_ROOT_HID_STRING, - sizeof(PCI_EXPRESS_ROOT_HID_STRING))))) - { + sizeof(PCI_ROOT_HID_STRING))) + || + !(ACPI_STRNCMP + (object_hID.value, + PCI_EXPRESS_ROOT_HID_STRING, + sizeof(PCI_EXPRESS_ROOT_HID_STRING)))) { /* Install a handler for this PCI root bridge */ From 34c4415ab857dc6d51db08d62bcd45d4b8513bb6 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 10 Oct 2006 14:20:41 -0700 Subject: [PATCH 28/28] ACPI: check battery status on resume for un/plug events during sleep Add ->resume method to the ACPI battery handler to check if the battery state has changed during sleep. If yes, update the ACPI internal data structures for benefit of /proc/acpi/battery/. Signed-off-by: Jiri Kosina Cc: Stefan Seyfried Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/battery.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 9810e2a55d0..026e40755cd 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -64,6 +64,7 @@ extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); static int acpi_battery_add(struct acpi_device *device); static int acpi_battery_remove(struct acpi_device *device, int type); +static int acpi_battery_resume(struct acpi_device *device, int status); static struct acpi_driver acpi_battery_driver = { .name = ACPI_BATTERY_DRIVER_NAME, @@ -71,6 +72,7 @@ static struct acpi_driver acpi_battery_driver = { .ids = ACPI_BATTERY_HID, .ops = { .add = acpi_battery_add, + .resume = acpi_battery_resume, .remove = acpi_battery_remove, }, }; @@ -753,6 +755,18 @@ static int acpi_battery_remove(struct acpi_device *device, int type) return 0; } +/* this is needed to learn about changes made in suspended state */ +static int acpi_battery_resume(struct acpi_device *device, int state) +{ + struct acpi_battery *battery; + + if (!device) + return -EINVAL; + + battery = device->driver_data; + return acpi_battery_check(battery); +} + static int __init acpi_battery_init(void) { int result;