diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-05-18 13:04:56 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-05-18 13:04:56 +1000 |
commit | 7f389f5d44aec1422034bf34adfa8b2d3d6d5b14 (patch) | |
tree | 9b8929fbc1433e999c8ef36291105fa60865216a /drivers | |
parent | 442dc36f03db9bb80050b1dbc749bccc08f97b80 (diff) | |
parent | 9532b3c84be27b25ca6e01da460d72ccf8fa91e3 (diff) |
Merge commit 'acpi/test'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/acpica/acglobal.h | 3 | ||||
-rw-r--r-- | drivers/acpi/acpica/dsobject.c | 5 | ||||
-rw-r--r-- | drivers/acpi/acpica/dsopcode.c | 17 | ||||
-rw-r--r-- | drivers/acpi/acpica/dswstate.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/evxfevnt.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/exmutex.c | 9 | ||||
-rw-r--r-- | drivers/acpi/acpica/hwregs.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/nsnames.c | 2 | ||||
-rw-r--r-- | drivers/acpi/acpica/nspredef.c | 7 | ||||
-rw-r--r-- | drivers/acpi/acpica/nssearch.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/rscalc.c | 3 | ||||
-rw-r--r-- | drivers/acpi/acpica/rsxface.c | 8 | ||||
-rw-r--r-- | drivers/acpi/acpica/tbfadt.c | 16 | ||||
-rw-r--r-- | drivers/acpi/acpica/tbinstal.c | 2 | ||||
-rw-r--r-- | drivers/acpi/acpica/utcopy.c | 23 | ||||
-rw-r--r-- | drivers/acpi/acpica/utdebug.c | 8 | ||||
-rw-r--r-- | drivers/acpi/acpica/utmisc.c | 20 | ||||
-rw-r--r-- | drivers/acpi/acpica/utmutex.c | 26 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 14 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 87 | ||||
-rw-r--r-- | drivers/platform/x86/oqo-wmi.c | 941 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 206 |
23 files changed, 1336 insertions, 78 deletions
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 16e5210ae936..3d87362d17ed 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -362,9 +362,6 @@ extern u8 acpi_gbl_method_executing; extern u8 acpi_gbl_abort_method; extern u8 acpi_gbl_db_terminate_threads; -ACPI_EXTERN int optind; -ACPI_EXTERN char *optarg; - ACPI_EXTERN u8 acpi_gbl_db_opt_tables; ACPI_EXTERN u8 acpi_gbl_db_opt_stats; ACPI_EXTERN u8 acpi_gbl_db_opt_ini_methods; diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index dab3f48f0b42..02e6caad4a76 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -734,7 +734,8 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, /* Local ID (0-7) is (AML opcode - base AML_LOCAL_OP) */ - obj_desc->reference.value = opcode - AML_LOCAL_OP; + obj_desc->reference.value = + ((u32)opcode) - AML_LOCAL_OP; obj_desc->reference.class = ACPI_REFCLASS_LOCAL; #ifndef ACPI_NO_METHOD_EXECUTION @@ -754,7 +755,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, /* Arg ID (0-6) is (AML opcode - base AML_ARG_OP) */ - obj_desc->reference.value = opcode - AML_ARG_OP; + obj_desc->reference.value = ((u32)opcode) - AML_ARG_OP; obj_desc->reference.class = ACPI_REFCLASS_ARG; #ifndef ACPI_NO_METHOD_EXECUTION diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index b4c87b5053e6..584d766e6f12 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -1386,14 +1386,19 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, case AML_BREAK_POINT_OP: - /* Call up to the OS service layer to handle this */ - - status = - acpi_os_signal(ACPI_SIGNAL_BREAKPOINT, - "Executed AML Breakpoint opcode"); + /* + * Set the single-step flag. This will cause the debugger (if present) + * to break to the console within the AML debugger at the start of the + * next AML instruction. + */ + ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE); + ACPI_DEBUGGER_EXEC(acpi_os_printf + ("**break** Executed AML BreakPoint opcode\n")); - /* If and when it returns, all done. */ + /* Call to the OSL in case OS wants a piece of the action */ + status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT, + "Executed AML Breakpoint opcode"); break; case AML_BREAK_OP: diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 40f92bf7dce5..e46c821cf572 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -102,7 +102,7 @@ acpi_ds_result_pop(union acpi_operand_object **object, /* Return object of the top element and clean that top element result stack */ walk_state->result_count--; - index = walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; + index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; *object = state->results.obj_desc[index]; if (!*object) { @@ -186,7 +186,7 @@ acpi_ds_result_push(union acpi_operand_object * object, /* Assign the address of object to the top free element of result stack */ - index = walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; + index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; state->results.obj_desc[index] = object; walk_state->result_count++; diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index d0a080747ec3..4721f58fe42c 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -51,7 +51,7 @@ ACPI_MODULE_NAME("evxfevnt") /* Local prototypes */ -acpi_status +static acpi_status acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context); @@ -785,7 +785,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_gpe_device) * block device. NULL if the GPE is one of the FADT-defined GPEs. * ******************************************************************************/ -acpi_status +static acpi_status acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context) { diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index d301c1f363ef..d7cb030a21f8 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -373,11 +373,12 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, walk_state->thread->thread_id) && (obj_desc != acpi_gbl_global_lock_mutex)) { ACPI_ERROR((AE_INFO, - "Thread %lX cannot release Mutex [%4.4s] acquired by thread %lX", - (unsigned long)walk_state->thread->thread_id, + "Thread %p cannot release Mutex [%4.4s] acquired by thread %p", + ACPI_CAST_PTR(void, walk_state->thread->thread_id), acpi_ut_get_node_name(obj_desc->mutex.node), - (unsigned long)obj_desc->mutex.owner_thread-> - thread_id)); + ACPI_CAST_PTR(void, + obj_desc->mutex.owner_thread-> + thread_id))); return_ACPI_STATUS(AE_AML_NOT_OWNER); } diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 7b2fb602b5cb..23d5505cb1f7 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -81,9 +81,9 @@ acpi_status acpi_hw_clear_acpi_status(void) ACPI_FUNCTION_TRACE(hw_clear_acpi_status); - ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %0llX\n", + ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n", ACPI_BITMASK_ALL_FIXED_STATUS, - acpi_gbl_xpm1a_status.address)); + ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index ae3dc10a7e81..af8e6bcee07e 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -149,7 +149,7 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node) name_buffer = ACPI_ALLOCATE_ZEROED(size); if (!name_buffer) { - ACPI_ERROR((AE_INFO, "Allocation failure")); + ACPI_ERROR((AE_INFO, "Could not allocate %u bytes", (u32)size)); return_PTR(NULL); } diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index d9e8cbc6e679..7f8e066b12a3 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -144,7 +144,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, pathname = acpi_ns_get_external_pathname(node); if (!pathname) { - pathname = ACPI_CAST_PTR(char, predefined->info.name); + return AE_OK; /* Could not get pathname, ignore */ } /* @@ -230,10 +230,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, } exit: - if (pathname != predefined->info.name) { - ACPI_FREE(pathname); - } - + ACPI_FREE(pathname); return (status); } diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index f9b4f51bf8f2..7e865639a928 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -45,6 +45,10 @@ #include "accommon.h" #include "acnamesp.h" +#ifdef ACPI_ASL_COMPILER +#include "amlcode.h" +#endif + #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nssearch") diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 88b5a2c4814d..d3f77a5df79d 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -593,9 +593,6 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, } else { temp_size_needed += acpi_ns_get_pathname_length((*sub_object_list)->reference.node); - if (!temp_size_needed) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } } } else { /* diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 69a2aa5b5d83..395212bcd19b 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -338,13 +338,17 @@ acpi_resource_to_address64(struct acpi_resource *resource, switch (resource->type) { case ACPI_RESOURCE_TYPE_ADDRESS16: - address16 = (struct acpi_resource_address16 *)&resource->data; + address16 = + ACPI_CAST_PTR(struct acpi_resource_address16, + &resource->data); ACPI_COPY_ADDRESS(out, address16); break; case ACPI_RESOURCE_TYPE_ADDRESS32: - address32 = (struct acpi_resource_address32 *)&resource->data; + address32 = + ACPI_CAST_PTR(struct acpi_resource_address32, + &resource->data); ACPI_COPY_ADDRESS(out, address32); break; diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 71e655d14cb0..82b02dcb942e 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -284,9 +284,9 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) if (length > sizeof(struct acpi_table_fadt)) { ACPI_WARNING((AE_INFO, "FADT (revision %u) is longer than ACPI 2.0 version, " - "truncating length 0x%X to 0x%zX", - table->revision, (unsigned)length, - sizeof(struct acpi_table_fadt))); + "truncating length 0x%X to 0x%X", + table->revision, length, + (u32)sizeof(struct acpi_table_fadt))); } /* Clear the entire local FADT */ @@ -441,7 +441,7 @@ static void acpi_tb_convert_fadt(void) &acpi_gbl_FADT, fadt_info_table [i].length), - address32); + (u64) address32); } } } @@ -469,7 +469,6 @@ static void acpi_tb_convert_fadt(void) static void acpi_tb_validate_fadt(void) { char *name; - u32 *address32; struct acpi_generic_address *address64; u8 length; u32 i; @@ -505,15 +504,12 @@ static void acpi_tb_validate_fadt(void) for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { /* - * Generate pointers to the 32-bit and 64-bit addresses, get the - * register length (width), and the register name + * Generate pointer to the 64-bit address, get the register + * length (width) and the register name */ address64 = ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT, fadt_info_table[i].address64); - address32 = - ACPI_ADD_PTR(u32, &acpi_gbl_FADT, - fadt_info_table[i].address32); length = *ACPI_ADD_PTR(u8, &acpi_gbl_FADT, fadt_info_table[i].length); diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index f865d5a096de..63e82329a9e8 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -472,7 +472,7 @@ acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index) * lock may block, and also since the execution of a namespace walk * must be allowed to use the interpreter. */ - acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock); acpi_ns_delete_namespace_by_owner(owner_id); diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 919624f123d5..0f0c64bf8ac9 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -676,6 +676,7 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, { u16 reference_count; union acpi_operand_object *next_object; + acpi_status status; /* Save fields from destination that we don't want to overwrite */ @@ -768,6 +769,28 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, } break; + /* + * For Mutex and Event objects, we cannot simply copy the underlying + * OS object. We must create a new one. + */ + case ACPI_TYPE_MUTEX: + + status = acpi_os_create_mutex(&dest_desc->mutex.os_mutex); + if (ACPI_FAILURE(status)) { + return status; + } + break; + + case ACPI_TYPE_EVENT: + + status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0, + &dest_desc->event. + os_semaphore); + if (ACPI_FAILURE(status)) { + return status; + } + break; + default: /* Nothing to do for other simple objects */ break; diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 38821f53042c..527d729f6815 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -179,9 +179,9 @@ acpi_debug_print(u32 requested_debug_level, if (thread_id != acpi_gbl_prev_thread_id) { if (ACPI_LV_THREADS & acpi_dbg_level) { acpi_os_printf - ("\n**** Context Switch from TID %lX to TID %lX ****\n\n", - (unsigned long)acpi_gbl_prev_thread_id, - (unsigned long)thread_id); + ("\n**** Context Switch from TID %p to TID %p ****\n\n", + ACPI_CAST_PTR(void, acpi_gbl_prev_thread_id), + ACPI_CAST_PTR(void, thread_id)); } acpi_gbl_prev_thread_id = thread_id; @@ -194,7 +194,7 @@ acpi_debug_print(u32 requested_debug_level, acpi_os_printf("%8s-%04ld ", module_name, line_number); if (ACPI_LV_THREADS & acpi_dbg_level) { - acpi_os_printf("[%04lX] ", (unsigned long)thread_id); + acpi_os_printf("[%p] ", ACPI_CAST_PTR(void, thread_id)); } acpi_os_printf("[%02ld] %-22.22s: ", diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index 1c9e250caefb..fbe782348b0b 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -1033,11 +1033,12 @@ acpi_error(const char *module_name, u32 line_number, const char *format, ...) { va_list args; - acpi_os_printf("ACPI Error (%s-%04d): ", module_name, line_number); + acpi_os_printf("ACPI Error: "); va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); + acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, + line_number); va_end(args); } @@ -1047,12 +1048,12 @@ acpi_exception(const char *module_name, { va_list args; - acpi_os_printf("ACPI Exception (%s-%04d): %s, ", module_name, - line_number, acpi_format_exception(status)); + acpi_os_printf("ACPI Exception: %s, ", acpi_format_exception(status)); va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); + acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, + line_number); va_end(args); } @@ -1061,11 +1062,12 @@ acpi_warning(const char *module_name, u32 line_number, const char *format, ...) { va_list args; - acpi_os_printf("ACPI Warning (%s-%04d): ", module_name, line_number); + acpi_os_printf("ACPI Warning: "); va_start(args, format); acpi_os_vprintf(format, args); - acpi_os_printf(" [%X]\n", ACPI_CA_VERSION); + acpi_os_printf(" %8.8X %s-%u\n", ACPI_CA_VERSION, module_name, + line_number); va_end(args); } @@ -1074,10 +1076,6 @@ acpi_info(const char *module_name, u32 line_number, const char *format, ...) { va_list args; - /* - * Removed module_name, line_number, and acpica version, not needed - * for info output - */ acpi_os_printf("ACPI: "); va_start(args, format); diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 26c93a748e64..80bb65154117 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -230,17 +230,18 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) { if (i == mutex_id) { ACPI_ERROR((AE_INFO, - "Mutex [%s] already acquired by this thread [%X]", + "Mutex [%s] already acquired by this thread [%p]", acpi_ut_get_mutex_name (mutex_id), - this_thread_id)); + ACPI_CAST_PTR(void, + this_thread_id))); return (AE_ALREADY_ACQUIRED); } ACPI_ERROR((AE_INFO, - "Invalid acquire order: Thread %X owns [%s], wants [%s]", - this_thread_id, + "Invalid acquire order: Thread %p owns [%s], wants [%s]", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(i), acpi_ut_get_mutex_name(mutex_id))); @@ -251,24 +252,24 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) #endif ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %lX attempting to acquire Mutex [%s]\n", - (unsigned long)this_thread_id, + "Thread %p attempting to acquire Mutex [%s]\n", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(mutex_id))); status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, ACPI_WAIT_FOREVER); if (ACPI_SUCCESS(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %lX acquired Mutex [%s]\n", - (unsigned long)this_thread_id, + "Thread %p acquired Mutex [%s]\n", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(mutex_id))); acpi_gbl_mutex_info[mutex_id].use_count++; acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id; } else { ACPI_EXCEPTION((AE_INFO, status, - "Thread %lX could not acquire Mutex [%X]", - (unsigned long)this_thread_id, mutex_id)); + "Thread %p could not acquire Mutex [%X]", + ACPI_CAST_PTR(void, this_thread_id), mutex_id)); } return (status); @@ -293,9 +294,8 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) ACPI_FUNCTION_NAME(ut_release_mutex); this_thread_id = acpi_os_get_thread_id(); - ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %lX releasing Mutex [%s]\n", - (unsigned long)this_thread_id, + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %p releasing Mutex [%s]\n", + ACPI_CAST_PTR(void, this_thread_id), acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 284ebaca6e45..cc79ef364f38 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -143,6 +143,19 @@ config MSI_LAPTOP If you have an MSI S270 laptop, say Y or M here. +config OQO_WMI + tristate "OQO WMI extras" + depends on ACPI_WMI + depends on INPUT && INPUT_POLLDEV + depends on RFKILL + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to support rfkill and backlight control on + series 2 OQO handheld devices. + + To compile this driver as a module, choose M here: the module will + be called oqo-wmi. + config PANASONIC_LAPTOP tristate "Panasonic Laptop Extras" depends on INPUT && ACPI @@ -341,6 +354,7 @@ config EEEPC_LAPTOP select BACKLIGHT_CLASS_DEVICE select HWMON select RFKILL + select HOTPLUG_PCI ---help--- This driver supports the Fn-Fx keys on Eee PC laptops. It also adds the ability to switch camera/wlan on/off. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e40c7bd1b87e..1637378ad3f4 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o +obj-$(CONFIG_OQO_WMI) += oqo-wmi.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 353a898c3693..a0845b2853c0 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -31,6 +31,7 @@ #include <linux/input.h> #include <linux/rfkill.h> #include <linux/pci.h> +#include <linux/pci_hotplug.h> #define EEEPC_LAPTOP_VERSION "0.1" @@ -132,6 +133,7 @@ struct eeepc_hotk { u16 *keycode_map; struct rfkill *eeepc_wlan_rfkill; struct rfkill *eeepc_bluetooth_rfkill; + struct hotplug_slot *hotplug_slot; }; /* The actual device the driver binds to */ @@ -197,6 +199,15 @@ static struct acpi_driver eeepc_hotk_driver = { }, }; +/* PCI hotplug ops */ +static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value); + +static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { + .owner = THIS_MODULE, + .get_adapter_status = eeepc_get_adapter_status, + .get_power_status = eeepc_get_adapter_status, +}; + /* The backlight device /sys/class/backlight */ static struct backlight_device *eeepc_backlight_device; @@ -529,6 +540,19 @@ static int notify_brn(void) return -1; } +static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, + u8 *value) +{ + int val = get_acpi(CM_ASL_WLAN); + + if (val == 1 || val == 0) + *value = val; + else + return -EINVAL; + + return 0; +} + static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) { enum rfkill_state state; @@ -655,6 +679,54 @@ static void eeepc_unregister_rfkill_notifier(char *node) } } +static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) +{ + kfree(hotplug_slot->info); + kfree(hotplug_slot); +} + +static int eeepc_setup_pci_hotplug(void) +{ + int ret = -ENOMEM; + struct pci_bus *bus = pci_find_bus(0, 1); + + if (!bus) { + printk(EEEPC_ERR "Unable to find wifi PCI bus\n"); + return -ENODEV; + } + + ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); + if (!ehotk->hotplug_slot) + goto error_slot; + + ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), + GFP_KERNEL); + if (!ehotk->hotplug_slot->info) + goto error_info; + + ehotk->hotplug_slot->private = ehotk; + ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; + ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops; + eeepc_get_adapter_status(ehotk->hotplug_slot, + &ehotk->hotplug_slot->info->adapter_status); + + ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi"); + if (ret) { + printk(EEEPC_ERR "Unable to register hotplug slot - %d\n", ret); + goto error_register; + } + + return 0; + +error_register: + kfree(ehotk->hotplug_slot->info); +error_info: + kfree(ehotk->hotplug_slot); + ehotk->hotplug_slot = NULL; +error_slot: + return ret; +} + static int eeepc_hotk_add(struct acpi_device *device) { acpi_status status = AE_OK; @@ -738,8 +810,21 @@ static int eeepc_hotk_add(struct acpi_device *device) goto bluetooth_fail; } + result = eeepc_setup_pci_hotplug(); + /* + * If we get -EBUSY then something else is handling the PCI hotplug - + * don't fail in this case + */ + if (result == -EBUSY) + return 0; + else if (result) + goto pci_fail; + return 0; + pci_fail: + if (ehotk->eeepc_bluetooth_rfkill) + rfkill_unregister(ehotk->eeepc_bluetooth_rfkill); bluetooth_fail: if (ehotk->eeepc_bluetooth_rfkill) rfkill_free(ehotk->eeepc_bluetooth_rfkill); @@ -770,6 +855,8 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); + if (ehotk->hotplug_slot) + pci_hp_deregister(ehotk->hotplug_slot); kfree(ehotk); return 0; diff --git a/drivers/platform/x86/oqo-wmi.c b/drivers/platform/x86/oqo-wmi.c new file mode 100644 index 000000000000..0a88af82f127 --- /dev/null +++ b/drivers/platform/x86/oqo-wmi.c @@ -0,0 +1,941 @@ +/* + * OQO WMI UPMC Extras + * + * Copyright (C) 2008 Brian S. Julin <bri@abrij.org> + * + * Based on acer-wmi: + * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * NOTE: You need to turn SMI on in BIOS (if dmidecode works, you already have) + * NOTE: acpi-wmi support mandatory + * NOTE: backlight and inputdev support a must, ifdefs will come later + */ + +/* + * + * 0.3: added WLAN enable switch, restore settings on unload, + * resume/suspend handling + * 0.2: Still not production-ready, but added ambient light sensor, + * backlight, and it prints the unit serial number to dmesg (do + * not know where to make that available to userspace yet.) + * 0.1: This is a first cut. Plan to reboot after playing with this. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/dmi.h> +#include <linux/backlight.h> +#include <linux/leds.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/i8042.h> +#include <linux/input-polldev.h> +#include <linux/rfkill.h> + +#include <acpi/acpi_drivers.h> + +MODULE_AUTHOR("Brian Julin"); +MODULE_DESCRIPTION("OQO UPMC WMI Extras Driver"); +MODULE_LICENSE("GPL"); + +#define OQO_LOGPREFIX "oqo-wmi: " +#define OQO_ERR KERN_ERR OQO_LOGPREFIX +#define OQO_NOTICE KERN_NOTICE OQO_LOGPREFIX +#define OQO_INFO KERN_INFO OQO_LOGPREFIX + +#define OQO_KINE_MAXTRY 3 + +/* Store defined devices globally since we only have one instance. */ +static struct platform_device *oqo_platform_device; +static struct backlight_device *oqo_backlight_device; +static struct rfkill *oqo_rfkill; +static struct input_dev *oqo_kine; +static struct input_polled_dev *oqo_kine_polled; + +/* Likewise store current and original settings globally. */ +struct oqo_settings { + int lid_wakes; /* not sure if ACPI handles/needs help here */ + int kine_itvl; + int bl_bright; +}; + +static struct oqo_settings orig, curr; + +/* Some of this code is left like in acer-wmi so we can add the older + Model 01 and any future models more easily, but we should not expect + it to be as complicated as Acer given each model is a leap rather than + a subtle variant on the last, so we aren't using "quirks" perse. Not + sure if there is any real difference for our purposes between the o2 + and e2. +*/ +struct oqo_model { + const char *model; + u16 model_subs; +}; +#define MODEL_SUB_OQO_O2_SMB0 3 + +static struct oqo_model oqo_models[] = { + { + .model = "Model 2", + .model_subs = MODEL_SUB_OQO_O2_SMB0, + }, + {} +}; + +static struct oqo_model *model; + +static int force; +module_param(force, bool, 0644); +MODULE_PARM_DESC(force, "Force WMI detection even if DMI detection failed"); + +/* + * OQO Model 2 SMBUS registers + * We are just using WMI to read the Cx700 smbus, to share the + * ACPI mutex (what may also eventually work in VMs/win32) + * Using i2c-viapro directly could interfere with PM. + */ + +#define OQO_O2_SMB0_WWAN_DSBL_ADDR 0x19 +#define OQO_O2_SMB0_WWAN_DSBL_MASK 0x02 +#define OQO_O2_SMB0_LUMIN_LO 0x20 +#define OQO_O2_SMB0_LUMIN_HI 0x21 +#define OQO_O2_SMB0_BL_LO 0x26 +#define OQO_O2_SMB0_BL_HI 0x27 +#define OQO_O2_SMB0_ACCEL_POLL_ITVL 0x45 +#define OQO_O2_SMB0_ACCEL_XLO 0x50 +#define OQO_O2_SMB0_ACCEL_XHI 0x51 +#define OQO_O2_SMB0_ACCEL_YLO 0x52 +#define OQO_O2_SMB0_ACCEL_YHI 0x53 +#define OQO_O2_SMB0_ACCEL_ZLO 0x54 +#define OQO_O2_SMB0_ACCEL_ZHI 0x55 +/* These may be handled by ACPI not sure yet. */ +#define OQO_O2_SMB0_LID_WAKES_ADDR 0x58 +#define OQO_O2_SMB0_LID_WAKES_MASK 0x08 + +#define OQO_O2_SMB0_SERIAL_START 0x70 +#define OQO_O2_SMB0_SERIAL_LEN 11 + +static char oqo_sn[OQO_O2_SMB0_SERIAL_LEN + 1]; + +/* Other addresses I have noticed used on the 02 SMBUS (from DSDT and whatnot) + * + * These are not used because the linux ACPI drivers work fine on them + * + * 0x0A -- processor sleep mode? + * 0x0C -- ACPI events, probably clears when read. + * 0x30 -- thermal zone + * There is something going on at 0x31 through 0x34 which is likely + * also thermal. The values change over time. Have not figured that + * out yet. + * 0x41 -- AC detect + * 0x42 -- LID button ACTUALLY THIS DOES NOT WORK AND NEEDS TO BE FIXED + * 0xa0 and 0xa1 -- battery something (presence? state?) + * 0xa4 to 0xcf -- battery info (0xc8-0xca contains "OQO") + * 0xd4 to 0xef -- other battery stats + */ + +/* + * OQO method GUIDs + */ +#define OQO_O2_AMW0_GUID "ABBC0F6D-8EA1-11D1-00A0-C90629100000" +MODULE_ALIAS("wmi:ABBC0F6D-8EA1-11D1-00A0-C90629100000"); + +/* + * Interface type flags + */ +enum interface_type { + OQO_O2_AMW0, +}; + +/* Each low-level interface must define at least some of the following */ +struct wmi_interface { + /* The WMI device type */ + u32 type; +}; + +static struct wmi_interface AMW0_interface = { + .type = OQO_O2_AMW0, +}; + +/* The detected/chosen interface */ +static struct wmi_interface *interface; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + model = dmi->driver_data; + /* + * Detect which ACPI-WMI interface we're using. + */ + if (wmi_has_guid(OQO_O2_AMW0_GUID)) + interface = &AMW0_interface; + + return 0; +} + +static struct dmi_system_id oqo_dmis[] = { + { + .callback = dmi_matched, + .ident = "OQO 02", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OQO Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OQO Model 2"), + }, + .driver_data = oqo_models + 0, + }, + {} +}; + +/* + * AMW0 (V1) interface on OQO Model 2 + * + * wmba: has four functions selected by int arg 1. arg2 is 3 byte buffer. + * 1: performs GETB method on the SMBUS using bytes 0, 1 of Arg2 + * returns a buffer object containing a single byte + * 2: performs SETB on SMBUS using bytes 0, 1, 2 of Arg2 + * returns 0 as int. + * 3: dumps 256 values into a given SMBUS register (not used here) + * returns 0 as int. + * 4: puts byte 0 of arg2 into some sort of busy flag. Some ACPI + * funcs check this (==0) to decide if SMBUS operations are safe. + * returns 0 as int. + * wmbb: simply returns the busy flag set by wmba #4 + */ +static acpi_status oqo_smbus_getb(u8 addr, u8 *result) +{ + struct acpi_buffer input, res; + acpi_status status; + union acpi_object *obj; + u32 arg2; + + input.length = 4; + input.pointer = &arg2; + res.length = ACPI_ALLOCATE_BUFFER; + res.pointer = NULL; + + arg2 = addr; + arg2 <<= 8; + arg2 |= 0x12; /* HOSTCMD */ + + status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 1, &input, &res); + + if (status != AE_OK) + return status; + + obj = (union acpi_object *)res.pointer; + if (!obj) + return AE_NULL_OBJECT; + + if (obj->type != ACPI_TYPE_BUFFER + || obj->buffer.length != 1 || obj->buffer.pointer == NULL) { + kfree(obj); + return AE_TYPE; + } + *result = ((u8 *) (obj->buffer.pointer))[0]; + kfree(obj); + return status; +} + +static acpi_status oqo_smbus_setb(u8 addr, u8 val) +{ + struct acpi_buffer input, res; + acpi_status status; + union acpi_object *obj; + u32 arg2; + + input.length = 4; + input.pointer = &arg2; + res.length = ACPI_ALLOCATE_BUFFER; + res.pointer = NULL; + + arg2 = val; + arg2 <<= 8; + arg2 |= addr; + arg2 <<= 8; + arg2 |= 0x12; /* HOSTCMD */ + + status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 2, &input, &res); + + if (status != AE_OK) + return status; + + obj = (union acpi_object *)res.pointer; + if (!obj) + return AE_NULL_OBJECT; + + if (obj->type != ACPI_TYPE_INTEGER) { + kfree(obj); + return AE_TYPE; + } + kfree(obj); + return status; +} + +/* + * We assume we are the only one using this ...ahem... "lock" on + * the SMBUS because it would be pathetically noneffective otherwise. + * + * Nonzero silly_lock will keep certain ACPI routines away from the + * SMBUS (if they aren't already on it when you call it.) Zero + * silly_lock will let them back on + * + * This is probably useful before sleeping the system, and one + * waits until any ACPI funcs would have long finished before + * proceeding. It seems harmless enough and will work to wrap + * more accesses with it. + */ +static acpi_status oqo_lock_smbus(int silly_lock) +{ + struct acpi_buffer input, res; + acpi_status status; + union acpi_object *obj; + u32 arg2; + + input.length = 4; + input.pointer = &arg2; + res.length = ACPI_ALLOCATE_BUFFER; + res.pointer = NULL; + + arg2 = !!silly_lock; + + status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 4, &input, &res); + + if (status != AE_OK) + return status; + + obj = (union acpi_object *)res.pointer; + if (!obj) + return AE_NULL_OBJECT; + + if (obj->type != ACPI_TYPE_INTEGER) { + kfree(obj); + return AE_TYPE; + } + kfree(obj); + return status; +} + +static int smread_s16(u8 hi_addr, u8 lo_addr) +{ + s16 ret = -1; + acpi_status status; + u8 r; + + /* Keep some ACPI routines off the SMBUS */ + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + status = oqo_smbus_getb(hi_addr, &r); + if (ACPI_FAILURE(status)) + goto skip; + + ret = r; + ret <<= 8; + + status = oqo_smbus_getb(lo_addr, &r); + if (ACPI_FAILURE(status)) { + ret = -1; + goto skip; + } + + ret |= r; + ret &= 0x7fff; +skip: + /* Let ACPI routines back on the SMBUS */ + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return (int)ret; +} + +static int smwrite_s16(u8 hi_addr, u8 lo_addr, s16 val) +{ + acpi_status status; + u8 r; + int ret = -1; + + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + r = (val >> 8) & 0x7f; + status = oqo_smbus_setb(hi_addr, r); + if (ACPI_FAILURE(status)) + goto skip; + + r = val & 0xff; + status = oqo_smbus_setb(lo_addr, r); + if (ACPI_FAILURE(status)) + goto skip; + + ret = 0; +skip: + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return ret; +} + +static int smread_u8(u8 addr) +{ + int ret = -1; + acpi_status status; + u8 r; + + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + status = oqo_smbus_getb(addr, &r); + if (ACPI_FAILURE(status)) + goto skip; + + ret = r; +skip: + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return (int)ret; +} + +static int smwrite_u8(u8 addr, u8 val) +{ + acpi_status status; + int ret = -1; + + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + status = oqo_smbus_setb(addr, val); + if (ACPI_FAILURE(status)) + goto skip; + + ret = 0; +skip: + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return ret; +} + +/* + * Accelerometer inputdev + */ + +/* + * Get a reading of the accelerometer from the firwmware and push + * it to an inputdev. + * + * Also the ambient light detector hitch-hikes on the inputdev, since + * it could be useful in some of the same applications for accelerometers. + * + * Available information and a bit of poking have not found a + * way to freeze a snapshot of the accelerometer data, so we have + * to do consistency checks to reduce the odds that we mix low + * and high bytes from different updates. + * + * Unfortunately SMBUS access is very slow (11ms) and the firmware API + * does not provide 2-byte transfers, so mixed readings happen and + * have to be corrected a lot. (Do not know why; it should be a + * multi-kHz.. bus and the reads take only a hundred-ish cycles/byte. + * It is not the ACPI function -- it is slow on i2c-viapro as well.) + * + * Since there is such a big time lag between readings, the axis + * are decoupled and reported separately on different timelines as + * different events rather than as a set. + */ +static acpi_status oqo_read_kine(int *good, s16 *x, s16 *y, s16 *z, + u16 *lumin) +{ + u8 hiregs[4] = { OQO_O2_SMB0_ACCEL_XHI, + OQO_O2_SMB0_ACCEL_YHI, + OQO_O2_SMB0_ACCEL_ZHI, + OQO_O2_SMB0_LUMIN_HI + }; + u8 loregs[4] = { OQO_O2_SMB0_ACCEL_XLO, + OQO_O2_SMB0_ACCEL_YLO, + OQO_O2_SMB0_ACCEL_ZLO, + OQO_O2_SMB0_LUMIN_LO + }; + + short ax[4] = { ABS_X, ABS_Y, ABS_Z, ABS_MISC }; + u8 realgood = 0; + u16 res[4]; + acpi_status status; + int i; + + *good = 0; + + /* Routine: Starting with the lo byte, read lo/hi bytes + alternately until two lo byte readings, match. Then + take that reading and combine it with the hi reading + sandwiched between. Errors can still happen when + jittering at wrap boundaries, but should be rare. + + Don't use this for missile guidance. + + Userspace post-processing error detection encouraged. + */ + for (i = 0; i < 4; i++) { + int maxtry; + u32 log; + u8 r, lo, hi; + + lo = loregs[i]; + hi = hiregs[i]; + log = 0; + +#define LOGRES(reg) do { \ + status = oqo_smbus_getb(reg, &r); \ + log <<= 8; log |= r; log &= 0xffffff; \ + if (ACPI_FAILURE(status)) \ + goto leave; \ + } while (0) + + maxtry = OQO_KINE_MAXTRY + 1; + while (maxtry) { + LOGRES(lo); + if (maxtry <= OQO_KINE_MAXTRY && + (log >> 16) == (log & 0xff)) { + *(res + i) = log & 0xffff; + break; + } + LOGRES(hi); + maxtry--; + } + + if (maxtry == OQO_KINE_MAXTRY) + realgood |= 1 << i; + + if (maxtry) { + *good |= 1 << i; + /* JIC CYA: this bit may be reserved */ + res[3] &= 0x7fff; + input_report_abs(oqo_kine, ax[i], (s16) res[i]); + } + /* else we had trouble getting the reading to lock + and we skip reporting this axis. + */ + } + + *x = (u16) res[0]; + *y = (u16) res[1]; + *z = (u16) res[2]; + *lumin = (u16) res[3]; + return status; +leave: + return status; +} + +/* + * Generic Device (interface-independent) + */ + +static void oqo_kine_poll(struct input_polled_dev *dev) +{ + s16 x, y, z; + u16 lumin; + int good; + /* struct timeval tv1, tv2; */ + + if (dev != oqo_kine_polled) + return; + if (orig.kine_itvl < 0) + return; + + x = y = z = 0; + oqo_read_kine(&good, &x, &y, &z, &lumin); +} + +static int __devinit oqo_kine_init(void) +{ + int err; + + oqo_kine = input_allocate_device(); + if (!oqo_kine) + return -ENOMEM; + + oqo_kine->name = "OQO embedded accelerometer"; + oqo_kine->phys = "platform:oqo-wmi:kine"; + oqo_kine->id.bustype = 0; + oqo_kine->id.vendor = 0; + oqo_kine->id.product = 2; + oqo_kine->id.version = 0; + oqo_kine->evbit[0] = BIT_MASK(EV_ABS); + set_bit(ABS_X, oqo_kine->absbit); + set_bit(ABS_Y, oqo_kine->absbit); + set_bit(ABS_Z, oqo_kine->absbit); + set_bit(ABS_MISC, oqo_kine->absbit); + oqo_kine->absmin[ABS_X] = + oqo_kine->absmin[ABS_Y] = + oqo_kine->absmin[ABS_Z] = oqo_kine->absmin[ABS_MISC] = -32768; + oqo_kine->absmax[ABS_X] = + oqo_kine->absmax[ABS_Y] = + oqo_kine->absmax[ABS_Z] = oqo_kine->absmax[ABS_MISC] = 32767; + + dev_set_name(&oqo_kine->dev, "kine"); + + oqo_kine_polled = input_allocate_polled_device(); + if (!oqo_kine_polled) { + err = -ENOMEM; + goto bail0; + } + + oqo_kine_polled->poll = oqo_kine_poll; + oqo_kine_polled->poll_interval = 250; + oqo_kine_polled->input = oqo_kine; + + orig.kine_itvl = -1; /* prevent callback from running */ + err = input_register_polled_device(oqo_kine_polled); + if (err) { + printk(OQO_ERR "Failed to register OQO kine input\n"); + goto bail1; + } + + /* This will allow the callback to run now if successful. */ + orig.kine_itvl = smread_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL); + smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, 250); + curr.kine_itvl = smread_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL); + if (orig.kine_itvl < 0 || curr.kine_itvl != 250) { + printk(OQO_ERR "Test communication with kine sensor failed\n"); + err = -ENODEV; + goto bail2; + } + + printk(OQO_INFO "Created OQO kine input.\n"); + printk(OQO_INFO "Firmware interval %ims, driver interval %ims\n", + curr.kine_itvl, oqo_kine_polled->poll_interval); + return 0; +bail2: + input_unregister_polled_device(oqo_kine_polled); +bail1: + input_free_polled_device(oqo_kine_polled); /* frees oqo_kine */ + return err; +bail0: + input_free_device(oqo_kine); + return err; +} + +static void __devexit oqo_kine_fini(void) +{ + smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, orig.kine_itvl); + input_unregister_polled_device(oqo_kine_polled); + input_free_polled_device(oqo_kine_polled); +} + +/* + * Backlight device + */ +static int read_brightness(struct backlight_device *bd) +{ + return (int)smread_s16(OQO_O2_SMB0_BL_HI, OQO_O2_SMB0_BL_LO); +} + +static int update_bl_status(struct backlight_device *bd) +{ + return smwrite_s16(OQO_O2_SMB0_BL_HI, + OQO_O2_SMB0_BL_LO, (s16) bd->props.brightness); +} + +static struct backlight_ops oqo_bl_ops = { + .get_brightness = read_brightness, + .update_status = update_bl_status, +}; + +static int __devinit oqo_backlight_init(struct device *dev) +{ + struct backlight_device *bd; + + /* + * It would be nice if someone would figure out how backlights + * like these, which are not driven through the video hardware, + * are supposed to find their associated fb and bind to it (and + * rebind when fb drivers change. + * + * Most extras backlights just shove a junk name in like we do here, + * and don't end up integrated with fbcon sysfs as a result. + */ + bd = backlight_device_register("oqo-bl", dev, NULL, &oqo_bl_ops); + + if (IS_ERR(bd)) { + printk(OQO_ERR "Could not register OQO backlight device\n"); + oqo_backlight_device = NULL; + return PTR_ERR(bd); + } + + oqo_backlight_device = bd; + bd->props.max_brightness = 0x7fff; + curr.bl_bright = orig.bl_bright = bd->props.brightness = + read_brightness(NULL); + + if (bd->props.brightness < 0) + goto fail; + + backlight_update_status(bd); + printk(OQO_INFO "Found backlight set at %i\n", bd->props.brightness); + return 0; + +fail: + backlight_device_unregister(oqo_backlight_device); + oqo_backlight_device = NULL; + return -ENODEV; +} + +static void __devexit oqo_backlight_fini(void) +{ + if (!oqo_backlight_device) + return; + oqo_backlight_device->props.brightness = orig.bl_bright; + backlight_update_status(oqo_backlight_device); + backlight_device_unregister(oqo_backlight_device); +} + +/* + * RFKill device + */ + +static int oqo_rfkill_get(void *data, enum rfkill_state *state) +{ + int res; + + res = smread_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR); + if (res < 0) + return res; + + res &= OQO_O2_SMB0_WWAN_DSBL_MASK; + + if (res) + *state = RFKILL_STATE_SOFT_BLOCKED; + else + *state = RFKILL_STATE_UNBLOCKED; + + return 0; +} + +static int oqo_rfkill_toggle(void *data, enum rfkill_state state) +{ + int res; + + res = smread_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR); + + if (state == RFKILL_STATE_UNBLOCKED) + res &= ~OQO_O2_SMB0_WWAN_DSBL_MASK; + else + res |= OQO_O2_SMB0_WWAN_DSBL_MASK; + + return smwrite_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR, res); +} + +static int __devinit oqo_rfkill_init(struct device *dev) +{ + int res; + + oqo_rfkill = rfkill_allocate(dev, RFKILL_TYPE_WWAN); + if (!oqo_rfkill) + return -ENODEV; + + res = smread_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR); + res &= OQO_O2_SMB0_WWAN_DSBL_MASK; + + oqo_rfkill->name = "oqo-wwan"; + if (res) + oqo_rfkill->state = RFKILL_STATE_SOFT_BLOCKED; + else + oqo_rfkill->state = RFKILL_STATE_UNBLOCKED; + + oqo_rfkill->get_state = oqo_rfkill_get; + oqo_rfkill->toggle_radio = oqo_rfkill_toggle; + oqo_rfkill->user_claim_unsupported = 1; + + res = rfkill_register(oqo_rfkill); + + if (res) + rfkill_free(oqo_rfkill); + + return res; +} + +static void __devexit oqo_rfkill_fini(void) +{ + if (!oqo_rfkill) + return; + rfkill_unregister(oqo_rfkill); +} + +/* + * Platform device + */ + +static int __devinit oqo_platform_probe(struct platform_device *device) +{ + int err; + int i; + char *troubleok = "trouble, but continuing.\n"; + + memset(oqo_sn, 0, OQO_O2_SMB0_SERIAL_LEN + 1); + for (i = 0; i < OQO_O2_SMB0_SERIAL_LEN; i++) { + err = oqo_smbus_getb(OQO_O2_SMB0_SERIAL_START + i, oqo_sn + i); + if (err) { + printk(OQO_ERR "Serial number check failed.\n"); + return err; + } + } + printk(OQO_INFO "Found OQO with serial number %s.\n", oqo_sn); + + err = oqo_backlight_init(&device->dev); + if (err) + printk(OQO_ERR "Backlight init %s", troubleok); + + err = oqo_rfkill_init(&device->dev); + if (err) + printk(OQO_ERR "RFKill init %s", troubleok); + + /* LID does not work at all yet, and this may be taken + care of by ACPI. + */ + orig.lid_wakes = smread_u8(OQO_O2_SMB0_LID_WAKES_ADDR); + orig.lid_wakes &= OQO_O2_SMB0_LID_WAKES_MASK; + orig.lid_wakes = curr.lid_wakes = !!orig.lid_wakes; + if (orig.lid_wakes < 0) { + printk(OQO_ERR "Wake on LID event %s", troubleok); + } else { + printk(OQO_INFO "Wake on LID is %s.\n", + (orig.lid_wakes ? "on" : "off")); + } + + err = oqo_kine_init(); + return err; +} + +static int oqo_platform_remove(struct platform_device *device) +{ + oqo_backlight_fini(); + oqo_rfkill_fini(); + oqo_kine_fini(); + + return 0; +} + +#ifdef CONFIG_PM + +static int oqo_platform_suspend(struct platform_device *dev, pm_message_t state) +{ + if (!interface) + return -ENOMEM; + + /* This sticks during boot so do not turn it entirely off */ + if (oqo_backlight_device) { + curr.bl_bright = read_brightness(oqo_backlight_device); + smwrite_s16(OQO_O2_SMB0_BL_HI, OQO_O2_SMB0_BL_LO, 256); + } + return 0; +} + +static int oqo_platform_resume(struct platform_device *device) +{ + if (!interface) + return -ENOMEM; + + if (oqo_backlight_device) { + smwrite_s16(OQO_O2_SMB0_BL_HI, + OQO_O2_SMB0_BL_LO, curr.bl_bright); + } + + return 0; +} + +#else +#define oqo_platform_suspend NULL +#define oqo_platform_resume NULL +#endif + +static struct platform_driver oqo_platform_driver = { + .driver = { + .name = "oqo-wmi", + .owner = THIS_MODULE, + }, + .probe = oqo_platform_probe, + .remove = oqo_platform_remove, + .suspend = oqo_platform_suspend, + .resume = oqo_platform_resume, +}; + +static int __init oqo_wmi_init(void) +{ + int err; + + dmi_check_system(oqo_dmis); + + if (!interface && force) { + model = oqo_models; + if (wmi_has_guid(OQO_O2_AMW0_GUID)) + interface = &AMW0_interface; + } + + if (!interface) { + printk(OQO_ERR "No or unsupported WMI interface. Aborting.\n"); + printk(OQO_ERR "Hint: Get dmidecode working and try again.\n"); + printk(OQO_ERR "(Check \"System Management BIOS\" in BIOS)\n"); + if (!force) + printk(OQO_ERR "Use the force option to skip DMI" + " checking\n"); + return -ENODEV; + } + + err = platform_driver_register(&oqo_platform_driver); + if (err) { + printk(OQO_ERR "platform_driver_register gave %d.\n", err); + goto bail0; + } + + oqo_platform_device = platform_device_alloc("oqo-wmi", -1); + if (!oqo_platform_device) { + printk(OQO_ERR "Could not allocate platform device.\n"); + err = -ENOMEM; + goto bail1; + } + + err = platform_device_add(oqo_platform_device); + if (err) { + printk(OQO_ERR "platform_device_add gave %d.\n", err); + platform_device_put(oqo_platform_device); + goto bail1; + } + + return 0; + +bail1: + platform_driver_unregister(&oqo_platform_driver); +bail0: + return err; +} + +static void __exit oqo_wmi_fini(void) +{ + platform_device_del(oqo_platform_device); + platform_driver_unregister(&oqo_platform_driver); + + return; +} + +module_init(oqo_wmi_init); +module_exit(oqo_wmi_fini); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 9f187265db8e..0c6cd7c654da 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -46,6 +46,7 @@ #include <linux/platform_device.h> #include <linux/rfkill.h> #include <linux/input-polldev.h> +#include <linux/input.h> #include <asm/uaccess.h> @@ -62,9 +63,10 @@ MODULE_LICENSE("GPL"); /* Toshiba ACPI method paths */ #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" -#define METHOD_HCI_1 "\\_SB_.VALD.GHCI" -#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" +#define TOSH_INTERFACE_1 "\\_SB_.VALD" +#define TOSH_INTERFACE_2 "\\_SB_.VALZ" #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" +#define GHCI_METHOD ".GHCI" /* Toshiba HCI interface definitions * @@ -116,6 +118,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); +struct key_entry { + char type; + u16 code; + u16 keycode; +}; + +enum {KE_KEY, KE_END}; + +static struct key_entry toshiba_acpi_keymap[] = { + {KE_KEY, 0x101, KEY_MUTE}, + {KE_KEY, 0x13b, KEY_COFFEE}, + {KE_KEY, 0x13c, KEY_BATTERY}, + {KE_KEY, 0x13d, KEY_SLEEP}, + {KE_KEY, 0x13e, KEY_SUSPEND}, + {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x142, KEY_WLAN}, + {KE_KEY, 0x143, KEY_PROG1}, + {KE_KEY, 0xb05, KEY_PROG2}, + {KE_KEY, 0xb06, KEY_WWW}, + {KE_KEY, 0xb07, KEY_MAIL}, + {KE_KEY, 0xb30, KEY_STOP}, + {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, + {KE_KEY, 0xb32, KEY_NEXTSONG}, + {KE_KEY, 0xb33, KEY_PLAYPAUSE}, + {KE_KEY, 0xb5a, KEY_MEDIA}, + {KE_END, 0, 0}, +}; + /* utility */ @@ -252,6 +284,8 @@ struct toshiba_acpi_dev { struct platform_device *p_dev; struct rfkill *rfk_dev; struct input_polled_dev *poll_dev; + struct input_dev *hotkey_dev; + acpi_handle handle; const char *bt_name; const char *rfk_name; @@ -700,6 +734,154 @@ static struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; +static struct key_entry *toshiba_acpi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *toshiba_acpi_get_entry_by_keycode(int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; + + return NULL; +} + +static int toshiba_acpi_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int toshiba_acpi_setkeycode(struct input_dev *dev, int scancode, + int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = toshiba_acpi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) +{ + u32 hci_result, value; + struct key_entry *key; + + if (event != 0x80) + return; + do { + hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + if (value == 0x100) + continue; + else if (value & 0x80) { + key = toshiba_acpi_get_entry_by_scancode + (value & ~0x80); + if (!key) { + printk(MY_INFO "Unknown key %x\n", + value & ~0x80); + continue; + } + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 1); + input_sync(toshiba_acpi.hotkey_dev); + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 0); + input_sync(toshiba_acpi.hotkey_dev); + } + } else if (hci_result == HCI_NOT_SUPPORTED) { + /* This is a workaround for an unresolved issue on + * some machines where system events sporadically + * become disabled. */ + hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + printk(MY_NOTICE "Re-enabled hotkeys\n"); + } + } while (hci_result != HCI_EMPTY); +} + +static int toshiba_acpi_setup_keyboard(char *device) +{ + acpi_status status; + acpi_handle handle; + int result; + const struct key_entry *key; + + status = acpi_get_handle(NULL, device, &handle); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to get notification device\n"); + return -ENODEV; + } + + toshiba_acpi.handle = handle; + + status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to enable hotkeys\n"); + return -ENODEV; + } + + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to install hotkey notification\n"); + return -ENODEV; + } + + toshiba_acpi.hotkey_dev = input_allocate_device(); + if (!toshiba_acpi.hotkey_dev) { + printk(MY_INFO "Unable to register input device\n"); + return -ENOMEM; + } + + toshiba_acpi.hotkey_dev->name = "Toshiba input device"; + toshiba_acpi.hotkey_dev->phys = device; + toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; + toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; + toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { + set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); + set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); + } + + result = input_register_device(toshiba_acpi.hotkey_dev); + if (result) { + printk(MY_INFO "Unable to register input device\n"); + return result; + } + + return 0; +} + static void toshiba_acpi_exit(void) { if (toshiba_acpi.poll_dev) { @@ -707,12 +889,18 @@ static void toshiba_acpi_exit(void) input_free_polled_device(toshiba_acpi.poll_dev); } + if (toshiba_acpi.hotkey_dev) + input_unregister_device(toshiba_acpi.hotkey_dev); + if (toshiba_acpi.rfk_dev) rfkill_unregister(toshiba_acpi.rfk_dev); if (toshiba_backlight_device) backlight_device_unregister(toshiba_backlight_device); + acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify); + remove_device(); if (toshiba_proc_dir) @@ -736,11 +924,15 @@ static int __init toshiba_acpi_init(void) return -ENODEV; /* simple device detection: look for HCI method */ - if (is_valid_acpi_path(METHOD_HCI_1)) - method_hci = METHOD_HCI_1; - else if (is_valid_acpi_path(METHOD_HCI_2)) - method_hci = METHOD_HCI_2; - else + if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { + method_hci = TOSH_INTERFACE_1 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) + printk(MY_INFO "Unable to activate hotkeys\n"); + } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { + method_hci = TOSH_INTERFACE_2 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) + printk(MY_INFO "Unable to activate hotkeys\n"); + } else return -ENODEV; printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", |