From 4857a94de17a3cffeb71c51486aed1db5de594a6 Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Thu, 4 Aug 2016 16:42:19 +0800 Subject: ACPICA: Fix deconstification warnings (-Wcast-qual) with function traces. ACPICA commit f722da0372261331b74d3ac67645bba912a21643 Link: https://github.com/acpica/acpica/commit/f722da03 Signed-off-by: Jung-uk Kim Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index a7dbb2b..0bb6325 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -318,6 +318,11 @@ acpi_ut_ptr_exit(u32 line_number, const char *module_name, u32 component_id, u8 *ptr); void +acpi_ut_str_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, const char *string); + +void acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id); void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 offset); diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 5744222..84c0853 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -562,6 +562,43 @@ acpi_ut_ptr_exit(u32 line_number, /******************************************************************************* * + * FUNCTION: acpi_ut_str_exit + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * string - String to display + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit value also. + * + ******************************************************************************/ + +void +acpi_ut_str_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, const char *string) +{ + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %s\n", + acpi_gbl_function_exit_prefix, string); + } + + if (acpi_gbl_nesting_level) { + acpi_gbl_nesting_level--; + } +} + +/******************************************************************************* + * * FUNCTION: acpi_trace_point * * PARAMETERS: type - Trace event type diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index efd7988..15728ad 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -253,7 +253,7 @@ const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc) return_PTR("Invalid object"); } - return_PTR(acpi_ut_get_type_name(obj_desc->common.type)); + return_STR(acpi_ut_get_type_name(obj_desc->common.type)); } /******************************************************************************* diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index 34f601e..48eb4dd 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -366,7 +366,7 @@ ACPI_TRACE_ENTRY (name, acpi_ut_trace_u32, u32, value) #define ACPI_FUNCTION_TRACE_STR(name, string) \ - ACPI_TRACE_ENTRY (name, acpi_ut_trace_str, char *, string) + ACPI_TRACE_ENTRY (name, acpi_ut_trace_str, const char *, string) #define ACPI_FUNCTION_ENTRY() \ acpi_ut_track_stack_ptr() @@ -425,6 +425,9 @@ #define return_PTR(pointer) \ ACPI_TRACE_EXIT (acpi_ut_ptr_exit, void *, pointer) +#define return_STR(string) \ + ACPI_TRACE_EXIT (acpi_ut_str_exit, const char *, string) + #define return_VALUE(value) \ ACPI_TRACE_EXIT (acpi_ut_value_exit, u64, value) @@ -478,6 +481,7 @@ #define return_VOID return #define return_ACPI_STATUS(s) return(s) #define return_PTR(s) return(s) +#define return_STR(s) return(s) #define return_VALUE(s) return(s) #define return_UINT8(s) return(s) #define return_UINT32(s) return(s) -- cgit v0.10.2 From fee4ab9c0cf223e9f0f2d9d2e5a2f24f6539ccbd Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Thu, 4 Aug 2016 16:42:34 +0800 Subject: ACPICA: Fix deconstification warnings (-Wcast-qual) with acpi_ns_root_initialize(). ACPICA commit 8b3b57c9d11d9c322e09cb06bedac7aa783458fd Link: https://github.com/acpica/acpica/commit/8b3b57c9 Signed-off-by: Jung-uk Kim Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index 426a630..73f98d3 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -108,9 +108,9 @@ acpi_status acpi_ns_root_initialize(void) } status = - acpi_ns_lookup(NULL, (char *)init_val->name, init_val->type, - ACPI_IMODE_LOAD_PASS2, ACPI_NS_NO_UPSEARCH, - NULL, &new_node); + acpi_ns_lookup(NULL, ACPI_CAST_PTR(char, init_val->name), + init_val->type, ACPI_IMODE_LOAD_PASS2, + ACPI_NS_NO_UPSEARCH, NULL, &new_node); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Could not create predefined name %s", -- cgit v0.10.2 From 7fdb5cea9bf288eb6408538fa67f1559f433ab9d Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:42:42 +0800 Subject: ACPICA: Interpreter: Remove temporary code for External() opcode ACPICA commit f2d349f8a11efc0f438ad6903564f3a6755dc6b9 The interpreter should never see this opcode (it is used by disassemblers), so the final implementation is to return an error. Link: https://github.com/acpica/acpica/commit/f2d349f8 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c index f393de9..7d8ef52 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -565,15 +565,14 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state, status = AE_OK; } else if (parent_op->common.aml_opcode == AML_EXTERNAL_OP) { - - /* TBD: May only be temporary */ - - obj_desc = - acpi_ut_create_string_object((acpi_size)name_length); - - strncpy(obj_desc->string.pointer, - name_string, name_length); - status = AE_OK; + /* + * This opcode should never appear here. It is used only + * by AML disassemblers and is surrounded by an If(0) + * by the ASL compiler. + * + * Therefore, if we see it here, it is a serious error. + */ + status = AE_AML_BAD_OPCODE; } else { /* * We just plain didn't find it -- which is a -- cgit v0.10.2 From b5c0875a16039d90f4cdf6b75ae4031daae01d56 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:42:49 +0800 Subject: ACPICA: Utilities: Introduce facility to allow Linux to set correct logging levels ACPICA commit 58c9e7b83ae35247e430c39363f55b6f70fa04a2 It is reported that the logging level of the ACPICA messages are not correct in the Linux kernel. This patch fixes this issue. Lv Zheng. Link: https://github.com/acpica/acpica/commit/58c9e7b8 Link: https://bugzilla.kernel.org/show_bug.cgi?id=117461 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 0bb6325..91269a6 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -114,13 +114,25 @@ extern const char *acpi_gbl_pt_decode[]; /* * Common error message prefixes */ +#ifndef ACPI_MSG_ERROR #define ACPI_MSG_ERROR "ACPI Error: " +#endif +#ifndef ACPI_MSG_EXCEPTION #define ACPI_MSG_EXCEPTION "ACPI Exception: " +#endif +#ifndef ACPI_MSG_WARNING #define ACPI_MSG_WARNING "ACPI Warning: " +#endif +#ifndef ACPI_MSG_INFO #define ACPI_MSG_INFO "ACPI: " +#endif +#ifndef ACPI_MSG_BIOS_ERROR #define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): " +#endif +#ifndef ACPI_MSG_BIOS_WARNING #define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): " +#endif /* * Common message suffix diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 93b61b1..296a276 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -168,6 +168,14 @@ #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_next_filename #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_close_directory +#define ACPI_MSG_ERROR KERN_ERR "ACPI Error: " +#define ACPI_MSG_EXCEPTION KERN_ERR "ACPI Exception: " +#define ACPI_MSG_WARNING KERN_WARNING "ACPI Warning: " +#define ACPI_MSG_INFO KERN_INFO "ACPI: " + +#define ACPI_MSG_BIOS_ERROR KERN_ERR "ACPI BIOS Error (bug): " +#define ACPI_MSG_BIOS_WARNING KERN_WARNING "ACPI BIOS Warning (bug): " + #else /* !__KERNEL__ */ #include -- cgit v0.10.2 From 60d836fcdf7cc38f1408773d36cd9b6b6d698097 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:42:55 +0800 Subject: ACPICA: Debugger: Extend some max line lengths ACPICA commit 622063bae684490191c8e8b10bf18e86d0ab4ebf Fix a couple of arbitrarily small output line lengths. Link: https://github.com/acpica/acpica/commit/622063ba Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dbobject.c b/drivers/acpi/acpica/dbobject.c index 1d59e8b..08eaaf3 100644 --- a/drivers/acpi/acpica/dbobject.c +++ b/drivers/acpi/acpica/dbobject.c @@ -142,11 +142,11 @@ void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc) case ACPI_TYPE_STRING: - acpi_os_printf("(%u) \"%.24s", + acpi_os_printf("(%u) \"%.60s", obj_desc->string.length, obj_desc->string.pointer); - if (obj_desc->string.length > 24) { + if (obj_desc->string.length > 60) { acpi_os_printf("..."); } else { acpi_os_printf("\""); diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index ce1f860..84f35dd 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -338,7 +338,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle, case ACPI_TYPE_STRING: acpi_os_printf("Len %.2X ", obj_desc->string.length); - acpi_ut_print_string(obj_desc->string.pointer, 32); + acpi_ut_print_string(obj_desc->string.pointer, 80); acpi_os_printf("\n"); break; -- cgit v0.10.2 From 7c312ad1f28030c3a95b0de087bf52c45c16a0db Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:43:02 +0800 Subject: ACPICA: Disassembler: Add option to emit embedded External operators/opcodes ACPICA commit 152a8ca2c7fc877d6aff0f9d0965184ef2ddce5c Opcode 0x15 was added in ACPI 6.0 for disassemblers. The disassembler by default does not emit the actual opcodes, they are used internally. Option added for internal debugging only. This patch doesn't affect Linux kernel as disassembler is not in the Linux kernel. Link: https://github.com/acpica/acpica/commit/152a8ca2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index fded776..8c2c504 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -317,6 +317,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE); ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_opt_verbose, TRUE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_emit_external_opcodes, FALSE); ACPI_GLOBAL(u8, acpi_gbl_dm_opt_disasm); ACPI_GLOBAL(u8, acpi_gbl_dm_opt_listing); -- cgit v0.10.2 From 02dcdc3e860ac21f5fb3a9a30a5073267f74e596 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:43:13 +0800 Subject: ACPICA: Simplify configuration for "Max Loops" system parameter ACPICA commit 857c510d70e18eecc275dd3087807a18bae8aa51 Allow for static configuration of this parameter. It is used to abort out of infinite loops caused by non-response from hardware. Link: https://github.com/acpica/acpica/commit/857c510d Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index f91f724..1711fdf 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -206,7 +206,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_next_owner_id_offset = 0; acpi_gbl_debugger_configuration = DEBUGGER_THREADING; acpi_gbl_osi_mutex = NULL; - acpi_gbl_max_loop_iterations = 0xFFFF; + acpi_gbl_max_loop_iterations = ACPI_MAX_LOOP_COUNT; /* Hardware oriented */ diff --git a/include/acpi/acconfig.h b/include/acpi/acconfig.h index fe2e3ac..12c2882 100644 --- a/include/acpi/acconfig.h +++ b/include/acpi/acconfig.h @@ -144,6 +144,10 @@ #define ACPI_ADDRESS_RANGE_MAX 2 +/* Maximum number of While() loops before abort */ + +#define ACPI_MAX_LOOP_COUNT 0xFFFF + /****************************************************************************** * * ACPI Specification constants (Do not change unless the specification changes) -- cgit v0.10.2 From 2368b1a17c95e5952d0aa4706f4fc7a32ebe8828 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:43:19 +0800 Subject: ACPICA: Divergence: Port declarators back to ACPICA ACPICA commit c160cae765412f5736cf88a9ebcc6138aa761a48 Linux uses asmlinkage and sparse macros to mark function symbols. This leads to the divergences between the Linux and the ACPICA. This patch ports such declarators back to ACPICA. Lv Zheng. Link: https://github.com/acpica/acpica/commit/c160cae7 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index cd5a135..86d4d62 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -159,7 +159,8 @@ acpi_status acpi_tb_install_fixed_table(acpi_physical_address address, char *signature, u32 *table_index); -acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address); +acpi_status ACPI_INIT_FUNCTION +acpi_tb_parse_root_table(acpi_physical_address rsdp_address); /* * tbxfload diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index e285539..51eb07c 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -252,7 +252,8 @@ acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size) * ******************************************************************************/ -acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) +acpi_status ACPI_INIT_FUNCTION +acpi_tb_parse_root_table(acpi_physical_address rsdp_address) { struct acpi_table_rsdp *rsdp; u32 table_entry_size; diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 3ecec93..4ab6b9c 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -98,7 +98,7 @@ acpi_status acpi_allocate_root_table(u32 initial_table_count) * ******************************************************************************/ -acpi_status __init +acpi_status ACPI_INIT_FUNCTION acpi_initialize_tables(struct acpi_table_desc *initial_table_array, u32 initial_table_count, u8 allow_resize) { @@ -164,7 +164,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_tables) * kernel. * ******************************************************************************/ -acpi_status __init acpi_reallocate_root_table(void) +acpi_status ACPI_INIT_FUNCTION acpi_reallocate_root_table(void) { acpi_status status; diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index ac71abc..e0cc919 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -63,7 +63,7 @@ ACPI_MODULE_NAME("tbxfload") * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT * ******************************************************************************/ -acpi_status __init acpi_load_tables(void) +acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void) { acpi_status status; @@ -272,7 +272,7 @@ unlock_and_exit: * ******************************************************************************/ -acpi_status __init +acpi_status ACPI_INIT_FUNCTION acpi_install_table(acpi_physical_address address, u8 physical) { acpi_status status; diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index adb6cfc..0adb1c7 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -142,7 +142,8 @@ acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp) * ******************************************************************************/ -acpi_status __init acpi_find_root_pointer(acpi_physical_address *table_address) +acpi_status ACPI_INIT_FUNCTION +acpi_find_root_pointer(acpi_physical_address *table_address) { u8 *table_ptr; u8 *mem_rover; @@ -244,6 +245,8 @@ acpi_status __init acpi_find_root_pointer(acpi_physical_address *table_address) return_ACPI_STATUS(AE_NOT_FOUND); } +ACPI_EXPORT_SYMBOL_INIT(acpi_find_root_pointer) + /******************************************************************************* * * FUNCTION: acpi_tb_scan_memory_for_rsdp diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index d9e6aac..ec503c8 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -61,7 +61,7 @@ ACPI_MODULE_NAME("utxface") * DESCRIPTION: Shutdown the ACPICA subsystem and release all resources. * ******************************************************************************/ -acpi_status __init acpi_terminate(void) +acpi_status ACPI_INIT_FUNCTION acpi_terminate(void) { acpi_status status; diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c index 75b5f27..d6d9462 100644 --- a/drivers/acpi/acpica/utxfinit.c +++ b/drivers/acpi/acpica/utxfinit.c @@ -69,7 +69,7 @@ void ae_do_object_overrides(void); * ******************************************************************************/ -acpi_status __init acpi_initialize_subsystem(void) +acpi_status ACPI_INIT_FUNCTION acpi_initialize_subsystem(void) { acpi_status status; @@ -141,7 +141,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_subsystem) * Puts system into ACPI mode if it isn't already. * ******************************************************************************/ -acpi_status __init acpi_enable_subsystem(u32 flags) +acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags) { acpi_status status = AE_OK; @@ -239,7 +239,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_enable_subsystem) * objects and executing AML code for Regions, buffers, etc. * ******************************************************************************/ -acpi_status __init acpi_initialize_objects(u32 flags) +acpi_status ACPI_INIT_FUNCTION acpi_initialize_objects(u32 flags) { acpi_status status = AE_OK; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 1ff3a76..875964d 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -416,18 +416,19 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); /* * Initialization */ -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION acpi_initialize_tables(struct acpi_table_desc *initial_storage, u32 initial_table_count, u8 allow_resize)) -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_initialize_subsystem(void)) - -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_enable_subsystem(u32 flags)) - -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init - acpi_initialize_objects(u32 flags)) -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_terminate(void)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION + acpi_initialize_subsystem(void)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION + acpi_enable_subsystem(u32 flags)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION + acpi_initialize_objects(u32 flags)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION + acpi_terminate(void)) /* * Miscellaneous global interfaces @@ -467,7 +468,7 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status /* * ACPI table load/unload interfaces */ -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION acpi_install_table(acpi_physical_address address, u8 physical)) @@ -476,14 +477,17 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_unload_parent_table(acpi_handle object)) -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_load_tables(void)) + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION + acpi_load_tables(void)) /* * ACPI table manipulation interfaces */ -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_reallocate_root_table(void)) +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION + acpi_reallocate_root_table(void)) -ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init +ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION acpi_find_root_pointer(acpi_physical_address *rsdp_address)) ACPI_EXTERNAL_RETURN_STATUS(acpi_status diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 86b5a84..ed959e5 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -403,4 +403,8 @@ typedef char *va_list; #endif /* ACPI_APPLICATION */ #endif /* ACPI_FILE */ +#ifndef ACPI_INIT_FUNCTION +#define ACPI_INIT_FUNCTION +#endif + #endif /* __ACENV_H__ */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 296a276..cde6f33 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -92,6 +92,8 @@ #include #endif +#define ACPI_INIT_FUNCTION __init + #ifndef CONFIG_ACPI /* External globals for __KERNEL__, stubs is needed */ diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h index f8bb0d8..a5509d8 100644 --- a/include/acpi/platform/aclinuxex.h +++ b/include/acpi/platform/aclinuxex.h @@ -71,7 +71,7 @@ /* * Overrides for in-kernel ACPICA */ -acpi_status __init acpi_os_initialize(void); +acpi_status ACPI_INIT_FUNCTION acpi_os_initialize(void); acpi_status acpi_os_terminate(void); -- cgit v0.10.2 From d8303ace36aaa001e1704acb2bd13dd4f08a0d67 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:43:26 +0800 Subject: ACPICA: iASL/Disassembler: Add a check for missing filename ACPICA commit fc0f12b1eff6253f83e599a7ee1765fcc8e42dcc Add check for required filename for the -d and -da options. ACPICA BZ 1285. Link: https://github.com/acpica/acpica/commit/fc0f12b1 Link: https://bugs.acpica.org/show_bug.cgi?id=1285 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c index 0bd343f..e9e8091 100644 --- a/tools/power/acpi/common/getopt.c +++ b/tools/power/acpi/common/getopt.c @@ -94,7 +94,7 @@ int acpi_getopt_argument(int argc, char **argv) acpi_gbl_optarg = &argv[acpi_gbl_optind++][(int)(current_char_ptr + 1)]; } else if (++acpi_gbl_optind >= argc) { - ACPI_OPTION_ERROR("Option requires an argument: -", 'v'); + ACPI_OPTION_ERROR("\nOption requires an argument", 0); current_char_ptr = 1; return (-1); -- cgit v0.10.2 From 9556ec4ec1d3575090f1f110c558169c5f0973a9 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:43:32 +0800 Subject: ACPICA: Use os_allocate_zeroed ACPICA commit 2b896c59e53243c95600f2a3f7e1fd02c044cb37 Eliminates an unnecessary memset. Suggested-by: Amitoj Kaur Chawla Link: https://github.com/acpica/acpica/commit/2b896c59 Signed-off-by: Amitoj Kaur Chawla Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c index 0df07df..df31d71 100644 --- a/drivers/acpi/acpica/uttrack.c +++ b/drivers/acpi/acpica/uttrack.c @@ -95,13 +95,11 @@ acpi_ut_create_list(const char *list_name, { struct acpi_memory_list *cache; - cache = acpi_os_allocate(sizeof(struct acpi_memory_list)); + cache = acpi_os_allocate_zeroed(sizeof(struct acpi_memory_list)); if (!cache) { return (AE_NO_MEMORY); } - memset(cache, 0, sizeof(struct acpi_memory_list)); - cache->list_name = list_name; cache->object_size = object_size; -- cgit v0.10.2 From 2af52c2bd20c50e80b121e15cd50a579e364485a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:43:39 +0800 Subject: ACPICA: Events: Introduce acpi_mask_gpe() to implement GPE masking mechanism ACPICA commit 23a417ca406a527e7ae1710893e59a8b6db30e14 There is a facility in Linux, developers can control the enabling/disabling of a GPE via /sys/firmware/acpi/interrupts/gpexx. This is mainly for debugging purposes. But many users expect to use this facility to implement quirks to mask a specific GPE when there is a gap in Linux causing this GPE to flood. This is not working correctly because currently this facility invokes enabling/disabling counting based GPE driver APIs: acpi_enable_gpe()/acpi_disable_gpe() and the GPE drivers can still affect the count to mess up the GPE masking purposes. However, most of the IRQ chip designs allow masking/unmasking IRQs via a masking bit which is different from the enabled bit to achieve the same purpose. But the GPE hardware doesn't contain such a feature, this brings the trouble. In this patch, we introduce a software mechanism to implement the GPE masking feature, and acpi_mask_gpe() are provided to the OSPMs to mask/unmask GPEs in the above mentioned situation instead of acpi_enable_gpe()/acpi_disable_gpe(). ACPICA BZ 1102. Lv Zheng. Link: https://github.com/acpica/acpica/commit/23a417ca Link: https://bugs.acpica.org/show_bug.cgi?id=1102 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 77af91c..92fa47c 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -86,6 +86,9 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info); acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); acpi_status +acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked); + +acpi_status acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info); acpi_status diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 13331d7..dff1207 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -484,6 +484,7 @@ struct acpi_gpe_event_info { u8 flags; /* Misc info about this GPE */ u8 gpe_number; /* This GPE */ u8 runtime_count; /* References to a run GPE */ + u8 disable_for_dispatch; /* Masked during dispatching */ }; /* Information about a GPE register pair, one per each status/enable pair in an array */ @@ -494,6 +495,7 @@ struct acpi_gpe_register_info { u16 base_gpe_number; /* Base GPE number for this register */ u8 enable_for_wake; /* GPEs to keep enabled when sleeping */ u8 enable_for_run; /* GPEs to keep enabled when running */ + u8 mask_for_run; /* GPEs to keep masked when running */ u8 enable_mask; /* Current mask of enabled GPEs */ }; diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 4b4949c..bdb10be 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -130,6 +130,60 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) /******************************************************************************* * + * FUNCTION: acpi_ev_mask_gpe + * + * PARAMETERS: gpe_event_info - GPE to be blocked/unblocked + * is_masked - Whether the GPE is masked or not + * + * RETURN: Status + * + * DESCRIPTION: Unconditionally mask/unmask a GPE during runtime. + * + ******************************************************************************/ + +acpi_status +acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked) +{ + struct acpi_gpe_register_info *gpe_register_info; + u32 register_bit; + + ACPI_FUNCTION_TRACE(ev_mask_gpe); + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); + + /* Perform the action */ + + if (is_masked) { + if (register_bit & gpe_register_info->mask_for_run) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + ACPI_SET_BIT(gpe_register_info->mask_for_run, (u8)register_bit); + } else { + if (!(register_bit & gpe_register_info->mask_for_run)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + ACPI_CLEAR_BIT(gpe_register_info->mask_for_run, + (u8)register_bit); + if (gpe_event_info->runtime_count + && !gpe_event_info->disable_for_dispatch) { + (void)acpi_hw_low_set_gpe(gpe_event_info, + ACPI_GPE_ENABLE); + } + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * * FUNCTION: acpi_ev_add_gpe_reference * * PARAMETERS: gpe_event_info - Add a reference to this GPE @@ -674,6 +728,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) * in the event_info. */ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); + gpe_event_info->disable_for_dispatch = FALSE; return (AE_OK); } @@ -737,6 +792,8 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, } } + gpe_event_info->disable_for_dispatch = TRUE; + /* * Dispatch the GPE to either an installed handler or the control * method associated with this GPE (_Lxx or _Exx). If a handler diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 17cfef7..d7a3b27 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -235,11 +235,13 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) case ACPI_GPE_ENABLE: status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); + gpe_event_info->disable_for_dispatch = FALSE; break; case ACPI_GPE_DISABLE: status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + gpe_event_info->disable_for_dispatch = TRUE; break; default: @@ -257,6 +259,47 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe) /******************************************************************************* * + * FUNCTION: acpi_mask_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * is_masked - Whether the GPE is masked or not + * + * RETURN: Status + * + * DESCRIPTION: Unconditionally mask/unmask the an individual GPE, ex., to + * prevent a GPE flooding. + * + ******************************************************************************/ +acpi_status acpi_mask_gpe(acpi_handle gpe_device, u32 gpe_number, u8 is_masked) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_mask_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ev_mask_gpe(gpe_event_info, is_masked); + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_mask_gpe) + +/******************************************************************************* + * * FUNCTION: acpi_mark_gpe_for_wake * * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index bdecd5e..76b0e35 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -98,7 +98,7 @@ acpi_status acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) { struct acpi_gpe_register_info *gpe_register_info; - acpi_status status; + acpi_status status = AE_OK; u32 enable_mask; u32 register_bit; @@ -148,9 +148,14 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) return (AE_BAD_PARAMETER); } - /* Write the updated enable mask */ + if (!(register_bit & gpe_register_info->mask_for_run)) { - status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); + /* Write the updated enable mask */ + + status = + acpi_hw_write(enable_mask, + &gpe_register_info->enable_address); + } return (status); } @@ -242,6 +247,12 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, local_event_status |= ACPI_EVENT_FLAG_ENABLED; } + /* GPE currently masked? (masked for runtime?) */ + + if (register_bit & gpe_register_info->mask_for_run) { + local_event_status |= ACPI_EVENT_FLAG_MASKED; + } + /* GPE enabled for wake? */ if (register_bit & gpe_register_info->enable_for_wake) { @@ -397,6 +408,7 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, u32 i; acpi_status status; struct acpi_gpe_register_info *gpe_register_info; + u8 enable_mask; /* NOTE: assumes that all GPEs are currently disabled */ @@ -410,9 +422,10 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Enable all "runtime" GPEs in this register */ + enable_mask = gpe_register_info->enable_for_run & + ~gpe_register_info->mask_for_run; status = - acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run, - gpe_register_info); + acpi_hw_gpe_enable_write(enable_mask, gpe_register_info); if (ACPI_FAILURE(status)) { return (status); } diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 875964d..c2d4b9c 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -736,6 +736,10 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status u32 gpe_number)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status + acpi_mask_gpe(acpi_handle gpe_device, + u32 gpe_number, u8 is_masked)) + +ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_mark_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number)) diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index cb389ef..fa4bd6a 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -732,16 +732,17 @@ typedef u32 acpi_event_type; * The encoding of acpi_event_status is illustrated below. * Note that a set bit (1) indicates the property is TRUE * (e.g. if bit 0 is set then the event is enabled). - * +-------------+-+-+-+-+-+ - * | Bits 31:5 |4|3|2|1|0| - * +-------------+-+-+-+-+-+ - * | | | | | | - * | | | | | +- Enabled? - * | | | | +--- Enabled for wake? - * | | | +----- Status bit set? - * | | +------- Enable bit set? - * | +--------- Has a handler? - * +--------------- + * +-------------+-+-+-+-+-+-+ + * | Bits 31:6 |5|4|3|2|1|0| + * +-------------+-+-+-+-+-+-+ + * | | | | | | | + * | | | | | | +- Enabled? + * | | | | | +--- Enabled for wake? + * | | | | +----- Status bit set? + * | | | +------- Enable bit set? + * | | +--------- Has a handler? + * | +----------- Masked? + * +----------------- */ typedef u32 acpi_event_status; @@ -751,6 +752,7 @@ typedef u32 acpi_event_status; #define ACPI_EVENT_FLAG_STATUS_SET (acpi_event_status) 0x04 #define ACPI_EVENT_FLAG_ENABLE_SET (acpi_event_status) 0x08 #define ACPI_EVENT_FLAG_HAS_HANDLER (acpi_event_status) 0x10 +#define ACPI_EVENT_FLAG_MASKED (acpi_event_status) 0x20 #define ACPI_EVENT_FLAG_SET ACPI_EVENT_FLAG_STATUS_SET /* Actions for acpi_set_gpe, acpi_gpe_wakeup, acpi_hw_low_set_gpe */ @@ -761,14 +763,15 @@ typedef u32 acpi_event_status; /* * GPE info flags - Per GPE - * +-------+-+-+---+ - * | 7:5 |4|3|2:0| - * +-------+-+-+---+ - * | | | | - * | | | +-- Type of dispatch:to method, handler, notify, or none - * | | +----- Interrupt type: edge or level triggered - * | +------- Is a Wake GPE - * +------------ + * +---+-+-+-+---+ + * |7:6|5|4|3|2:0| + * +---+-+-+-+---+ + * | | | | | + * | | | | +-- Type of dispatch:to method, handler, notify, or none + * | | | +----- Interrupt type: edge or level triggered + * | | +------- Is a Wake GPE + * | +--------- Is GPE masked by the software GPE masking machanism + * +------------ */ #define ACPI_GPE_DISPATCH_NONE (u8) 0x00 #define ACPI_GPE_DISPATCH_METHOD (u8) 0x01 -- cgit v0.10.2 From 6ea8c546f3655a81f82672f24b66dad6095bdd07 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:43:58 +0800 Subject: ACPICA: FADT support cleanup ACPICA commit 34ccd43af3fd1870fddfac0617dd0ba706963558 Remove all vestiges of the version 2 FADT which never was included in the ACPI specification. This enabled significant cleanup of both the data table compiler and the disassembler. Added many clarification comments to associate each FADT version with the version of the ACPI spec where it was originally defined. Link: https://github.com/acpica/acpica/commit/34ccd43a Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 6208069..016bcdc 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -476,17 +476,19 @@ static void acpi_tb_convert_fadt(void) u32 i; /* - * For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which + * For ACPI 1.0 FADTs (revision 1), ensure that reserved fields which * should be zero are indeed zero. This will workaround BIOSs that * inadvertently place values in these fields. * * The ACPI 1.0 reserved fields that will be zeroed are the bytes located * at offset 45, 55, 95, and the word located at offset 109, 110. * - * Note: The FADT revision value is unreliable. Only the length can be - * trusted. + * Note: The FADT revision value is unreliable because of BIOS errors. + * The table length is instead used as the final word on the version. + * + * Note: FADT revision 3 is the ACPI 2.0 version of the FADT. */ - if (acpi_gbl_FADT.header.length <= ACPI_FADT_V2_SIZE) { + if (acpi_gbl_FADT.header.length <= ACPI_FADT_V3_SIZE) { acpi_gbl_FADT.preferred_profile = 0; acpi_gbl_FADT.pstate_control = 0; acpi_gbl_FADT.cst_control = 0; diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index c19700e..1b949e0 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -230,62 +230,72 @@ struct acpi_table_facs { /* Fields common to all versions of the FADT */ struct acpi_table_fadt { - struct acpi_table_header header; /* Common ACPI table header */ - u32 facs; /* 32-bit physical address of FACS */ - u32 dsdt; /* 32-bit physical address of DSDT */ - u8 model; /* System Interrupt Model (ACPI 1.0) - not used in ACPI 2.0+ */ - u8 preferred_profile; /* Conveys preferred power management profile to OSPM. */ - u16 sci_interrupt; /* System vector of SCI interrupt */ - u32 smi_command; /* 32-bit Port address of SMI command port */ - u8 acpi_enable; /* Value to write to SMI_CMD to enable ACPI */ - u8 acpi_disable; /* Value to write to SMI_CMD to disable ACPI */ - u8 s4_bios_request; /* Value to write to SMI_CMD to enter S4BIOS state */ - u8 pstate_control; /* Processor performance state control */ - u32 pm1a_event_block; /* 32-bit port address of Power Mgt 1a Event Reg Blk */ - u32 pm1b_event_block; /* 32-bit port address of Power Mgt 1b Event Reg Blk */ - u32 pm1a_control_block; /* 32-bit port address of Power Mgt 1a Control Reg Blk */ - u32 pm1b_control_block; /* 32-bit port address of Power Mgt 1b Control Reg Blk */ - u32 pm2_control_block; /* 32-bit port address of Power Mgt 2 Control Reg Blk */ - u32 pm_timer_block; /* 32-bit port address of Power Mgt Timer Ctrl Reg Blk */ - u32 gpe0_block; /* 32-bit port address of General Purpose Event 0 Reg Blk */ - u32 gpe1_block; /* 32-bit port address of General Purpose Event 1 Reg Blk */ - u8 pm1_event_length; /* Byte Length of ports at pm1x_event_block */ - u8 pm1_control_length; /* Byte Length of ports at pm1x_control_block */ - u8 pm2_control_length; /* Byte Length of ports at pm2_control_block */ - u8 pm_timer_length; /* Byte Length of ports at pm_timer_block */ - u8 gpe0_block_length; /* Byte Length of ports at gpe0_block */ - u8 gpe1_block_length; /* Byte Length of ports at gpe1_block */ - u8 gpe1_base; /* Offset in GPE number space where GPE1 events start */ - u8 cst_control; /* Support for the _CST object and C-States change notification */ - u16 c2_latency; /* Worst case HW latency to enter/exit C2 state */ - u16 c3_latency; /* Worst case HW latency to enter/exit C3 state */ - u16 flush_size; /* Processor memory cache line width, in bytes */ - u16 flush_stride; /* Number of flush strides that need to be read */ - u8 duty_offset; /* Processor duty cycle index in processor P_CNT reg */ - u8 duty_width; /* Processor duty cycle value bit width in P_CNT register */ - u8 day_alarm; /* Index to day-of-month alarm in RTC CMOS RAM */ - u8 month_alarm; /* Index to month-of-year alarm in RTC CMOS RAM */ - u8 century; /* Index to century in RTC CMOS RAM */ - u16 boot_flags; /* IA-PC Boot Architecture Flags (see below for individual flags) */ - u8 reserved; /* Reserved, must be zero */ - u32 flags; /* Miscellaneous flag bits (see below for individual flags) */ - struct acpi_generic_address reset_register; /* 64-bit address of the Reset register */ - u8 reset_value; /* Value to write to the reset_register port to reset the system */ - u16 arm_boot_flags; /* ARM-Specific Boot Flags (see below for individual flags) (ACPI 5.1) */ - u8 minor_revision; /* FADT Minor Revision (ACPI 5.1) */ - u64 Xfacs; /* 64-bit physical address of FACS */ - u64 Xdsdt; /* 64-bit physical address of DSDT */ - struct acpi_generic_address xpm1a_event_block; /* 64-bit Extended Power Mgt 1a Event Reg Blk address */ - struct acpi_generic_address xpm1b_event_block; /* 64-bit Extended Power Mgt 1b Event Reg Blk address */ - struct acpi_generic_address xpm1a_control_block; /* 64-bit Extended Power Mgt 1a Control Reg Blk address */ - struct acpi_generic_address xpm1b_control_block; /* 64-bit Extended Power Mgt 1b Control Reg Blk address */ - struct acpi_generic_address xpm2_control_block; /* 64-bit Extended Power Mgt 2 Control Reg Blk address */ - struct acpi_generic_address xpm_timer_block; /* 64-bit Extended Power Mgt Timer Ctrl Reg Blk address */ - struct acpi_generic_address xgpe0_block; /* 64-bit Extended General Purpose Event 0 Reg Blk address */ - struct acpi_generic_address xgpe1_block; /* 64-bit Extended General Purpose Event 1 Reg Blk address */ - struct acpi_generic_address sleep_control; /* 64-bit Sleep Control register (ACPI 5.0) */ - struct acpi_generic_address sleep_status; /* 64-bit Sleep Status register (ACPI 5.0) */ - u64 hypervisor_id; /* Hypervisor Vendor ID (ACPI 6.0) */ + struct acpi_table_header header; /* [V1] Common ACPI table header */ + u32 facs; /* [V1] 32-bit physical address of FACS */ + u32 dsdt; /* [V1] 32-bit physical address of DSDT */ + u8 model; /* [V1] System Interrupt Model (ACPI 1.0) - not used in ACPI 2.0+ */ + u8 preferred_profile; /* [V1] Conveys preferred power management profile to OSPM. */ + u16 sci_interrupt; /* [V1] System vector of SCI interrupt */ + u32 smi_command; /* [V1] 32-bit Port address of SMI command port */ + u8 acpi_enable; /* [V1] Value to write to SMI_CMD to enable ACPI */ + u8 acpi_disable; /* [V1] Value to write to SMI_CMD to disable ACPI */ + u8 s4_bios_request; /* [V1] Value to write to SMI_CMD to enter S4BIOS state */ + u8 pstate_control; /* [V1] Processor performance state control */ + u32 pm1a_event_block; /* [V1] 32-bit port address of Power Mgt 1a Event Reg Blk */ + u32 pm1b_event_block; /* [V1] 32-bit port address of Power Mgt 1b Event Reg Blk */ + u32 pm1a_control_block; /* [V1] 32-bit port address of Power Mgt 1a Control Reg Blk */ + u32 pm1b_control_block; /* [V1] 32-bit port address of Power Mgt 1b Control Reg Blk */ + u32 pm2_control_block; /* [V1] 32-bit port address of Power Mgt 2 Control Reg Blk */ + u32 pm_timer_block; /* [V1] 32-bit port address of Power Mgt Timer Ctrl Reg Blk */ + u32 gpe0_block; /* [V1] 32-bit port address of General Purpose Event 0 Reg Blk */ + u32 gpe1_block; /* [V1] 32-bit port address of General Purpose Event 1 Reg Blk */ + u8 pm1_event_length; /* [V1] Byte Length of ports at pm1x_event_block */ + u8 pm1_control_length; /* [V1] Byte Length of ports at pm1x_control_block */ + u8 pm2_control_length; /* [V1] Byte Length of ports at pm2_control_block */ + u8 pm_timer_length; /* [V1] Byte Length of ports at pm_timer_block */ + u8 gpe0_block_length; /* [V1] Byte Length of ports at gpe0_block */ + u8 gpe1_block_length; /* [V1] Byte Length of ports at gpe1_block */ + u8 gpe1_base; /* [V1] Offset in GPE number space where GPE1 events start */ + u8 cst_control; /* [V1] Support for the _CST object and C-States change notification */ + u16 c2_latency; /* [V1] Worst case HW latency to enter/exit C2 state */ + u16 c3_latency; /* [V1] Worst case HW latency to enter/exit C3 state */ + u16 flush_size; /* [V1] Processor memory cache line width, in bytes */ + u16 flush_stride; /* [V1] Number of flush strides that need to be read */ + u8 duty_offset; /* [V1] Processor duty cycle index in processor P_CNT reg */ + u8 duty_width; /* [V1] Processor duty cycle value bit width in P_CNT register */ + u8 day_alarm; /* [V1] Index to day-of-month alarm in RTC CMOS RAM */ + u8 month_alarm; /* [V1] Index to month-of-year alarm in RTC CMOS RAM */ + u8 century; /* [V1] Index to century in RTC CMOS RAM */ + u16 boot_flags; /* [V3] IA-PC Boot Architecture Flags (see below for individual flags) */ + u8 reserved; /* [V1] Reserved, must be zero */ + u32 flags; /* [V1] Miscellaneous flag bits (see below for individual flags) */ + /* End of Version 1 FADT fields (ACPI 1.0) */ + + struct acpi_generic_address reset_register; /* [V3] 64-bit address of the Reset register */ + u8 reset_value; /* [V3] Value to write to the reset_register port to reset the system */ + u16 arm_boot_flags; /* [V5] ARM-Specific Boot Flags (see below for individual flags) (ACPI 5.1) */ + u8 minor_revision; /* [V5] FADT Minor Revision (ACPI 5.1) */ + u64 Xfacs; /* [V3] 64-bit physical address of FACS */ + u64 Xdsdt; /* [V3] 64-bit physical address of DSDT */ + struct acpi_generic_address xpm1a_event_block; /* [V3] 64-bit Extended Power Mgt 1a Event Reg Blk address */ + struct acpi_generic_address xpm1b_event_block; /* [V3] 64-bit Extended Power Mgt 1b Event Reg Blk address */ + struct acpi_generic_address xpm1a_control_block; /* [V3] 64-bit Extended Power Mgt 1a Control Reg Blk address */ + struct acpi_generic_address xpm1b_control_block; /* [V3] 64-bit Extended Power Mgt 1b Control Reg Blk address */ + struct acpi_generic_address xpm2_control_block; /* [V3] 64-bit Extended Power Mgt 2 Control Reg Blk address */ + struct acpi_generic_address xpm_timer_block; /* [V3] 64-bit Extended Power Mgt Timer Ctrl Reg Blk address */ + struct acpi_generic_address xgpe0_block; /* [V3] 64-bit Extended General Purpose Event 0 Reg Blk address */ + struct acpi_generic_address xgpe1_block; /* [V3] 64-bit Extended General Purpose Event 1 Reg Blk address */ + /* End of Version 3 FADT fields (ACPI 2.0) */ + + struct acpi_generic_address sleep_control; /* [V4] 64-bit Sleep Control register (ACPI 5.0) */ + /* End of Version 4 FADT fields (ACPI 3.0 and ACPI 4.0) (Field was originally reserved in ACPI 3.0) */ + + struct acpi_generic_address sleep_status; /* [V5] 64-bit Sleep Status register (ACPI 5.0) */ + /* End of Version 5 FADT fields (ACPI 5.0) */ + + u64 hypervisor_id; /* [V6] Hypervisor Vendor ID (ACPI 6.0) */ + /* End of Version 6 FADT fields (ACPI 6.0) */ + }; /* Masks for FADT IA-PC Boot Architecture Flags (boot_flags) [Vx]=Introduced in this FADT revision */ @@ -301,8 +311,8 @@ struct acpi_table_fadt { /* Masks for FADT ARM Boot Architecture Flags (arm_boot_flags) ACPI 5.1 */ -#define ACPI_FADT_PSCI_COMPLIANT (1) /* 00: [V5+] PSCI 0.2+ is implemented */ -#define ACPI_FADT_PSCI_USE_HVC (1<<1) /* 01: [V5+] HVC must be used instead of SMC as the PSCI conduit */ +#define ACPI_FADT_PSCI_COMPLIANT (1) /* 00: [V5] PSCI 0.2+ is implemented */ +#define ACPI_FADT_PSCI_USE_HVC (1<<1) /* 01: [V5] HVC must be used instead of SMC as the PSCI conduit */ /* Masks for FADT flags */ @@ -399,20 +409,34 @@ struct acpi_table_desc { * match the expected length. In other words, the length of the * FADT is the bottom line as to what the version really is. * - * For reference, the values below are as follows: - * FADT V1 size: 0x074 - * FADT V2 size: 0x084 - * FADT V3 size: 0x0F4 - * FADT V4 size: 0x0F4 - * FADT V5 size: 0x10C - * FADT V6 size: 0x114 + * NOTE: There is no officialy released V2 of the FADT. This + * version was used only for prototyping and testing during the + * 32-bit to 64-bit transition. V3 was the first official 64-bit + * version of the FADT. + * + * Update this list of defines when a new version of the FADT is + * added to the ACPI specification. Note that the FADT version is + * only incremented when new fields are appended to the existing + * version. Therefore, the FADT version is competely independent + * from the version of the ACPI specification where it is + * defined. + * + * For reference, the various FADT lengths are as follows: + * FADT V1 size: 0x074 ACPI 1.0 + * FADT V3 size: 0x0F4 ACPI 2.0 + * FADT V4 size: 0x100 ACPI 3.0 and ACPI 4.0 + * FADT V5 size: 0x10C ACPI 5.0 + * FADT V6 size: 0x114 ACPI 6.0 */ -#define ACPI_FADT_V1_SIZE (u32) (ACPI_FADT_OFFSET (flags) + 4) -#define ACPI_FADT_V2_SIZE (u32) (ACPI_FADT_OFFSET (minor_revision) + 1) -#define ACPI_FADT_V3_SIZE (u32) (ACPI_FADT_OFFSET (sleep_control)) -#define ACPI_FADT_V5_SIZE (u32) (ACPI_FADT_OFFSET (hypervisor_id)) -#define ACPI_FADT_V6_SIZE (u32) (sizeof (struct acpi_table_fadt)) +#define ACPI_FADT_V1_SIZE (u32) (ACPI_FADT_OFFSET (flags) + 4) /* ACPI 1.0 */ +#define ACPI_FADT_V3_SIZE (u32) (ACPI_FADT_OFFSET (sleep_control)) /* ACPI 2.0 */ +#define ACPI_FADT_V4_SIZE (u32) (ACPI_FADT_OFFSET (sleep_status)) /* ACPI 3.0 and ACPI 4.0 */ +#define ACPI_FADT_V5_SIZE (u32) (ACPI_FADT_OFFSET (hypervisor_id)) /* ACPI 5.0 */ +#define ACPI_FADT_V6_SIZE (u32) (sizeof (struct acpi_table_fadt)) /* ACPI 6.0 */ + +/* Update these when new FADT versions are added */ +#define ACPI_FADT_MAX_VERSION 6 #define ACPI_FADT_CONFORMANCE "ACPI 6.1 (FADT version 6)" #endif /* __ACTBL_H__ */ -- cgit v0.10.2 From e8f2c16f74ed1eb26e3c8030e7a14213474393ee Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:04 +0800 Subject: ACPICA: Debugger: Fix wrong inclusions in dbfileio.c ACPICA commit 649eb441fbef21965d10a1aca6ff41dcf23f8e05 dbfileio.c implements debugger functionalities that can only be used by the application layer debugger (acpiexec), thus it should always include and thus shouldn't include separately. Lv Zheng. Link: https://github.com/acpica/acpica/commit/649eb441 Link: https://bugs.acpica.org/show_bug.cgi?id=1292 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c index 4832879..6f05b8c 100644 --- a/drivers/acpi/acpica/dbfileio.c +++ b/drivers/acpi/acpica/dbfileio.c @@ -46,14 +46,12 @@ #include "accommon.h" #include "acdebug.h" #include "actables.h" -#include -#ifdef ACPI_APPLICATION -#include "acapps.h" -#endif #define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME("dbfileio") +#ifdef ACPI_APPLICATION +#include "acapps.h" #ifdef ACPI_DEBUGGER /******************************************************************************* * @@ -69,8 +67,6 @@ ACPI_MODULE_NAME("dbfileio") void acpi_db_close_debug_file(void) { -#ifdef ACPI_APPLICATION - if (acpi_gbl_debug_file) { fclose(acpi_gbl_debug_file); acpi_gbl_debug_file = NULL; @@ -78,7 +74,6 @@ void acpi_db_close_debug_file(void) acpi_os_printf("Debug output file %s closed\n", acpi_gbl_db_debug_filename); } -#endif } /******************************************************************************* @@ -96,8 +91,6 @@ void acpi_db_close_debug_file(void) void acpi_db_open_debug_file(char *name) { -#ifdef ACPI_APPLICATION - acpi_db_close_debug_file(); acpi_gbl_debug_file = fopen(name, "w+"); if (!acpi_gbl_debug_file) { @@ -109,8 +102,6 @@ void acpi_db_open_debug_file(char *name) strncpy(acpi_gbl_db_debug_filename, name, sizeof(acpi_gbl_db_debug_filename)); acpi_gbl_db_output_to_file = TRUE; - -#endif } #endif @@ -152,12 +143,13 @@ acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head) return (status); } - fprintf(stderr, - "Acpi table [%4.4s] successfully installed and loaded\n", - table->signature); + acpi_os_printf + ("Acpi table [%4.4s] successfully installed and loaded\n", + table->signature); table_list_head = table_list_head->next; } return (AE_OK); } +#endif -- cgit v0.10.2 From 722280ecac0e7e8279a32f7c6270ac3a525134f0 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:11 +0800 Subject: ACPICA: OSL: Add correct acpi_gbl_debug_timeout export to allow acpiexec to link ACPICA commit 408198c8c9786f9f104ee925020c3ab1701906e4 The acpi_gbl_debug_timeout which is used by acpiexec -et option now is only implemented in oswinxf.c and used for WIN32 builds. This makes it very difficult to remember that we need to add this variable to other os specific layer files in order for linking. This patch makes it a global option dependent on ACPI_APPLICATION so that it can always be linked by the applications. Lv Zheng. Link: https://github.com/acpica/acpica/commit/408198c8 Link: https://bugs.acpica.org/show_bug.cgi?id=1295 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 8c2c504..750fa82 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -383,6 +383,7 @@ ACPI_GLOBAL(const char, *acpi_gbl_pld_shape_list[]); ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL); ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL); +ACPI_INIT_GLOBAL(u8, acpi_gbl_debug_timeout, FALSE); /* Print buffer */ diff --git a/tools/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c index 88aa66e..8d8003c 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixxf.c +++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c @@ -63,10 +63,7 @@ #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osunixxf") -u8 acpi_gbl_debug_timeout = FALSE; - /* Upcalls to acpi_exec */ - void ae_table_override(struct acpi_table_header *existing_table, struct acpi_table_header **new_table); -- cgit v0.10.2 From eba4ed2046c605e5b8cafd3d2347617d866c1375 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:17 +0800 Subject: ACPICA: acpidump: Fix a duplicate variable definition ACPICA commit 080f99d5b29313380accd00d2b9768e809eb417b acpi_gbl_integer_byte_width has already been instantiated by ACPI_GLOBAL() in acglobal.h. Lv Zheng. Link: https://github.com/acpica/acpica/commit/080f99d5 Link: https://bugs.acpica.org/show_bug.cgi?id=1301 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/acpi/tools/acpidump/acpidump.h b/tools/power/acpi/tools/acpidump/acpidump.h index 025c232..94dd8a1 100644 --- a/tools/power/acpi/tools/acpidump/acpidump.h +++ b/tools/power/acpi/tools/acpidump/acpidump.h @@ -72,12 +72,6 @@ EXTERN ACPI_FILE INIT_GLOBAL(gbl_output_file, NULL); EXTERN char INIT_GLOBAL(*gbl_output_filename, NULL); EXTERN u64 INIT_GLOBAL(gbl_rsdp_base, 0); -/* Globals required for use with ACPICA modules */ - -#ifdef _DECLARE_GLOBALS -u8 acpi_gbl_integer_byte_width = 8; -#endif - /* Action table used to defer requested options */ struct ap_dump_action { diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index 7692e6b..cd6c097 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -315,6 +315,7 @@ int ACPI_SYSTEM_XFACE acpi_main(int argc, char *argv[]) ACPI_DEBUG_INITIALIZE(); /* For debug version only */ acpi_os_initialize(); gbl_output_file = ACPI_FILE_OUT; + acpi_gbl_integer_byte_width = 8; /* Process command line options */ -- cgit v0.10.2 From 556eae8bea04a8208ffb7b5a3af5bb92ebc6f1ed Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:24 +0800 Subject: ACPICA: Applications: Enable USE_NATIVE_ALLOCATE_ZEROED environment for all applications ACPICA commit 56920e2093d612ac6338dd8eb0fa89231446198f We now safe to enable USE_NATIVE_ALLOCATE_ZEROED for all applications as there are implementations in oswinxf.c, osunixxf.c and osefixf.c. Lv Zheng. Link: https://github.com/acpica/acpica/commit/56920e20 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index ed959e5..50ae2d6 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -78,6 +78,7 @@ (defined ACPI_EXAMPLE_APP) #define ACPI_APPLICATION #define ACPI_SINGLE_THREADED +#define USE_NATIVE_ALLOCATE_ZEROED #endif /* iASL configuration */ @@ -124,7 +125,6 @@ #ifdef ACPI_DUMP_APP #define ACPI_USE_NATIVE_MEMORY_MAPPING -#define USE_NATIVE_ALLOCATE_ZEROED #endif /* acpi_names/Example configuration. Hardware disabled */ -- cgit v0.10.2 From b597664f8f87a19611e2433888f743f31442532c Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:31 +0800 Subject: ACPICA: Clib: Fix build issues when ACPI_USE_STANDARD_HEADERS is not defined by converting size_t to acpi_size ACPICA commit 7cf411136c69ef0b8f184b96599eb45c15b89226 When standard size_t is not defined due to ACPI_USE_STANDARD_HEADERS=n, we shouldn't use size_t, but should use acpi_size instead. This fixes such build issue. Lv Zheng. Link: https://github.com/acpica/acpica/commit/7cf41113 Link: https://bugs.acpica.org/show_bug.cgi?id=1296 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index 5fcd970..c527521 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -134,7 +134,7 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance) char filename[ACPI_NAME_SIZE + 16]; char instance_str[16]; ACPI_FILE file; - size_t actual; + acpi_size actual; u32 table_length; /* Obtain table length */ @@ -211,7 +211,7 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, struct acpi_table_header *buffer = NULL; ACPI_FILE file; u32 file_size; - size_t actual; + acpi_size actual; /* Must use binary mode */ -- cgit v0.10.2 From 5fb3ab848b35b68e9bc757f52e4562481e801882 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:38 +0800 Subject: ACPICA: Clib: Cleanup va_arg related code ACPICA commit 32701b33cdc48d9bc43da8c9274cf172135b68fc We in fact always use the compiler specific stdarg.h for GCC even when ACPI_USE_STANDARD_HEADERS is not defined. So that the va_arg usages can always be correct for different compiler options. Likewise, the va_arg implemented in acenv.h is actually MSVC specific, this patch also moves it to acmsvc.h and tunes acwin.h to correctly use it. After cleaning up, this patch removes all inclusions from other files, but doesn't touch the BSD headers. Lv Zheng. Link: https://github.com/acpica/acpica/commit/32701b33 Link: https://bugs.acpica.org/show_bug.cgi?id=1298 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 50ae2d6..f405113 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -342,52 +342,12 @@ /* Use the standard headers from the standard locations */ -#include #include #include #include #endif /* ACPI_USE_STANDARD_HEADERS */ -/* We will be linking to the standard Clib functions */ - -#else - -/****************************************************************************** - * - * Not using native C library, use local implementations - * - *****************************************************************************/ - -/* - * Use local definitions of C library macros and functions. These function - * implementations may not be as efficient as an inline or assembly code - * implementation provided by a native C library, but they are functionally - * equivalent. - */ -#ifndef va_arg - -#ifndef _VALIST -#define _VALIST -typedef char *va_list; -#endif /* _VALIST */ - -/* Storage alignment properties */ - -#define _AUPBND (sizeof (acpi_native_int) - 1) -#define _ADNBND (sizeof (acpi_native_int) - 1) - -/* Variable argument list macro definitions */ - -#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd))) -#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) -#define va_end(ap) (ap = (va_list) NULL) -#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) - -#endif /* va_arg */ - -/* Use the local (ACPICA) definitions of the clib functions */ - #endif /* ACPI_USE_SYSTEM_CLIBRARY */ #ifndef ACPI_FILE diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h index c5a216c..9f79e98 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -44,6 +44,12 @@ #ifndef __ACGCC_H__ #define __ACGCC_H__ +/* + * Use compiler specific is a good practice for even when + * -nostdinc is specified (i.e., ACPI_USE_STANDARD_HEADERS undefined. + */ +#include + #define ACPI_INLINE __inline__ /* Function name is used for debug output. Non-ANSI, compiler-dependent */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index cde6f33..447c33f 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -180,7 +180,6 @@ #else /* !__KERNEL__ */ -#include #include #include #include -- cgit v0.10.2 From da24f31d33dd18c622bb4f21791565ab9b6421db Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:44 +0800 Subject: ACPICA: Clib: Fix wrong mini C library usage ACPICA commit 4aab18466b56f3660f27cffd3c0160900737f844 When mini C library is used, we should have the following macros undefined. The only user should be the EFI applications: ACPI_USE_SYSTEM_CLIBRARY=n ACPI_USE_STANDARD_HEADERS=n All other applications uses the compiler specific library: ACPI_USE_SYSTEM_CLIBRARY=y ACPI_USE_STANDARD_HEADERS=y Linux/BSD kernels are the kind of hosts providing C library but does not provide the standard headers: ACPI_USE_SYSTEM_CLIBRARY=y ACPI_USE_STANDARD_HEADERS=n But the above logic hasn't been synchronized between the header files. This patch synchronizes all header files to correct C library usages for different platforms. This patch moves all ACPI_USE_SYSTEM_CLIRARY and ACPI_USE_STANDARD_HEADERS to the top most lines of a platform specific header. After synchronization, ACPI_USE_SYSTEM_CLIRARY definition can be removed for ACPI_APPLICATION. Lv Zheng. Link: https://github.com/acpica/acpica/commit/4aab1846 Link: https://bugs.acpica.org/show_bug.cgi?id=1299 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index f405113..ee5c0b9 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -149,7 +149,6 @@ /* Common for all ACPICA applications */ #ifdef ACPI_APPLICATION -#define ACPI_USE_SYSTEM_CLIBRARY #define ACPI_USE_LOCAL_CACHE #endif @@ -326,7 +325,8 @@ * ACPI_USE_SYSTEM_CLIBRARY - Define this if linking to an actual C library. * Otherwise, local versions of string/memory functions will be used. * ACPI_USE_STANDARD_HEADERS - Define this if linking to a C library and - * the standard header files may be used. + * the standard header files may be used. Defining this implies that + * ACPI_USE_SYSTEM_CLIBRARY has been defined. * * The ACPICA subsystem only uses low level C library functions that do not * call operating system services and may therefore be inlined in the code. @@ -334,7 +334,6 @@ * It may be necessary to tailor these include files to the target * generation environment. */ -#ifdef ACPI_USE_SYSTEM_CLIBRARY /* Use the standard C library headers. We want to keep these to a minimum. */ @@ -348,8 +347,6 @@ #endif /* ACPI_USE_STANDARD_HEADERS */ -#endif /* ACPI_USE_SYSTEM_CLIBRARY */ - #ifndef ACPI_FILE #ifdef ACPI_APPLICATION #include diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 447c33f..1380ec7 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -180,10 +180,11 @@ #else /* !__KERNEL__ */ -#include -#include -#include +#define ACPI_USE_STANDARD_HEADERS + +#ifdef ACPI_USE_STANDARD_HEADERS #include +#endif /* Define/disable kernel-specific declarators */ -- cgit v0.10.2 From 4e2fc6a0aa0a18403ccdcd46d7ed9f624845772b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:52 +0800 Subject: ACPICA: Clib/EFI: Fix wrong order of standard integer types/IO handles ACPICA commit 7f9b359b7c78c69b07f62eb2d58f710c351fd75d EFI header should use standard C library stuffs (integer types and IO handles) rather than implementing such standard stuffs. This patch fixes this issue by: 1. Implementing standard integer types for ACPI_USE_STANDARD_HADERS=n; 2. Defining EFI types using standard integer types and standard IO handles; 3. Tuning header inclusion order and environment definition order; 4. Removing wrong standard header inclusion from ACPICA core files; 5. Moving several application headers from acpidump.h to acenv.h. This patch corrects some of them. Lv Zheng. Except some harmless header inclusion re-ordering, Linux kernel is not affected by this change. Link: https://github.com/acpica/acpica/commit/7f9b359b Link: https://bugs.acpica.org/show_bug.cgi?id=1300 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index ca2c060..247f30c 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -44,8 +44,6 @@ #ifndef _ACAPPS #define _ACAPPS -#include - /* Common info for tool signons */ #define ACPICA_NAME "Intel ACPI Component Architecture" diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c index 770a177..ce18346 100644 --- a/drivers/acpi/acpica/utpredef.c +++ b/drivers/acpi/acpica/utpredef.c @@ -176,8 +176,6 @@ void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes) ******************************************************************************/ #if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP) -#include -#include /* Local prototypes */ diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index ee5c0b9..1e2fe95 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -209,18 +209,20 @@ #elif defined(__OS2__) #include "acos2.h" -#elif defined(_AED_EFI) -#include "acefi.h" - -#elif defined(_GNU_EFI) -#include "acefi.h" - #elif defined(__HAIKU__) #include "achaiku.h" #elif defined(__QNX__) #include "acqnx.h" +/* + * EFI applications can be built with -nostdlib, in this case, it must be + * included after including all other host environmental definitions, in + * order to override the definitions. + */ +#elif defined(_AED_EFI) || defined(_GNU_EFI) +#include "acefi.h" + #else /* Unknown environment */ @@ -344,12 +346,16 @@ #include #include #include +#ifdef ACPI_APPLICATION +#include +#include +#include +#include +#endif #endif /* ACPI_USE_STANDARD_HEADERS */ -#ifndef ACPI_FILE #ifdef ACPI_APPLICATION -#include #define ACPI_FILE FILE * #define ACPI_FILE_OUT stdout #define ACPI_FILE_ERR stderr @@ -358,7 +364,6 @@ #define ACPI_FILE_OUT NULL #define ACPI_FILE_ERR NULL #endif /* ACPI_APPLICATION */ -#endif /* ACPI_FILE */ #ifndef ACPI_INIT_FUNCTION #define ACPI_INIT_FUNCTION diff --git a/include/acpi/platform/acenvex.h b/include/acpi/platform/acenvex.h index 4f15c1d..b672615 100644 --- a/include/acpi/platform/acenvex.h +++ b/include/acpi/platform/acenvex.h @@ -59,15 +59,17 @@ #elif defined(WIN32) #include "acwinex.h" -#elif defined(_AED_EFI) -#include "acefiex.h" - -#elif defined(_GNU_EFI) -#include "acefiex.h" - #elif defined(__DragonFly__) #include "acdragonflyex.h" +/* + * EFI applications can be built with -nostdlib, in this case, it must be + * included after including all other host environmental definitions, in + * order to override the definitions. + */ +#elif defined(_AED_EFI) || defined(_GNU_EFI) +#include "acefiex.h" + #endif /*! [End] no source code translation !*/ diff --git a/tools/power/acpi/common/cmfsize.c b/tools/power/acpi/common/cmfsize.c index e73a79f..d2240e6 100644 --- a/tools/power/acpi/common/cmfsize.c +++ b/tools/power/acpi/common/cmfsize.c @@ -44,7 +44,6 @@ #include #include "accommon.h" #include "acapps.h" -#include #define _COMPONENT ACPI_TOOLS ACPI_MODULE_NAME("cmfsize") diff --git a/tools/power/acpi/tools/acpidump/acpidump.h b/tools/power/acpi/tools/acpidump/acpidump.h index 94dd8a1..9f7db00 100644 --- a/tools/power/acpi/tools/acpidump/acpidump.h +++ b/tools/power/acpi/tools/acpidump/acpidump.h @@ -56,11 +56,6 @@ #include "accommon.h" #include "actables.h" -#include -#include -#include -#include - /* Globals */ EXTERN u8 INIT_GLOBAL(gbl_summary_mode, FALSE); -- cgit v0.10.2 From e323c02dee59af7da65637852f6fa95551325d80 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:44:59 +0800 Subject: ACPICA: MSVC9: Fix inclusion order issue ACPICA commit 9bb265c2afb9910e46f820d6759648580edabd09 When /Za is specified, headers of some Windows SDKs contain bugs breaking VC builds, and MSVC9's default SDK is one of such header-buggy library. In order to solve this issue, many VC developers stop using /Za. However we've been asked to have this fixed without removing /Za. In MSVC9 default SDK, this issue can be fixed by restricting to be the last standard file included by every source file in the projects. This patch thus moves inclusion to "acapps.h", so that this issue can be fixed by ensuring that "acapps.h" is always the last standard file included by all of the ACPICA source files. This is in fact also a useful cleanup because applications can only include one header (e.x., acpidump.h) instead of including acapps.h separately. Lv Zheng. Except some harmless header inclusion re-ordering, Linux kernel is not affected by this change. Link: https://github.com/acpica/acpica/commit/9bb265c2 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index 247f30c..bc38534 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -44,6 +44,10 @@ #ifndef _ACAPPS #define _ACAPPS +#ifdef ACPI_USE_STANDARD_HEADERS +#include +#endif /* ACPI_USE_STANDARD_HEADERS */ + /* Common info for tool signons */ #define ACPICA_NAME "Intel ACPI Component Architecture" diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 1e2fe95..3dbfa27 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -350,7 +350,8 @@ #include #include #include -#include +#include +#include #endif #endif /* ACPI_USE_STANDARD_HEADERS */ diff --git a/tools/power/acpi/tools/acpidump/acpidump.h b/tools/power/acpi/tools/acpidump/acpidump.h index 9f7db00..00423fc 100644 --- a/tools/power/acpi/tools/acpidump/acpidump.h +++ b/tools/power/acpi/tools/acpidump/acpidump.h @@ -55,6 +55,7 @@ #include #include "accommon.h" #include "actables.h" +#include "acapps.h" /* Globals */ diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index c527521..44a0010 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -42,7 +42,6 @@ */ #include "acpidump.h" -#include "acapps.h" /* Local prototypes */ diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index cd6c097..17ce0d7 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -43,7 +43,6 @@ #define _DECLARE_GLOBALS #include "acpidump.h" -#include "acapps.h" /* * acpidump - A portable utility for obtaining system ACPI tables and dumping -- cgit v0.10.2 From f173a7750eb188fd7d888d5950d58454bcfbd09b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:45:06 +0800 Subject: ACPICA: Clib: Add -nostdinc support for EFI layer ACPICA commit d261d40ea168f8e4c4e3986de720b8651c4aba1c This patch adds sprintf()/snprintf()/vsnprintf()/printf()/vfprintf() support for OSPMs that have ACPI_USE_SYSTEM_CLIBRARY defined but do not have ACPI_USE_STANDARD_HEADERS defined. -iwithprefix include is required to include which contains compiler specific implementation of vargs when -nostdinc is specified. -fno-builtin is required for GCC to avoid optimization performed printf(). This optimization cannot be automatically disabled by specifying -nostdlib. Please refer to the first link below for the details. However, the build option changes do not affect Linux kernel builds and are not included. Lv Zheng. Link: http://www.ciselant.de/projects/gcc_printf/gcc_printf.html Link: https://github.com/acpica/acpica/commit/d261d40e Link: https://bugs.acpica.org/show_bug.cgi?id=1302 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 91269a6..d899296 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -724,25 +724,6 @@ const struct ah_device_id *acpi_ah_match_hardware_id(char *hid); const char *acpi_ah_match_uuid(u8 *data); /* - * utprint - printf/vprintf output functions - */ -const char *acpi_ut_scan_number(const char *string, u64 *number_ptr); - -const char *acpi_ut_print_number(char *string, u64 number); - -int -acpi_ut_vsnprintf(char *string, - acpi_size size, const char *format, va_list args); - -int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...); - -#ifdef ACPI_APPLICATION -int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args); - -int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...); -#endif - -/* * utuuid -- UUID support functions */ #if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP) diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c index bd31faf..ff29812 100644 --- a/drivers/acpi/acpica/utbuffer.c +++ b/drivers/acpi/acpica/utbuffer.c @@ -239,8 +239,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file, u8 buf_char; if (!buffer) { - acpi_ut_file_printf(file, - "Null Buffer Pointer in DumpBuffer!\n"); + fprintf(file, "Null Buffer Pointer in DumpBuffer!\n"); return; } @@ -254,7 +253,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file, /* Print current offset */ - acpi_ut_file_printf(file, "%6.4X: ", (base_offset + i)); + fprintf(file, "%6.4X: ", (base_offset + i)); /* Print 16 hex chars */ @@ -263,8 +262,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file, /* Dump fill spaces */ - acpi_ut_file_printf(file, "%*s", - ((display * 2) + 1), " "); + fprintf(file, "%*s", ((display * 2) + 1), " "); j += display; continue; } @@ -273,34 +271,34 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file, case DB_BYTE_DISPLAY: default: /* Default is BYTE display */ - acpi_ut_file_printf(file, "%02X ", - buffer[(acpi_size)i + j]); + fprintf(file, "%02X ", + buffer[(acpi_size)i + j]); break; case DB_WORD_DISPLAY: ACPI_MOVE_16_TO_32(&temp32, &buffer[(acpi_size)i + j]); - acpi_ut_file_printf(file, "%04X ", temp32); + fprintf(file, "%04X ", temp32); break; case DB_DWORD_DISPLAY: ACPI_MOVE_32_TO_32(&temp32, &buffer[(acpi_size)i + j]); - acpi_ut_file_printf(file, "%08X ", temp32); + fprintf(file, "%08X ", temp32); break; case DB_QWORD_DISPLAY: ACPI_MOVE_32_TO_32(&temp32, &buffer[(acpi_size)i + j]); - acpi_ut_file_printf(file, "%08X", temp32); + fprintf(file, "%08X", temp32); ACPI_MOVE_32_TO_32(&temp32, &buffer[(acpi_size)i + j + 4]); - acpi_ut_file_printf(file, "%08X ", temp32); + fprintf(file, "%08X ", temp32); break; } @@ -311,24 +309,24 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file, * Print the ASCII equivalent characters but watch out for the bad * unprintable ones (printable chars are 0x20 through 0x7E) */ - acpi_ut_file_printf(file, " "); + fprintf(file, " "); for (j = 0; j < 16; j++) { if (i + j >= count) { - acpi_ut_file_printf(file, "\n"); + fprintf(file, "\n"); return; } buf_char = buffer[(acpi_size)i + j]; if (isprint(buf_char)) { - acpi_ut_file_printf(file, "%c", buf_char); + fprintf(file, "%c", buf_char); } else { - acpi_ut_file_printf(file, "."); + fprintf(file, "."); } } /* Done with that line. */ - acpi_ut_file_printf(file, "\n"); + fprintf(file, "\n"); i += 16; } diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 84c0853..005a4a2 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -646,7 +646,7 @@ void ACPI_INTERNAL_VAR_XFACE acpi_log_error(const char *format, ...) va_list args; va_start(args, format); - (void)acpi_ut_file_vprintf(ACPI_FILE_ERR, format, args); + (void)vfprintf(ACPI_FILE_ERR, format, args); va_end(args); } diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c index dd084cf..13b8617 100644 --- a/drivers/acpi/acpica/utprint.c +++ b/drivers/acpi/acpica/utprint.c @@ -336,7 +336,7 @@ static char *acpi_ut_format_number(char *string, /******************************************************************************* * - * FUNCTION: acpi_ut_vsnprintf + * FUNCTION: vsnprintf * * PARAMETERS: string - String with boundary * size - Boundary of the string @@ -349,9 +349,7 @@ static char *acpi_ut_format_number(char *string, * ******************************************************************************/ -int -acpi_ut_vsnprintf(char *string, - acpi_size size, const char *format, va_list args) +int vsnprintf(char *string, acpi_size size, const char *format, va_list args) { u8 base; u8 type; @@ -586,7 +584,7 @@ acpi_ut_vsnprintf(char *string, /******************************************************************************* * - * FUNCTION: acpi_ut_snprintf + * FUNCTION: snprintf * * PARAMETERS: string - String with boundary * size - Boundary of the string @@ -598,13 +596,38 @@ acpi_ut_vsnprintf(char *string, * ******************************************************************************/ -int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...) +int snprintf(char *string, acpi_size size, const char *format, ...) { va_list args; int length; va_start(args, format); - length = acpi_ut_vsnprintf(string, size, format, args); + length = vsnprintf(string, size, format, args); + va_end(args); + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: sprintf + * + * PARAMETERS: string - String with boundary + * Format, ... - Standard printf format + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to a string. + * + ******************************************************************************/ + +int sprintf(char *string, const char *format, ...) +{ + va_list args; + int length; + + va_start(args, format); + length = vsnprintf(string, ACPI_UINT32_MAX, format, args); va_end(args); return (length); @@ -613,7 +636,60 @@ int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...) #ifdef ACPI_APPLICATION /******************************************************************************* * - * FUNCTION: acpi_ut_file_vprintf + * FUNCTION: vprintf + * + * PARAMETERS: format - Standard printf format + * args - Argument list + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to stdout using argument list pointer. + * + ******************************************************************************/ + +int vprintf(const char *format, va_list args) +{ + acpi_cpu_flags flags; + int length; + + flags = acpi_os_acquire_lock(acpi_gbl_print_lock); + length = vsnprintf(acpi_gbl_print_buffer, + sizeof(acpi_gbl_print_buffer), format, args); + + (void)acpi_os_write_file(ACPI_FILE_OUT, acpi_gbl_print_buffer, length, + 1); + acpi_os_release_lock(acpi_gbl_print_lock, flags); + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: printf + * + * PARAMETERS: Format, ... - Standard printf format + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to stdout. + * + ******************************************************************************/ + +int printf(const char *format, ...) +{ + va_list args; + int length; + + va_start(args, format); + length = vprintf(format, args); + va_end(args); + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: vfprintf * * PARAMETERS: file - File descriptor * format - Standard printf format @@ -625,14 +701,14 @@ int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...) * ******************************************************************************/ -int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args) +int vfprintf(FILE * file, const char *format, va_list args) { acpi_cpu_flags flags; int length; flags = acpi_os_acquire_lock(acpi_gbl_print_lock); - length = acpi_ut_vsnprintf(acpi_gbl_print_buffer, - sizeof(acpi_gbl_print_buffer), format, args); + length = vsnprintf(acpi_gbl_print_buffer, + sizeof(acpi_gbl_print_buffer), format, args); (void)acpi_os_write_file(file, acpi_gbl_print_buffer, length, 1); acpi_os_release_lock(acpi_gbl_print_lock, flags); @@ -642,7 +718,7 @@ int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args) /******************************************************************************* * - * FUNCTION: acpi_ut_file_printf + * FUNCTION: fprintf * * PARAMETERS: file - File descriptor * Format, ... - Standard printf format @@ -653,13 +729,13 @@ int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args) * ******************************************************************************/ -int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...) +int fprintf(FILE * file, const char *format, ...) { va_list args; int length; va_start(args, format); - length = acpi_ut_file_vprintf(file, format, args); + length = vfprintf(file, format, args); va_end(args); return (length); diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index fb8f1d9..2a1507c 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c @@ -195,13 +195,13 @@ ap_dump_table_buffer(struct acpi_table_header *table, * Note: simplest to just always emit a 64-bit address. acpi_xtract * utility can handle this. */ - acpi_ut_file_printf(gbl_output_file, "%4.4s @ 0x%8.8X%8.8X\n", - table->signature, ACPI_FORMAT_UINT64(address)); + fprintf(gbl_output_file, "%4.4s @ 0x%8.8X%8.8X\n", + table->signature, ACPI_FORMAT_UINT64(address)); acpi_ut_dump_buffer_to_file(gbl_output_file, ACPI_CAST_PTR(u8, table), table_length, DB_BYTE_DISPLAY, 0); - acpi_ut_file_printf(gbl_output_file, "\n"); + fprintf(gbl_output_file, "\n"); return (0); } diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index 44a0010..71edeb4 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -157,8 +157,7 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance) /* Handle multiple SSDts - create different filenames for each */ if (instance > 0) { - acpi_ut_snprintf(instance_str, sizeof(instance_str), "%u", - instance); + snprintf(instance_str, sizeof(instance_str), "%u", instance); strcat(filename, instance_str); } -- cgit v0.10.2 From dd99cbcca4fea59ec5e93cf160cb4b939306f5c9 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:45:13 +0800 Subject: ACPICA: Clib: Eliminate acpi_os_XXXFile()/acpi_log_error and link clibrary fxxx()/errno/perror() instead ACPICA commit 189429fb7d06cdb89043ae32d615faf553467f1d This patch follows new ACPICA design, eliminates old portable OSLs, and implements fopen/fread/fwrite/fclose/fseek/ftell for GNU EFI environment. This patch also eliminates acpi_log_error(), convering them into fprintf(stderr)/perror(). Lv Zheng. Link: https://github.com/acpica/acpica/commit/189429fb Link: https://bugs.acpica.org/show_bug.cgi?id=1302 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 005a4a2..044df9b 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -628,27 +628,3 @@ acpi_trace_point(acpi_trace_event_type type, u8 begin, u8 *aml, char *pathname) ACPI_EXPORT_SYMBOL(acpi_trace_point) #endif -#ifdef ACPI_APPLICATION -/******************************************************************************* - * - * FUNCTION: acpi_log_error - * - * PARAMETERS: format - Printf format field - * ... - Optional printf arguments - * - * RETURN: None - * - * DESCRIPTION: Print error message to the console, used by applications. - * - ******************************************************************************/ -void ACPI_INTERNAL_VAR_XFACE acpi_log_error(const char *format, ...) -{ - va_list args; - - va_start(args, format); - (void)vfprintf(ACPI_FILE_ERR, format, args); - va_end(args); -} - -ACPI_EXPORT_SYMBOL(acpi_log_error) -#endif diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c index 13b8617..40eba80 100644 --- a/drivers/acpi/acpica/utprint.c +++ b/drivers/acpi/acpica/utprint.c @@ -656,8 +656,7 @@ int vprintf(const char *format, va_list args) length = vsnprintf(acpi_gbl_print_buffer, sizeof(acpi_gbl_print_buffer), format, args); - (void)acpi_os_write_file(ACPI_FILE_OUT, acpi_gbl_print_buffer, length, - 1); + (void)fwrite(acpi_gbl_print_buffer, length, 1, ACPI_FILE_OUT); acpi_os_release_lock(acpi_gbl_print_lock, flags); return (length); @@ -710,7 +709,7 @@ int vfprintf(FILE * file, const char *format, va_list args) length = vsnprintf(acpi_gbl_print_buffer, sizeof(acpi_gbl_print_buffer), format, args); - (void)acpi_os_write_file(file, acpi_gbl_print_buffer, length, 1); + (void)fwrite(acpi_gbl_print_buffer, length, 1, file); acpi_os_release_lock(acpi_gbl_print_lock, flags); return (length); diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 562603d..f3414c8 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -371,6 +371,12 @@ acpi_status acpi_os_wait_command_ready(void); acpi_status acpi_os_notify_command_complete(void); #endif +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_trace_point +void +acpi_os_trace_point(acpi_trace_event_type type, + u8 begin, u8 *aml, char *pathname); +#endif + /* * Obtain ACPI table(s) */ @@ -416,41 +422,4 @@ char *acpi_os_get_next_filename(void *dir_handle); void acpi_os_close_directory(void *dir_handle); #endif -/* - * File I/O and related support - */ -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_open_file -ACPI_FILE acpi_os_open_file(const char *path, u8 modes); -#endif - -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_close_file -void acpi_os_close_file(ACPI_FILE file); -#endif - -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_file -int -acpi_os_read_file(ACPI_FILE file, - void *buffer, acpi_size size, acpi_size count); -#endif - -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_write_file -int -acpi_os_write_file(ACPI_FILE file, - void *buffer, acpi_size size, acpi_size count); -#endif - -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_file_offset -long acpi_os_get_file_offset(ACPI_FILE file); -#endif - -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_set_file_offset -acpi_status acpi_os_set_file_offset(ACPI_FILE file, long offset, u8 from); -#endif - -#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_trace_point -void -acpi_os_trace_point(acpi_trace_event_type type, - u8 begin, u8 *aml, char *pathname); -#endif - #endif /* __ACPIOSXF_H__ */ diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index c2d4b9c..d94e5c0 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -943,9 +943,6 @@ ACPI_DBG_DEPENDENT_RETURN_VOID(void acpi_trace_point(acpi_trace_event_type type, u8 begin, u8 *aml, char *pathname)) -ACPI_APP_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(1) - void ACPI_INTERNAL_VAR_XFACE - acpi_log_error(const char *format, ...)) acpi_status acpi_initialize_debugger(void); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index fa4bd6a..e96907b 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1288,15 +1288,6 @@ typedef enum { #define ACPI_OSI_WIN_8 0x0C #define ACPI_OSI_WIN_10 0x0D -/* Definitions of file IO */ - -#define ACPI_FILE_READING 0x01 -#define ACPI_FILE_WRITING 0x02 -#define ACPI_FILE_BINARY 0x04 - -#define ACPI_FILE_BEGIN 0x01 -#define ACPI_FILE_END 0x02 - /* Definitions of getopt */ #define ACPI_OPT_END -1 diff --git a/tools/power/acpi/common/cmfsize.c b/tools/power/acpi/common/cmfsize.c index d2240e6..bc82596 100644 --- a/tools/power/acpi/common/cmfsize.c +++ b/tools/power/acpi/common/cmfsize.c @@ -68,24 +68,24 @@ u32 cm_get_file_size(ACPI_FILE file) /* Save the current file pointer, seek to EOF to obtain file size */ - current_offset = acpi_os_get_file_offset(file); + current_offset = ftell(file); if (current_offset < 0) { goto offset_error; } - status = acpi_os_set_file_offset(file, 0, ACPI_FILE_END); + status = fseek(file, 0, SEEK_END); if (ACPI_FAILURE(status)) { goto seek_error; } - file_size = acpi_os_get_file_offset(file); + file_size = ftell(file); if (file_size < 0) { goto offset_error; } /* Restore original file pointer */ - status = acpi_os_set_file_offset(file, current_offset, ACPI_FILE_BEGIN); + status = fseek(file, current_offset, SEEK_SET); if (ACPI_FAILURE(status)) { goto seek_error; } @@ -93,10 +93,10 @@ u32 cm_get_file_size(ACPI_FILE file) return ((u32)file_size); offset_error: - acpi_log_error("Could not get file offset"); + fprintf(stderr, "Could not get file offset\n"); return (ACPI_UINT32_MAX); seek_error: - acpi_log_error("Could not set file offset"); + fprintf(stderr, "Could not set file offset\n"); return (ACPI_UINT32_MAX); } diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c index e9e8091..3919970 100644 --- a/tools/power/acpi/common/getopt.c +++ b/tools/power/acpi/common/getopt.c @@ -57,7 +57,7 @@ #include "acapps.h" #define ACPI_OPTION_ERROR(msg, badchar) \ - if (acpi_gbl_opterr) {acpi_log_error ("%s%c\n", msg, badchar);} + if (acpi_gbl_opterr) {fprintf (stderr, "%s%c\n", msg, badchar);} int acpi_gbl_opterr = 1; int acpi_gbl_optind = 1; diff --git a/tools/power/acpi/os_specific/service_layers/oslibcfs.c b/tools/power/acpi/os_specific/service_layers/oslibcfs.c deleted file mode 100644 index 11f4aba..0000000 --- a/tools/power/acpi/os_specific/service_layers/oslibcfs.c +++ /dev/null @@ -1,217 +0,0 @@ -/****************************************************************************** - * - * Module Name: oslibcfs - C library OSL for file I/O - * - *****************************************************************************/ - -/* - * Copyright (C) 2000 - 2016, Intel Corp. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * 3. Neither the names of the above-listed copyright holders nor the names - * of any contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - */ - -#include -#include -#include - -#define _COMPONENT ACPI_OS_SERVICES -ACPI_MODULE_NAME("oslibcfs") - -/******************************************************************************* - * - * FUNCTION: acpi_os_open_file - * - * PARAMETERS: path - File path - * modes - File operation type - * - * RETURN: File descriptor. - * - * DESCRIPTION: Open a file for reading (ACPI_FILE_READING) or/and writing - * (ACPI_FILE_WRITING). - * - ******************************************************************************/ -ACPI_FILE acpi_os_open_file(const char *path, u8 modes) -{ - ACPI_FILE file; - u32 i = 0; - char modes_str[4]; - - if (modes & ACPI_FILE_READING) { - modes_str[i++] = 'r'; - } - if (modes & ACPI_FILE_WRITING) { - modes_str[i++] = 'w'; - } - - if (modes & ACPI_FILE_BINARY) { - modes_str[i++] = 'b'; - } - - modes_str[i++] = '\0'; - - file = fopen(path, modes_str); - if (!file) { - perror("Could not open file"); - } - - return (file); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_close_file - * - * PARAMETERS: file - An open file descriptor - * - * RETURN: None. - * - * DESCRIPTION: Close a file opened via acpi_os_open_file. - * - ******************************************************************************/ - -void acpi_os_close_file(ACPI_FILE file) -{ - - fclose(file); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_read_file - * - * PARAMETERS: file - An open file descriptor - * buffer - Data buffer - * size - Data block size - * count - Number of data blocks - * - * RETURN: Number of bytes actually read. - * - * DESCRIPTION: Read from a file. - * - ******************************************************************************/ - -int -acpi_os_read_file(ACPI_FILE file, void *buffer, acpi_size size, acpi_size count) -{ - int length; - - length = fread(buffer, size, count, file); - if (length < 0) { - perror("Error reading file"); - } - - return (length); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_write_file - * - * PARAMETERS: file - An open file descriptor - * buffer - Data buffer - * size - Data block size - * count - Number of data blocks - * - * RETURN: Number of bytes actually written. - * - * DESCRIPTION: Write to a file. - * - ******************************************************************************/ - -int -acpi_os_write_file(ACPI_FILE file, - void *buffer, acpi_size size, acpi_size count) -{ - int length; - - length = fwrite(buffer, size, count, file); - if (length < 0) { - perror("Error writing file"); - } - - return (length); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_get_file_offset - * - * PARAMETERS: file - An open file descriptor - * - * RETURN: Current file pointer position. - * - * DESCRIPTION: Get current file offset. - * - ******************************************************************************/ - -long acpi_os_get_file_offset(ACPI_FILE file) -{ - long offset; - - offset = ftell(file); - return (offset); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_set_file_offset - * - * PARAMETERS: file - An open file descriptor - * offset - New file offset - * from - From begin/end of file - * - * RETURN: Status - * - * DESCRIPTION: Set current file offset. - * - ******************************************************************************/ - -acpi_status acpi_os_set_file_offset(ACPI_FILE file, long offset, u8 from) -{ - int ret = 0; - - if (from == ACPI_FILE_BEGIN) { - ret = fseek(file, offset, SEEK_SET); - } - - if (from == ACPI_FILE_END) { - ret = fseek(file, offset, SEEK_END); - } - - if (ret < 0) { - return (AE_ERROR); - } else { - return (AE_OK); - } -} diff --git a/tools/power/acpi/tools/acpidump/Makefile b/tools/power/acpi/tools/acpidump/Makefile index 2942cdc..a710f60 100644 --- a/tools/power/acpi/tools/acpidump/Makefile +++ b/tools/power/acpi/tools/acpidump/Makefile @@ -41,7 +41,6 @@ TOOL_OBJS = \ utprint.o\ utstring.o\ utxferror.o\ - oslibcfs.o\ oslinuxtbl.o\ cmfsize.o\ getopt.o diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index 2a1507c..1c4e00b 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c @@ -69,16 +69,17 @@ u8 ap_is_valid_header(struct acpi_table_header *table) /* Make sure signature is all ASCII and a valid ACPI name */ if (!acpi_ut_valid_nameseg(table->signature)) { - acpi_log_error("Table signature (0x%8.8X) is invalid\n", - *(u32 *)table->signature); + fprintf(stderr, + "Table signature (0x%8.8X) is invalid\n", + *(u32 *)table->signature); return (FALSE); } /* Check for minimum table length */ if (table->length < sizeof(struct acpi_table_header)) { - acpi_log_error("Table length (0x%8.8X) is invalid\n", - table->length); + fprintf(stderr, "Table length (0x%8.8X) is invalid\n", + table->length); return (FALSE); } } @@ -115,8 +116,8 @@ u8 ap_is_valid_checksum(struct acpi_table_header *table) } if (ACPI_FAILURE(status)) { - acpi_log_error("%4.4s: Warning: wrong checksum in table\n", - table->signature); + fprintf(stderr, "%4.4s: Warning: wrong checksum in table\n", + table->signature); } return (AE_OK); @@ -239,14 +240,14 @@ int ap_dump_all_tables(void) if (status == AE_LIMIT) { return (0); } else if (i == 0) { - acpi_log_error - ("Could not get ACPI tables, %s\n", - acpi_format_exception(status)); + fprintf(stderr, + "Could not get ACPI tables, %s\n", + acpi_format_exception(status)); return (-1); } else { - acpi_log_error - ("Could not get ACPI table at index %u, %s\n", - i, acpi_format_exception(status)); + fprintf(stderr, + "Could not get ACPI table at index %u, %s\n", + i, acpi_format_exception(status)); continue; } } @@ -289,17 +290,17 @@ int ap_dump_table_by_address(char *ascii_address) status = acpi_ut_strtoul64(ascii_address, ACPI_ANY_BASE, ACPI_MAX64_BYTE_WIDTH, &long_address); if (ACPI_FAILURE(status)) { - acpi_log_error("%s: Could not convert to a physical address\n", - ascii_address); + fprintf(stderr, "%s: Could not convert to a physical address\n", + ascii_address); return (-1); } address = (acpi_physical_address)long_address; status = acpi_os_get_table_by_address(address, &table); if (ACPI_FAILURE(status)) { - acpi_log_error("Could not get table at 0x%8.8X%8.8X, %s\n", - ACPI_FORMAT_UINT64(address), - acpi_format_exception(status)); + fprintf(stderr, "Could not get table at 0x%8.8X%8.8X, %s\n", + ACPI_FORMAT_UINT64(address), + acpi_format_exception(status)); return (-1); } @@ -331,9 +332,9 @@ int ap_dump_table_by_name(char *signature) int table_status; if (strlen(signature) != ACPI_NAME_SIZE) { - acpi_log_error - ("Invalid table signature [%s]: must be exactly 4 characters\n", - signature); + fprintf(stderr, + "Invalid table signature [%s]: must be exactly 4 characters\n", + signature); return (-1); } @@ -363,9 +364,9 @@ int ap_dump_table_by_name(char *signature) return (0); } - acpi_log_error - ("Could not get ACPI table with signature [%s], %s\n", - local_signature, acpi_format_exception(status)); + fprintf(stderr, + "Could not get ACPI table with signature [%s], %s\n", + local_signature, acpi_format_exception(status)); return (-1); } @@ -408,24 +409,24 @@ int ap_dump_table_from_file(char *pathname) } if (!acpi_ut_valid_nameseg(table->signature)) { - acpi_log_error - ("No valid ACPI signature was found in input file %s\n", - pathname); + fprintf(stderr, + "No valid ACPI signature was found in input file %s\n", + pathname); } /* File must be at least as long as the table length */ if (table->length > file_size) { - acpi_log_error - ("Table length (0x%X) is too large for input file (0x%X) %s\n", - table->length, file_size, pathname); + fprintf(stderr, + "Table length (0x%X) is too large for input file (0x%X) %s\n", + table->length, file_size, pathname); goto exit; } if (gbl_verbose_mode) { - acpi_log_error - ("Input file: %s contains table [%4.4s], 0x%X (%u) bytes\n", - pathname, table->signature, file_size, file_size); + fprintf(stderr, + "Input file: %s contains table [%4.4s], 0x%X (%u) bytes\n", + pathname, table->signature, file_size, file_size); } table_status = ap_dump_table_buffer(table, 0, 0); diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index 71edeb4..dd5b861 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -65,7 +65,8 @@ static int ap_is_existing_file(char *pathname) struct stat stat_info; if (!stat(pathname, &stat_info)) { - acpi_log_error("Target path already exists, overwrite? [y|n] "); + fprintf(stderr, + "Target path already exists, overwrite? [y|n] "); if (getchar() != 'y') { return (-1); @@ -101,9 +102,9 @@ int ap_open_output_file(char *pathname) /* Point stdout to the file */ - file = acpi_os_open_file(pathname, ACPI_FILE_WRITING); + file = fopen(pathname, "w"); if (!file) { - acpi_log_error("Could not open output file: %s\n", pathname); + fprintf(stderr, "Could not open output file: %s\n", pathname); return (-1); } @@ -164,29 +165,29 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance) strcat(filename, FILE_SUFFIX_BINARY_TABLE); if (gbl_verbose_mode) { - acpi_log_error - ("Writing [%4.4s] to binary file: %s 0x%X (%u) bytes\n", - table->signature, filename, table->length, table->length); + fprintf(stderr, + "Writing [%4.4s] to binary file: %s 0x%X (%u) bytes\n", + table->signature, filename, table->length, + table->length); } /* Open the file and dump the entire table in binary mode */ - file = acpi_os_open_file(filename, - ACPI_FILE_WRITING | ACPI_FILE_BINARY); + file = fopen(filename, "wb"); if (!file) { - acpi_log_error("Could not open output file: %s\n", filename); + fprintf(stderr, "Could not open output file: %s\n", filename); return (-1); } - actual = acpi_os_write_file(file, table, 1, table_length); + actual = fwrite(table, 1, table_length, file); if (actual != table_length) { - acpi_log_error("Error writing binary output file: %s\n", - filename); - acpi_os_close_file(file); + fprintf(stderr, "Error writing binary output file: %s\n", + filename); + fclose(file); return (-1); } - acpi_os_close_file(file); + fclose(file); return (0); } @@ -213,10 +214,9 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, /* Must use binary mode */ - file = - acpi_os_open_file(pathname, ACPI_FILE_READING | ACPI_FILE_BINARY); + file = fopen(pathname, "rb"); if (!file) { - acpi_log_error("Could not open input file: %s\n", pathname); + fprintf(stderr, "Could not open input file: %s\n", pathname); return (NULL); } @@ -224,7 +224,8 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, file_size = cm_get_file_size(file); if (file_size == ACPI_UINT32_MAX) { - acpi_log_error("Could not get input file size: %s\n", pathname); + fprintf(stderr, + "Could not get input file size: %s\n", pathname); goto cleanup; } @@ -232,16 +233,17 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, buffer = ACPI_ALLOCATE_ZEROED(file_size); if (!buffer) { - acpi_log_error("Could not allocate file buffer of size: %u\n", - file_size); + fprintf(stderr, + "Could not allocate file buffer of size: %u\n", + file_size); goto cleanup; } /* Read the entire file */ - actual = acpi_os_read_file(file, buffer, 1, file_size); + actual = fread(buffer, 1, file_size, file); if (actual != file_size) { - acpi_log_error("Could not read input file: %s\n", pathname); + fprintf(stderr, "Could not read input file: %s\n", pathname); ACPI_FREE(buffer); buffer = NULL; goto cleanup; @@ -250,6 +252,6 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, *out_file_size = file_size; cleanup: - acpi_os_close_file(file); + fclose(file); return (buffer); } diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index 17ce0d7..f32968e 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -139,8 +139,8 @@ static int ap_insert_action(char *argument, u32 to_be_done) current_action++; if (current_action > AP_MAX_ACTIONS) { - acpi_log_error("Too many table options (max %u)\n", - AP_MAX_ACTIONS); + fprintf(stderr, "Too many table options (max %u)\n", + AP_MAX_ACTIONS); return (-1); } @@ -185,9 +185,9 @@ static int ap_do_options(int argc, char **argv) } else if (!strcmp(acpi_gbl_optarg, "off")) { gbl_dump_customized_tables = FALSE; } else { - acpi_log_error - ("%s: Cannot handle this switch, please use on|off\n", - acpi_gbl_optarg); + fprintf(stderr, + "%s: Cannot handle this switch, please use on|off\n", + acpi_gbl_optarg); return (-1); } continue; @@ -212,9 +212,9 @@ static int ap_do_options(int argc, char **argv) ACPI_MAX64_BYTE_WIDTH, &gbl_rsdp_base); if (ACPI_FAILURE(status)) { - acpi_log_error - ("%s: Could not convert to a physical address\n", - acpi_gbl_optarg); + fprintf(stderr, + "%s: Could not convert to a physical address\n", + acpi_gbl_optarg); return (-1); } continue; @@ -241,7 +241,7 @@ static int ap_do_options(int argc, char **argv) case 'z': /* Verbose mode */ gbl_verbose_mode = TRUE; - acpi_log_error(ACPI_COMMON_SIGNON(AP_UTILITY_NAME)); + fprintf(stderr, ACPI_COMMON_SIGNON(AP_UTILITY_NAME)); continue; /* @@ -353,8 +353,9 @@ int ACPI_SYSTEM_XFACE acpi_main(int argc, char *argv[]) default: - acpi_log_error("Internal error, invalid action: 0x%X\n", - action->to_be_done); + fprintf(stderr, + "Internal error, invalid action: 0x%X\n", + action->to_be_done); return (-1); } @@ -369,12 +370,12 @@ int ACPI_SYSTEM_XFACE acpi_main(int argc, char *argv[]) /* Summary for the output file */ file_size = cm_get_file_size(gbl_output_file); - acpi_log_error - ("Output file %s contains 0x%X (%u) bytes\n\n", - gbl_output_filename, file_size, file_size); + fprintf(stderr, + "Output file %s contains 0x%X (%u) bytes\n\n", + gbl_output_filename, file_size, file_size); } - acpi_os_close_file(gbl_output_file); + fclose(gbl_output_file); } return (status); -- cgit v0.10.2 From 911a9b842874f52429d0b0a420bf0fed22117611 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:45:22 +0800 Subject: ACPICA: Applications: Fix a potential issue that help messages may be dumped to acpi_gbl_debug_file ACPICA commit d1b7372c7eb89cdba3d3c239fb07e2fdc5abf880 This is a regression fix, restoring usage macro to its original implementation. There is an issue for usage macros, if an command line option changed acpi_gbl_debug_file, then the follow up usage message may be errornously dumped to the debug file. This is just a bug in theory, because currently acpi_gbl_debug_file can only be modified by acpibin and acpiexec. And this will not trigger such issue because: 1. For acpibin, acpi_gbl_debug_file will be modified by "-t" option and the program exits after processing this option without dumping help message or other error options. 2. For acpiexec, acpi_gbl_debug_file will only be modified by the open command, which happens after parsing the command line options, so no help message will be dumped into the debug file. But maintaining this logic is difficult, so this patch modifies acpi_os_printf() into printf() for usage macros so that the help messages are ensured to be dumped to the stdout. Lv Zheng. Link: https://github.com/acpica/acpica/commit/d1b7372c Link: https://bugs.acpica.org/show_bug.cgi?id=1142 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index bc38534..0bd6307 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -83,13 +83,13 @@ /* Macros for usage messages */ #define ACPI_USAGE_HEADER(usage) \ - acpi_os_printf ("Usage: %s\nOptions:\n", usage); + printf ("Usage: %s\nOptions:\n", usage); #define ACPI_USAGE_TEXT(description) \ - acpi_os_printf (description); + printf (description); #define ACPI_OPTION(name, description) \ - acpi_os_printf (" %-20s%s\n", name, description); + printf (" %-20s%s\n", name, description); /* Check for unexpected exceptions */ -- cgit v0.10.2 From d2d48eae4639f7d74e9d91e7698f566bdbd8cdd6 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:45:32 +0800 Subject: ACPICA: EFI: Port acpidump to EDK2 environment ACPICA commit 790b8bae858d6d97da6099c9f8485d4760035a0c Linux kernel is not affected by this change. Link: https://github.com/acpica/acpica/commit/790b8bae Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 3dbfa27..f9af0e0 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -220,7 +220,7 @@ * included after including all other host environmental definitions, in * order to override the definitions. */ -#elif defined(_AED_EFI) || defined(_GNU_EFI) +#elif defined(_AED_EFI) || defined(_GNU_EFI) || defined(_EDK2_EFI) #include "acefi.h" #else diff --git a/include/acpi/platform/acenvex.h b/include/acpi/platform/acenvex.h index b672615..58ca39c 100644 --- a/include/acpi/platform/acenvex.h +++ b/include/acpi/platform/acenvex.h @@ -67,7 +67,7 @@ * included after including all other host environmental definitions, in * order to override the definitions. */ -#elif defined(_AED_EFI) || defined(_GNU_EFI) +#elif defined(_AED_EFI) || defined(_GNU_EFI) || defined(_EDK2_EFI) #include "acefiex.h" #endif -- cgit v0.10.2 From 9fa1cebdbfff3db8953cebca8ee327d75edefc40 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:45:40 +0800 Subject: ACPICA: OSL: Cleanup the inclusion order of the compiler-specific headers ACPICA commit a760a98ec84b1ec782e0bff5f6612af6fb89c10c Originally compiler specific headers are included by the host-specific headers. This makes build configuration management very inconvenient. And many inclusion order issues can be hidden accross different host OSes. It will then likely that some host builds will be broken just because of fixing some inclusion order issues for other host builds. This patch splits the compiler-specific header inclusions out of the host-specific headers so that compiler-specific inclusion order issues will not get entangled in the host-specific inclusion orders. Note that intel compiler defines __GNUC__, so this patch contains special handling because acintel.h and acgcc.h should be mutual exclusive. Link: https://github.com/acpica/acpica/commit/a760a98e Link: https://bugs.acpica.org/show_bug.cgi?id=1303 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index f9af0e0..34cce72 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -166,10 +166,21 @@ /****************************************************************************** * * Host configuration files. The compiler configuration files are included - * by the host files. + * first. * *****************************************************************************/ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +#include + +#elif defined(_MSC_VER) +#include "acmsvc.h" + +#elif defined(__INTEL_COMPILER) +#include "acintel.h" + +#endif + #if defined(_LINUX) || defined(__linux__) #include diff --git a/include/acpi/platform/acenvex.h b/include/acpi/platform/acenvex.h index 58ca39c..060fb08 100644 --- a/include/acpi/platform/acenvex.h +++ b/include/acpi/platform/acenvex.h @@ -56,9 +56,6 @@ #if defined(_LINUX) || defined(__linux__) #include -#elif defined(WIN32) -#include "acwinex.h" - #elif defined(__DragonFly__) #include "acdragonflyex.h" @@ -72,6 +69,11 @@ #endif +#if defined(_MSC_VER) +#include "acmsvcex.h" + +#endif + /*! [End] no source code translation !*/ #endif /* __ACENVEX_H__ */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 1380ec7..a5d98d1 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -215,8 +215,4 @@ #endif /* __KERNEL__ */ -/* Linux uses GCC */ - -#include - #endif /* __ACLINUX_H__ */ -- cgit v0.10.2 From 34f3a91b3bf3e886c83309fc9f9c27765c27e16d Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:45:47 +0800 Subject: ACPICA: OSL: Fix a regression that old GCC requires a workaround for strchr() ACPICA commit be836c36454a624a4fb1d17234080ef8c07993fc There is a GCC false-warning issue on specific GCC versions that "strchr" will be preprocessed and extracted to contain !__buildin_constant_p() checker and it surely is a constant logical value "1" for strchr() arguments. Then -Wlogical-op errorneously reports a warning. The regression is triggered after the standard headers are re-ordered in the EFI porting task. This patch fixes this regression by moving the workaround to a new position after including all other standard headers. Link: https://github.com/acpica/acpica/commit/be836c36 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenvex.h b/include/acpi/platform/acenvex.h index 060fb08..b3171b9 100644 --- a/include/acpi/platform/acenvex.h +++ b/include/acpi/platform/acenvex.h @@ -69,7 +69,10 @@ #endif -#if defined(_MSC_VER) +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +#include "acgccex.h" + +#elif defined(_MSC_VER) #include "acmsvcex.h" #endif diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h index 9f79e98..8f66aaa 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -70,17 +70,6 @@ */ #define ACPI_UNUSED_VAR __attribute__ ((unused)) -/* - * Some versions of gcc implement strchr() with a buggy macro. So, - * undef it here. Prevents error messages of this form (usually from the - * file getopt.c): - * - * error: logical '&&' with non-zero constant will always evaluate as true - */ -#ifdef strchr -#undef strchr -#endif - /* GCC supports __VA_ARGS__ in macros */ #define COMPILER_VA_MACRO 1 diff --git a/include/acpi/platform/acgccex.h b/include/acpi/platform/acgccex.h new file mode 100644 index 0000000..46ead2c --- /dev/null +++ b/include/acpi/platform/acgccex.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * + * Name: acgccex.h - Extra GCC specific defines, etc. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2016, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACGCCEX_H__ +#define __ACGCCEX_H__ + +/* + * Some versions of gcc implement strchr() with a buggy macro. So, + * undef it here. Prevents error messages of this form (usually from the + * file getopt.c): + * + * error: logical '&&' with non-zero constant will always evaluate as true + */ +#ifdef strchr +#undef strchr +#endif + +#endif /* __ACGCCEX_H__ */ -- cgit v0.10.2 From 6bd483c0367f1e1c9d4e970d6ccdadf53d571916 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 4 Aug 2016 16:45:54 +0800 Subject: ACPICA: Update version to 20160729 ACPICA commit 1e997ab9a5b939533374fd567681f881cb97c4c7 Version 20160729. Link: https://github.com/acpica/acpica/commit/1e997ab9 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index d94e5c0..8b44717 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20160422 +#define ACPI_CA_VERSION 0x20160729 #include #include -- cgit v0.10.2 From 18864cc4892d207bf8bb81898f5dc7fe9e66d6f0 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 4 Aug 2016 16:43:45 +0800 Subject: ACPI / sysfs: Use new GPE masking mechanism in GPE interface Now GPE can be masked via the new acpi_mask_gpe() API and this patch modifies /sys/firmware/acpi/interrupts/gpexx to use this new facility. Writes "mask/unmask" to this file now invokes acpi_mask_gpe(). Reads from this file now returns new "EN/STS" when the corresponding GPE hardware register's EN/STS bits are flagged, and new "masked/unmasked" attribute to indicate the status of the masking mechanism. Signed-off-by: Lv Zheng [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2b38c1b..9788663 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -572,7 +572,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status); - if (pwr_btn_status & ACPI_EVENT_FLAG_SET) { + if (pwr_btn_status & ACPI_EVENT_FLAG_STATUS_SET) { acpi_clear_event(ACPI_EVENT_POWER_BUTTON); /* Flag for later */ pwr_btn_event_pending = true; diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 358165e..703c993 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -597,14 +597,27 @@ static ssize_t counter_show(struct kobject *kobj, if (result) goto end; + if (status & ACPI_EVENT_FLAG_ENABLE_SET) + size += sprintf(buf + size, " EN"); + else + size += sprintf(buf + size, " "); + if (status & ACPI_EVENT_FLAG_STATUS_SET) + size += sprintf(buf + size, " STS"); + else + size += sprintf(buf + size, " "); + if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) - size += sprintf(buf + size, " invalid"); + size += sprintf(buf + size, " invalid "); else if (status & ACPI_EVENT_FLAG_ENABLED) - size += sprintf(buf + size, " enabled"); + size += sprintf(buf + size, " enabled "); else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) - size += sprintf(buf + size, " wake_enabled"); + size += sprintf(buf + size, " wake_enabled"); else - size += sprintf(buf + size, " disabled"); + size += sprintf(buf + size, " disabled "); + if (status & ACPI_EVENT_FLAG_MASKED) + size += sprintf(buf + size, " masked "); + else + size += sprintf(buf + size, " unmasked"); end: size += sprintf(buf + size, "\n"); @@ -655,8 +668,12 @@ static ssize_t counter_set(struct kobject *kobj, !(status & ACPI_EVENT_FLAG_ENABLED)) result = acpi_enable_gpe(handle, index); else if (!strcmp(buf, "clear\n") && - (status & ACPI_EVENT_FLAG_SET)) + (status & ACPI_EVENT_FLAG_STATUS_SET)) result = acpi_clear_gpe(handle, index); + else if (!strcmp(buf, "mask\n")) + result = acpi_mask_gpe(handle, index, TRUE); + else if (!strcmp(buf, "unmask\n")) + result = acpi_mask_gpe(handle, index, FALSE); else if (!kstrtoul(buf, 0, &tmp)) all_counters[index].count = tmp; else @@ -664,13 +681,13 @@ static ssize_t counter_set(struct kobject *kobj, } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { int event = index - num_gpes; if (!strcmp(buf, "disable\n") && - (status & ACPI_EVENT_FLAG_ENABLED)) + (status & ACPI_EVENT_FLAG_ENABLE_SET)) result = acpi_disable_event(event, ACPI_NOT_ISR); else if (!strcmp(buf, "enable\n") && - !(status & ACPI_EVENT_FLAG_ENABLED)) + !(status & ACPI_EVENT_FLAG_ENABLE_SET)) result = acpi_enable_event(event, ACPI_NOT_ISR); else if (!strcmp(buf, "clear\n") && - (status & ACPI_EVENT_FLAG_SET)) + (status & ACPI_EVENT_FLAG_STATUS_SET)) result = acpi_clear_event(event); else if (!kstrtoul(buf, 0, &tmp)) all_counters[index].count = tmp; -- cgit v0.10.2 From df45db6177f8dde380d44149cca46ad800a00575 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 3 Aug 2016 09:07:58 +0800 Subject: ACPI / EC: Add PM operations for suspend/resume noirq stage It is reported that on some platforms, resume speed is not fast. The cause is: in noirq stage, EC driver is working in polling mode, and each state machine advancement requires a context switch. The context switch is not necessary to the EC driver's polling mode. This patch implements PM hooks to automatically switch the driver to/from the busy polling mode to eliminate the overhead caused by the context switch. This finally contributes to the tuning result: acpi_pm_finish() execution time is improved from 192ms to 6ms. Signed-off-by: Lv Zheng Reported-and-tested-by: Todd E Brandt [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e7bd57c..6f6c7d1 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1619,6 +1619,58 @@ error: return ret; } +#ifdef CONFIG_PM_SLEEP +static void acpi_ec_enter_noirq(struct acpi_ec *ec) +{ + unsigned long flags; + + if (ec == first_ec) { + spin_lock_irqsave(&ec->lock, flags); + ec->saved_busy_polling = ec_busy_polling; + ec->saved_polling_guard = ec_polling_guard; + ec_busy_polling = true; + ec_polling_guard = 0; + ec_log_drv("interrupt blocked"); + spin_unlock_irqrestore(&ec->lock, flags); + } +} + +static void acpi_ec_leave_noirq(struct acpi_ec *ec) +{ + unsigned long flags; + + if (ec == first_ec) { + spin_lock_irqsave(&ec->lock, flags); + ec_busy_polling = ec->saved_busy_polling; + ec_polling_guard = ec->saved_polling_guard; + ec_log_drv("interrupt unblocked"); + spin_unlock_irqrestore(&ec->lock, flags); + } +} + +static int acpi_ec_suspend_noirq(struct device *dev) +{ + struct acpi_ec *ec = + acpi_driver_data(to_acpi_device(dev)); + + acpi_ec_enter_noirq(ec); + return 0; +} + +static int acpi_ec_resume_noirq(struct device *dev) +{ + struct acpi_ec *ec = + acpi_driver_data(to_acpi_device(dev)); + + acpi_ec_leave_noirq(ec); + return 0; +} +#endif + +static const struct dev_pm_ops acpi_ec_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) +}; + static int param_set_event_clearing(const char *val, struct kernel_param *kp) { int result = 0; @@ -1664,6 +1716,7 @@ static struct acpi_driver acpi_ec_driver = { .add = acpi_ec_add, .remove = acpi_ec_remove, }, + .drv.pm = &acpi_ec_pm, }; static inline int acpi_ec_query_init(void) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 940218f..6996121 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -174,6 +174,8 @@ struct acpi_ec { struct work_struct work; unsigned long timestamp; unsigned long nr_pending_queries; + bool saved_busy_polling; + unsigned int saved_polling_guard; }; extern struct acpi_ec *first_ec; -- cgit v0.10.2 From bd2058dc1aa88ac5690184f85a1e5051b12cd573 Mon Sep 17 00:00:00 2001 From: Kamlakant Patel Date: Tue, 9 Aug 2016 19:35:21 +0530 Subject: ACPI / APD: Add device HID for Vulcan SPI controller Add device HID for SPI controller on Broadcom Vulcan ARM64. The default frequency for SPI on Vulcan is 133MHz. Signed-off-by: Kamlakant Patel Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 1daf9c4..c80c8f8 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -87,6 +87,11 @@ static struct apd_device_desc xgene_i2c_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 100000000, }; + +static struct apd_device_desc vulcan_spi_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 133000000, +}; #endif #else @@ -149,6 +154,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { #endif #ifdef CONFIG_ARM64 { "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, + { "BRCM900D", APD_ADDR(vulcan_spi_desc) }, #endif { } }; -- cgit v0.10.2 From daae45caf5a042a0c7d147749ed1e4c970fc86d8 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 28 Jul 2016 02:25:41 +0200 Subject: ACPI / bus: Make acpi_get_first_physical_node() public Following the fwnode of a device is currently a one-way road: We provide ACPI_COMPANION() to obtain the fwnode but there's no (public) method to do the reverse. Granted, there may be multiple physical_nodes, but often the first one in the list is sufficient. A handy function to obtain it was introduced with commit 3b95bd160547 ("ACPI: introduce a function to find the first physical device"), but currently it's only available internally. We're about to add an EFI Device Path parser which needs this function. Consider the following device path: ACPI(PNP0A03,0)/PCI(28,2)/PCI(0,0) The PCI root is encoded as an ACPI device in the path, so the parser has to find the corresponding ACPI device, then find its physical node, find the PCI bridge in slot 1c (decimal 28), function 2 below it and finally find the PCI device in slot 0, function 0. To this end, make acpi_get_first_physical_node() public. Signed-off-by: Lukas Wunner Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 940218f..bd7c52d 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -116,7 +116,6 @@ bool acpi_device_is_present(struct acpi_device *adev); bool acpi_device_is_battery(struct acpi_device *adev); bool acpi_device_is_first_physical_node(struct acpi_device *adev, const struct device *dev); -struct device *acpi_get_first_physical_node(struct acpi_device *adev); /* -------------------------------------------------------------------------- Device Matching and Notification diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 4d8452c..536c12e 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -85,6 +85,8 @@ static inline const char *acpi_dev_name(struct acpi_device *adev) return dev_name(&adev->dev); } +struct device *acpi_get_first_physical_node(struct acpi_device *adev); + enum acpi_irq_model_id { ACPI_IRQ_MODEL_PIC = 0, ACPI_IRQ_MODEL_IOAPIC, @@ -634,6 +636,11 @@ static inline const char *acpi_dev_name(struct acpi_device *adev) return NULL; } +static inline struct device *acpi_get_first_physical_node(struct acpi_device *adev) +{ + return NULL; +} + static inline void acpi_early_init(void) { } static inline void acpi_subsystem_init(void) { } -- cgit v0.10.2 From 478573c93abd369c4850de55c387be43aa01e2e8 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 28 Jul 2016 02:25:41 +0200 Subject: driver core: Don't leak secondary fwnode on device removal If device_add_property_set() is called for a device, a secondary fwnode is allocated and assigned to the device but currently not freed once the device is removed. This can be triggered on Apple Macs if a Thunderbolt device is plugged in on boot since Apple's NHI EFI driver sets a number of properties for that device which are leaked on unplug. Signed-off-by: Lukas Wunner Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/core.c b/drivers/base/core.c index 0a8bdad..70c5be5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1266,6 +1266,7 @@ void device_del(struct device *dev) bus_remove_device(dev); device_pm_remove(dev); driver_deferred_probe_del(dev); + device_remove_properties(dev); /* Notify the platform of the removal, in case they * need to do anything... -- cgit v0.10.2 From 750f628be68e8b8e1624d8abd003b9f1fc758ed6 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 3 Aug 2016 16:01:24 +0800 Subject: ACPI / EC: Add EC_FLAGS_QUERY_ENABLED to reveal a hidden logic There is a hidden logic in the EC driver: 1. During boot, EC_FLAGS_QUERY_PENDING is responsible for blocking event handling; 2. During suspend, EC_FLAGS_STARTED is responsible for blocking event handling. This patch uses a new EC_FLAGS_QUERY_ENABLED flag to make this hidden logic explicit and have code cleaned up. No functional change. Signed-off-by: Lv Zheng Tested-by: Todd E Brandt Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 6f6c7d1..4ab34d7 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -104,6 +104,7 @@ enum ec_command { #define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */ enum { + EC_FLAGS_QUERY_ENABLED, /* Query is enabled */ EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */ @@ -239,6 +240,22 @@ static bool acpi_ec_started(struct acpi_ec *ec) !test_bit(EC_FLAGS_STOPPED, &ec->flags); } +static bool acpi_ec_event_enabled(struct acpi_ec *ec) +{ + /* + * There is an OSPM early stage logic. During the early stages + * (boot/resume), OSPMs shouldn't enable the event handling, only + * the EC transactions are allowed to be performed. + */ + if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) + return false; + /* + * The EC event handling is automatically disabled as soon as the + * EC driver is stopped. + */ + return test_bit(EC_FLAGS_STARTED, &ec->flags); +} + static bool acpi_ec_flushed(struct acpi_ec *ec) { return ec->reference_count == 1; @@ -429,7 +446,8 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) static void acpi_ec_submit_query(struct acpi_ec *ec) { - if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { + if (acpi_ec_event_enabled(ec) && + !test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { ec_dbg_evt("Command(%s) submitted/blocked", acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); ec->nr_pending_queries++; @@ -446,6 +464,52 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) } } +static inline void __acpi_ec_enable_event(struct acpi_ec *ec) +{ + if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) + ec_log_drv("event unblocked"); +} + +static inline void __acpi_ec_disable_event(struct acpi_ec *ec) +{ + if (test_and_clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) + ec_log_drv("event blocked"); +} + +/* + * Process _Q events that might have accumulated in the EC. + * Run with locked ec mutex. + */ +static void acpi_ec_clear(struct acpi_ec *ec) +{ + int i, status; + u8 value = 0; + + for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { + status = acpi_ec_query(ec, &value); + if (status || !value) + break; + } + if (unlikely(i == ACPI_EC_CLEAR_MAX)) + pr_warn("Warning: Maximum of %d stale EC events cleared\n", i); + else + pr_info("%d stale EC events cleared\n", i); +} + +static void acpi_ec_enable_event(struct acpi_ec *ec) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + if (acpi_ec_started(ec)) + __acpi_ec_enable_event(ec); + spin_unlock_irqrestore(&ec->lock, flags); + + /* Drain additional events if hardware requires that */ + if (EC_FLAGS_CLEAR_ON_RESUME) + acpi_ec_clear(ec); +} + static bool acpi_ec_guard_event(struct acpi_ec *ec) { bool guarded = true; @@ -832,27 +896,6 @@ acpi_handle ec_get_handle(void) } EXPORT_SYMBOL(ec_get_handle); -/* - * Process _Q events that might have accumulated in the EC. - * Run with locked ec mutex. - */ -static void acpi_ec_clear(struct acpi_ec *ec) -{ - int i, status; - u8 value = 0; - - for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { - status = acpi_ec_query(ec, &value); - if (status || !value) - break; - } - - if (unlikely(i == ACPI_EC_CLEAR_MAX)) - pr_warn("Warning: Maximum of %d stale EC events cleared\n", i); - else - pr_info("%d stale EC events cleared\n", i); -} - static void acpi_ec_start(struct acpi_ec *ec, bool resuming) { unsigned long flags; @@ -864,7 +907,8 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming) if (!resuming) { acpi_ec_submit_request(ec); ec_dbg_ref(ec, "Increase driver"); - } + } else + __acpi_ec_enable_event(ec); ec_log_drv("EC started"); } spin_unlock_irqrestore(&ec->lock, flags); @@ -896,7 +940,8 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) if (!suspending) { acpi_ec_complete_request(ec); ec_dbg_ref(ec, "Decrease driver"); - } + } else + __acpi_ec_disable_event(ec); clear_bit(EC_FLAGS_STARTED, &ec->flags); clear_bit(EC_FLAGS_STOPPED, &ec->flags); ec_log_drv("EC stopped"); @@ -927,8 +972,7 @@ void acpi_ec_unblock_transactions(void) /* Allow transactions to be carried out again */ acpi_ec_start(ec, true); - if (EC_FLAGS_CLEAR_ON_RESUME) - acpi_ec_clear(ec); + acpi_ec_enable_event(ec); } void acpi_ec_unblock_transactions_early(void) @@ -1234,7 +1278,6 @@ static struct acpi_ec *make_acpi_ec(void) if (!ec) return NULL; - ec->flags = 1 << EC_FLAGS_QUERY_PENDING; mutex_init(&ec->mutex); init_waitqueue_head(&ec->wait); INIT_LIST_HEAD(&ec->list); @@ -1421,11 +1464,7 @@ static int acpi_ec_add(struct acpi_device *device) acpi_walk_dep_device_list(ec->handle); /* EC is fully operational, allow queries */ - clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - - /* Clear stale _Q events if hardware might require that */ - if (EC_FLAGS_CLEAR_ON_RESUME) - acpi_ec_clear(ec); + acpi_ec_enable_event(ec); return ret; } -- cgit v0.10.2 From e923e8e79e18fd6be9162f1be6b99a002e9df2cb Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 3 Aug 2016 16:01:30 +0800 Subject: ACPI / EC: Fix an issue that SCI_EVT cannot be detected after event is enabled After enabling the EC event handling, Linux is still in the noirq stage, if there is no triggering source (EC transaction, GPE STS status), advance_transaction() will not be invoked and SCI_EVT cannot be detected. This patch adds one more triggering source after enabling the EC event handling to poll the pending SCI_EVT. Known issues: 1. Still no SCI_EVT triggering source There could still be no SCI_EVT triggering source after handling the first SCI_EVT (polled by this patch if any). Because after handling the first SCI_EVT, Linux could still be in noirq stage and there could still be no further triggering source in this stage. Then the second SCI_EVT indicated during this stage still cannot be detected by the EC driver. With this improvement applied, it is then possible to move acpi_ec_enable_event() out of the noirq stage to fix this issue (if the first SCI_EVT is handled out of the noirq stage, the follow-up SCI_EVTs should be able to trigger IRQs). Signed-off-by: Lv Zheng Tested-by: Todd E Brandt Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 4ab34d7..79305be 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -468,6 +468,8 @@ static inline void __acpi_ec_enable_event(struct acpi_ec *ec) { if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) ec_log_drv("event unblocked"); + if (!test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) + advance_transaction(ec); } static inline void __acpi_ec_disable_event(struct acpi_ec *ec) -- cgit v0.10.2 From c2b46d679b30c5c0d7eb47a21085943242bdd8dc Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 3 Aug 2016 16:01:36 +0800 Subject: ACPI / EC: Add PM operations to improve event handling for resume process This patch makes 2 changes: 1. Restore old behavior Originally, EC driver stops handling both events and transactions in acpi_ec_block_transactions(), and restarts to handle transactions in acpi_ec_unblock_transactions_early(), restarts to handle both events and transactions in acpi_ec_unblock_transactions(). While currently, EC driver still stops handling both events and transactions in acpi_ec_block_transactions(), but restarts to handle both events and transactions in acpi_ec_unblock_transactions_early(). This patch tries to restore the old behavior by dropping __acpi_ec_enable_event() from acpi_unblock_transactions_early(). 2. Improve old behavior However this still cannot fix the real issue as both of the acpi_ec_unblock_xxx() functions are invoked in the noirq stage. Since the EC driver actually doesn't implement the event handling in the polling mode, re-enabling the event handling too early in the noirq stage could result in the problem that if there is no triggering source causing advance_transaction() to be invoked, pending SCI_EVT cannot be detected by the EC driver and _Qxx cannot be triggered. It actually makes sense to restart the event handling in any point during resuming after the noirq stage. Just like the boot stage where the event handling is enabled in .add(), this patch further moves acpi_ec_enable_event() to .resume(). After doing that, the following 2 functions can be combined: acpi_ec_unblock_transactions_early()/acpi_ec_unblock_transactions(). The differences of the event handling availability between the old behavior (this patch isn't applied) and the new behavior (this patch is applied) are as follows: !Applied Applied before suspend Y Y suspend before EC Y Y suspend after EC Y Y suspend_late Y Y suspend_noirq Y (actually N) Y (actually N) resume_noirq Y (actually N) Y (actually N) resume_late Y (actually N) Y (actually N) resume before EC Y (actually N) Y (actually N) resume after EC Y (actually N) Y after resume Y (actually N) Y Where "actually N" means if there is no triggering source, the EC driver is actually not able to notice the pending SCI_EVT occurred in the noirq stage. So we can clearly see that this patch has improved the situation. Signed-off-by: Lv Zheng Tested-by: Todd E Brandt Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 79305be..8d5444d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -909,8 +909,7 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming) if (!resuming) { acpi_ec_submit_request(ec); ec_dbg_ref(ec, "Increase driver"); - } else - __acpi_ec_enable_event(ec); + } ec_log_drv("EC started"); } spin_unlock_irqrestore(&ec->lock, flags); @@ -966,19 +965,6 @@ void acpi_ec_block_transactions(void) void acpi_ec_unblock_transactions(void) { - struct acpi_ec *ec = first_ec; - - if (!ec) - return; - - /* Allow transactions to be carried out again */ - acpi_ec_start(ec, true); - - acpi_ec_enable_event(ec); -} - -void acpi_ec_unblock_transactions_early(void) -{ /* * Allow transactions to happen again (this function is called from * atomic context during wakeup, so we don't need to acquire the mutex). @@ -1706,10 +1692,20 @@ static int acpi_ec_resume_noirq(struct device *dev) acpi_ec_leave_noirq(ec); return 0; } + +static int acpi_ec_resume(struct device *dev) +{ + struct acpi_ec *ec = + acpi_driver_data(to_acpi_device(dev)); + + acpi_ec_enable_event(ec); + return 0; +} #endif static const struct dev_pm_ops acpi_ec_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) + SET_SYSTEM_SLEEP_PM_OPS(NULL, acpi_ec_resume) }; static int param_set_event_clearing(const char *val, struct kernel_param *kp) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 6996121..29f2063 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -189,7 +189,6 @@ int acpi_ec_ecdt_probe(void); int acpi_ec_dsdt_probe(void); void acpi_ec_block_transactions(void); void acpi_ec_unblock_transactions(void); -void acpi_ec_unblock_transactions_early(void); int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, acpi_handle handle, acpi_ec_query_func func, void *data); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2b38c1b..bb1e0d2 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -586,7 +586,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) */ acpi_disable_all_gpes(); /* Allow EC transactions to happen. */ - acpi_ec_unblock_transactions_early(); + acpi_ec_unblock_transactions(); suspend_nvs_restore(); @@ -784,7 +784,7 @@ static void acpi_hibernation_leave(void) /* Restore the NVS memory area */ suspend_nvs_restore(); /* Allow EC transactions to happen. */ - acpi_ec_unblock_transactions_early(); + acpi_ec_unblock_transactions(); } static void acpi_pm_thaw(void) -- cgit v0.10.2 From 39a2a2aa3e9e5538984e9130c92a6c889ad86435 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 3 Aug 2016 16:01:43 +0800 Subject: ACPI / EC: Add PM operations to improve event handling for suspend process In the original EC driver, though the event handling is not explicitly stopped, the EC driver is actually not able to handle events during the noirq stage as the EC driver is not prepared to handle the EC events in the polling mode. So if there is no advance_transaction() triggered, the EC driver couldn't notice the EC events. However, do we actually need to handle EC events during suspend/resume stage? EC events are mostly useless for the suspend/resume period (key strokes and battery/thermal updates, etc.,), and the useful ones (lid close, power/sleep button press) should have already been delivered to the OSPM to trigger the power saving operations. Thus this patch implements acpi_ec_disable_event() to be a reverse call of acpi_ec_enable_event(), with which, the EC driver is able to stop handling the EC events in a position before entering the noirq stage. Since there are actually 2 choices for us: 1. implement event handling in polling mode; 2. stop event handling before entering noirq stage. And this patch only implements the second choice using .suspend() callback. Thus this is experimental (first choice is better? or different hook position is better?). This patch finally keeps the old behavior by default and prepares a boot parameter to enable this feature. The differences of the event handling availability between the old behavior (this patch is not applied) and the new behavior (this patch is applied) are as follows: !FreezeEvents FreezeEvents before suspend Y Y suspend before EC Y Y suspend after EC Y N suspend_late Y N suspend_noirq Y (actually N) N resume_noirq Y (actually N) N resume_late Y (actually N) N resume before EC Y (actually N) N resume after EC Y Y after resume Y Y Where "actually N" means if there is no EC transactions, the EC driver is actually not able to notice the pending events. We can see that FreezeEvents is the only approach now can actually flush the EC event handling with both query commands and _Qxx evaluations flushed, other modes can only flush the EC event handling with only query commands flushed, _Qxx evaluations occurred after stopping the EC driver may end up failure due to the failure of the EC transaction carried out in the _Qxx control methods. We also can see that this feature should be able to trigger some platform notifications later than resuming other drivers. Signed-off-by: Lv Zheng Tested-by: Todd E Brandt Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 8d5444d..2bec709 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -146,6 +146,10 @@ static unsigned int ec_storm_threshold __read_mostly = 8; module_param(ec_storm_threshold, uint, 0644); MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); +static bool ec_freeze_events __read_mostly = false; +module_param(ec_freeze_events, bool, 0644); +MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume"); + struct acpi_ec_query_handler { struct list_head node; acpi_ec_query_func func; @@ -250,10 +254,18 @@ static bool acpi_ec_event_enabled(struct acpi_ec *ec) if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) return false; /* - * The EC event handling is automatically disabled as soon as the - * EC driver is stopped. + * However, disabling the event handling is experimental for late + * stage (suspend), and is controlled by the boot parameter of + * "ec_freeze_events": + * 1. true: The EC event handling is disabled before entering + * the noirq stage. + * 2. false: The EC event handling is automatically disabled as + * soon as the EC driver is stopped. */ - return test_bit(EC_FLAGS_STARTED, &ec->flags); + if (ec_freeze_events) + return acpi_ec_started(ec); + else + return test_bit(EC_FLAGS_STARTED, &ec->flags); } static bool acpi_ec_flushed(struct acpi_ec *ec) @@ -512,6 +524,38 @@ static void acpi_ec_enable_event(struct acpi_ec *ec) acpi_ec_clear(ec); } +static bool acpi_ec_query_flushed(struct acpi_ec *ec) +{ + bool flushed; + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + flushed = !ec->nr_pending_queries; + spin_unlock_irqrestore(&ec->lock, flags); + return flushed; +} + +static void __acpi_ec_flush_event(struct acpi_ec *ec) +{ + /* + * When ec_freeze_events is true, we need to flush events in + * the proper position before entering the noirq stage. + */ + wait_event(ec->wait, acpi_ec_query_flushed(ec)); + if (ec_query_wq) + flush_workqueue(ec_query_wq); +} + +static void acpi_ec_disable_event(struct acpi_ec *ec) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + __acpi_ec_disable_event(ec); + spin_unlock_irqrestore(&ec->lock, flags); + __acpi_ec_flush_event(ec); +} + static bool acpi_ec_guard_event(struct acpi_ec *ec) { bool guarded = true; @@ -941,7 +985,7 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) if (!suspending) { acpi_ec_complete_request(ec); ec_dbg_ref(ec, "Decrease driver"); - } else + } else if (!ec_freeze_events) __acpi_ec_disable_event(ec); clear_bit(EC_FLAGS_STARTED, &ec->flags); clear_bit(EC_FLAGS_STOPPED, &ec->flags); @@ -1693,6 +1737,16 @@ static int acpi_ec_resume_noirq(struct device *dev) return 0; } +static int acpi_ec_suspend(struct device *dev) +{ + struct acpi_ec *ec = + acpi_driver_data(to_acpi_device(dev)); + + if (ec_freeze_events) + acpi_ec_disable_event(ec); + return 0; +} + static int acpi_ec_resume(struct device *dev) { struct acpi_ec *ec = @@ -1705,7 +1759,7 @@ static int acpi_ec_resume(struct device *dev) static const struct dev_pm_ops acpi_ec_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) - SET_SYSTEM_SLEEP_PM_OPS(NULL, acpi_ec_resume) + SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume) }; static int param_set_event_clearing(const char *val, struct kernel_param *kp) -- cgit v0.10.2 From d30283057ecdf8c543ae757ae34db3d7fd2d7732 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 3 Aug 2016 16:01:50 +0800 Subject: ACPI / EC: Enable event freeze mode to improve event handling for suspend process This patch enables the event freeze mode, flushing the EC event handling in .suspend() callback. This feature is experimental, if it is bisected out to be the cause of the real issues, please report the issues to the kernel bugzilla for further root causing and improvement. This mode eliminates useless _Qxx handling during the power saving operations, thus can help to tune the power saving operations faster. Tests show that this mode can efficiently block flooding _Qxx during the suspend process and tune the speed of the suspend faster. Signed-off-by: Lv Zheng Tested-by: Todd E Brandt Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 2bec709..1925589 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -146,7 +146,7 @@ static unsigned int ec_storm_threshold __read_mostly = 8; module_param(ec_storm_threshold, uint, 0644); MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); -static bool ec_freeze_events __read_mostly = false; +static bool ec_freeze_events __read_mostly = true; module_param(ec_freeze_events, bool, 0644); MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume"); -- cgit v0.10.2 From 12c78ca2ab5e64b636ce085fb08f7685654a5f22 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 10 Aug 2016 17:24:15 +0200 Subject: ACPI / battery: Add sysfs representation after checking _BST Thus move sysfs_add_battery() after acpi_battery_get_state(), which doesn't require the power_supply. Prevents possible hanged tasks if acpi_battery_get_state() fails consistently (and takes a long time in doing so) when called inside acpi_battery_add(). In this situation the battery module first calls sysfs_add_battery(), which creates a power_supply, which spawns an async power_supply_deferred_register_work() task, which shall try to hold the parent battery device mutex (being already held) so this register work is set up after device initialization. If initialization takes long enough the thread will be eventually run and try to hold the mutex before acpi_battery_add() had the chance to finish. Eventually the 5 retries in acpi_battery_update_retry() fail, the error state is propagated, and results in sysfs_remove_battery() being called within the error handling paths of acpi_battery_add(), and the power_supply tear down too. This triggers a cancel_delayed_work_sync() of the deferred_register_work task, which ends up in schedule(). The end result is that the deferred task is blocked trying to acquire the parent device mutex, which is not released because the thread doing initialization (and failure handling) went to sleep awaiting for the deferred task to be cancelled. The hanged tasks look like this: INFO: task kworker/u8:0:6 blocked for more than 120 seconds. ... Call Trace: [] schedule+0x35/0x80 [] schedule_timeout+0x1ec/0x250 [] ? check_preempt_curr+0x52/0x90 [] ? ttwu_do_wakeup+0x19/0xe0 [] wait_for_common+0xc5/0x190 [] ? wake_up_q+0x70/0x70 [] wait_for_completion+0x1d/0x20 [] flush_work+0x111/0x1c0 [] ? flush_workqueue_prep_pwqs+0x1a0/0x1a0 [] __cancel_work_timer+0x9f/0x1d0 [] cancel_delayed_work_sync+0x13/0x20 [] power_supply_unregister+0x37/0xc0 [] sysfs_remove_battery+0x3d/0x52 [battery] [] acpi_battery_add+0x112/0x181 [battery] [] acpi_device_probe+0x54/0x19b [] driver_probe_device+0x22c/0x440 [] __driver_attach+0xd1/0xf0 [] ? driver_probe_device+0x440/0x440 [] bus_for_each_dev+0x6c/0xc0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x1c3/0x280 [] driver_register+0x60/0xe0 [] acpi_bus_register_driver+0x3b/0x43 [] acpi_battery_init_async+0x1c/0x1e [battery] [] async_run_entry_fn+0x48/0x150 [] process_one_work+0x1e9/0x440 [] worker_thread+0x4b/0x4f0 [] ? process_one_work+0x440/0x440 [] kthread+0xd8/0xf0 [] ret_from_fork+0x1f/0x40 [] ? kthread_worker_fn+0x180/0x180 INFO: task kworker/u8:4:282 blocked for more than 120 seconds. ... Call Trace: [] ? put_prev_entity+0x35/0x8b0 [] schedule+0x35/0x80 [] schedule_preempt_disabled+0xe/0x10 [] __mutex_lock_slowpath+0xb3/0x120 [] mutex_lock+0x1f/0x30 [] power_supply_deferred_register_work+0x2b/0x50 [] process_one_work+0x1e9/0x440 [] worker_thread+0x4b/0x4f0 [] ? process_one_work+0x440/0x440 [] ? process_one_work+0x440/0x440 [] kthread+0xd8/0xf0 [] ret_from_fork+0x1f/0x40 [] ? kthread_worker_fn+0x180/0x180 Making sysfs_add_battery() the last operation here means that the power_supply won't be created yet when the acpi_add_battery() failure handling happens, the deferred task won't even spawn, and sysfs_remove_battery will just skip over the NULL battery->bat. Signed-off-by: Carlos Garnacho Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index ab23479..93ecae5 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -733,15 +733,17 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume) return result; acpi_battery_init_alarm(battery); } + + result = acpi_battery_get_state(battery); + if (result) + return result; + acpi_battery_quirks(battery); + if (!battery->bat) { result = sysfs_add_battery(battery); if (result) return result; } - result = acpi_battery_get_state(battery); - if (result) - return result; - acpi_battery_quirks(battery); /* * Wakeup the system if battery is critical low -- cgit v0.10.2 From aca314efb177274b458f7e72c5ff375c80a5c2d0 Mon Sep 17 00:00:00 2001 From: hotran Date: Mon, 15 Aug 2016 17:14:05 -0700 Subject: mailbox: pcc: Support HW-Reduced Communication Subspace type 2 ACPI 6.1 has a PCC HW-Reduced Communication Subspace type 2 intended for use on HW-Reduce ACPI Platform, which requires read-modify-write sequence to acknowledge doorbell interrupt. This patch provides the implementation for the Communication Subspace Type 2. Signed-off-by: Hoan Tran Reviewed-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 043828d..08c87fa 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -68,11 +69,16 @@ #include "mailbox.h" #define MAX_PCC_SUBSPACES 256 +#define MBOX_IRQ_NAME "pcc-mbox" static struct mbox_chan *pcc_mbox_channels; /* Array of cached virtual address for doorbell registers */ static void __iomem **pcc_doorbell_vaddr; +/* Array of cached virtual address for doorbell ack registers */ +static void __iomem **pcc_doorbell_ack_vaddr; +/* Array of doorbell interrupts */ +static int *pcc_doorbell_irq; static struct mbox_controller pcc_mbox_ctrl = {}; /** @@ -91,6 +97,132 @@ static struct mbox_chan *get_pcc_channel(int id) return &pcc_mbox_channels[id]; } +/* + * PCC can be used with perf critical drivers such as CPPC + * So it makes sense to locally cache the virtual address and + * use it to read/write to PCC registers such as doorbell register + * + * The below read_register and write_registers are used to read and + * write from perf critical registers such as PCC doorbell register + */ +static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width) +{ + int ret_val = 0; + + switch (bit_width) { + case 8: + *val = readb(vaddr); + break; + case 16: + *val = readw(vaddr); + break; + case 32: + *val = readl(vaddr); + break; + case 64: + *val = readq(vaddr); + break; + default: + pr_debug("Error: Cannot read register of %u bit width", + bit_width); + ret_val = -EFAULT; + break; + } + return ret_val; +} + +static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width) +{ + int ret_val = 0; + + switch (bit_width) { + case 8: + writeb(val, vaddr); + break; + case 16: + writew(val, vaddr); + break; + case 32: + writel(val, vaddr); + break; + case 64: + writeq(val, vaddr); + break; + default: + pr_debug("Error: Cannot write register of %u bit width", + bit_width); + ret_val = -EFAULT; + break; + } + return ret_val; +} + +/** + * pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number + * @interrupt: GSI number. + * @flags: interrupt flags + * + * Returns: a valid linux IRQ number on success + * 0 or -EINVAL on failure + */ +static int pcc_map_interrupt(u32 interrupt, u32 flags) +{ + int trigger, polarity; + + if (!interrupt) + return 0; + + trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE + : ACPI_LEVEL_SENSITIVE; + + polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW + : ACPI_ACTIVE_HIGH; + + return acpi_register_gsi(NULL, interrupt, trigger, polarity); +} + +/** + * pcc_mbox_irq - PCC mailbox interrupt handler + */ +static irqreturn_t pcc_mbox_irq(int irq, void *p) +{ + struct acpi_generic_address *doorbell_ack; + struct acpi_pcct_hw_reduced *pcct_ss; + struct mbox_chan *chan = p; + u64 doorbell_ack_preserve; + u64 doorbell_ack_write; + u64 doorbell_ack_val; + int ret; + + pcct_ss = chan->con_priv; + + mbox_chan_received_data(chan, NULL); + + if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { + struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv; + u32 id = chan - pcc_mbox_channels; + + doorbell_ack = &pcct2_ss->doorbell_ack_register; + doorbell_ack_preserve = pcct2_ss->ack_preserve_mask; + doorbell_ack_write = pcct2_ss->ack_write_mask; + + ret = read_register(pcc_doorbell_ack_vaddr[id], + &doorbell_ack_val, + doorbell_ack->bit_width); + if (ret) + return IRQ_NONE; + + ret = write_register(pcc_doorbell_ack_vaddr[id], + (doorbell_ack_val & doorbell_ack_preserve) + | doorbell_ack_write, + doorbell_ack->bit_width); + if (ret) + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + /** * pcc_mbox_request_channel - PCC clients call this function to * request a pointer to their PCC subspace, from which they @@ -135,6 +267,18 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) chan->txdone_method |= TXDONE_BY_ACK; + if (pcc_doorbell_irq[subspace_id] > 0) { + int rc; + + rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id], + pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan); + if (unlikely(rc)) { + dev_err(dev, "failed to register PCC interrupt %d\n", + pcc_doorbell_irq[subspace_id]); + chan = ERR_PTR(rc); + } + } + spin_unlock_irqrestore(&chan->lock, flags); return chan; @@ -149,80 +293,30 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); */ void pcc_mbox_free_channel(struct mbox_chan *chan) { + u32 id = chan - pcc_mbox_channels; unsigned long flags; if (!chan || !chan->cl) return; + if (id >= pcc_mbox_ctrl.num_chans) { + pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n"); + return; + } + spin_lock_irqsave(&chan->lock, flags); chan->cl = NULL; chan->active_req = NULL; if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) chan->txdone_method = TXDONE_BY_POLL; + if (pcc_doorbell_irq[id] > 0) + devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan); + spin_unlock_irqrestore(&chan->lock, flags); } EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); -/* - * PCC can be used with perf critical drivers such as CPPC - * So it makes sense to locally cache the virtual address and - * use it to read/write to PCC registers such as doorbell register - * - * The below read_register and write_registers are used to read and - * write from perf critical registers such as PCC doorbell register - */ -static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width) -{ - int ret_val = 0; - - switch (bit_width) { - case 8: - *val = readb(vaddr); - break; - case 16: - *val = readw(vaddr); - break; - case 32: - *val = readl(vaddr); - break; - case 64: - *val = readq(vaddr); - break; - default: - pr_debug("Error: Cannot read register of %u bit width", - bit_width); - ret_val = -EFAULT; - break; - } - return ret_val; -} - -static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width) -{ - int ret_val = 0; - - switch (bit_width) { - case 8: - writeb(val, vaddr); - break; - case 16: - writew(val, vaddr); - break; - case 32: - writel(val, vaddr); - break; - case 64: - writeq(val, vaddr); - break; - default: - pr_debug("Error: Cannot write register of %u bit width", - bit_width); - ret_val = -EFAULT; - break; - } - return ret_val; -} /** * pcc_send_data - Called from Mailbox Controller code. Used @@ -296,8 +390,10 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header, if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) { pcct_ss = (struct acpi_pcct_hw_reduced *) header; - if (pcct_ss->header.type != - ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) { + if ((pcct_ss->header.type != + ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) + && (pcct_ss->header.type != + ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2)) { pr_err("Incorrect PCC Subspace type detected\n"); return -EINVAL; } @@ -307,6 +403,43 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header, } /** + * pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register + * There should be one entry per PCC client. + * @id: PCC subspace index. + * @pcct_ss: Pointer to the ACPI subtable header under the PCCT. + * + * Return: 0 for Success, else errno. + * + * This gets called for each entry in the PCC table. + */ +static int pcc_parse_subspace_irq(int id, + struct acpi_pcct_hw_reduced *pcct_ss) +{ + pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->doorbell_interrupt, + (u32)pcct_ss->flags); + if (pcc_doorbell_irq[id] <= 0) { + pr_err("PCC GSI %d not registered\n", + pcct_ss->doorbell_interrupt); + return -EINVAL; + } + + if (pcct_ss->header.type + == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) { + struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss; + + pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap( + pcct2_ss->doorbell_ack_register.address, + pcct2_ss->doorbell_ack_register.bit_width / 8); + if (!pcc_doorbell_ack_vaddr[id]) { + pr_err("Failed to ioremap PCC ACK register\n"); + return -ENOMEM; + } + } + + return 0; +} + +/** * acpi_pcc_probe - Parse the ACPI tree for the PCCT. * * Return: 0 for Success, else errno. @@ -316,7 +449,9 @@ static int __init acpi_pcc_probe(void) acpi_size pcct_tbl_header_size; struct acpi_table_header *pcct_tbl; struct acpi_subtable_header *pcct_entry; - int count, i; + struct acpi_table_pcct *acpi_pcct_tbl; + int count, i, rc; + int sum = 0; acpi_status status = AE_OK; /* Search for PCCT */ @@ -333,37 +468,66 @@ static int __init acpi_pcc_probe(void) sizeof(struct acpi_table_pcct), ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE, parse_pcc_subspace, MAX_PCC_SUBSPACES); + sum += (count > 0) ? count : 0; + + count = acpi_table_parse_entries(ACPI_SIG_PCCT, + sizeof(struct acpi_table_pcct), + ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2, + parse_pcc_subspace, MAX_PCC_SUBSPACES); + sum += (count > 0) ? count : 0; - if (count <= 0) { + if (sum == 0 || sum >= MAX_PCC_SUBSPACES) { pr_err("Error parsing PCC subspaces from PCCT\n"); return -EINVAL; } pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * - count, GFP_KERNEL); - + sum, GFP_KERNEL); if (!pcc_mbox_channels) { pr_err("Could not allocate space for PCC mbox channels\n"); return -ENOMEM; } - pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); + pcc_doorbell_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL); if (!pcc_doorbell_vaddr) { - kfree(pcc_mbox_channels); - return -ENOMEM; + rc = -ENOMEM; + goto err_free_mbox; + } + + pcc_doorbell_ack_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL); + if (!pcc_doorbell_ack_vaddr) { + rc = -ENOMEM; + goto err_free_db_vaddr; + } + + pcc_doorbell_irq = kcalloc(sum, sizeof(int), GFP_KERNEL); + if (!pcc_doorbell_irq) { + rc = -ENOMEM; + goto err_free_db_ack_vaddr; } /* Point to the first PCC subspace entry */ pcct_entry = (struct acpi_subtable_header *) ( (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); - for (i = 0; i < count; i++) { + acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl; + if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) + pcc_mbox_ctrl.txdone_irq = true; + + for (i = 0; i < sum; i++) { struct acpi_generic_address *db_reg; struct acpi_pcct_hw_reduced *pcct_ss; pcc_mbox_channels[i].con_priv = pcct_entry; + pcct_ss = (struct acpi_pcct_hw_reduced *) pcct_entry; + + if (pcc_mbox_ctrl.txdone_irq) { + rc = pcc_parse_subspace_irq(i, pcct_ss); + if (rc < 0) + goto err; + } + /* If doorbell is in system memory cache the virt address */ - pcct_ss = (struct acpi_pcct_hw_reduced *)pcct_entry; db_reg = &pcct_ss->doorbell_register; if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address, @@ -372,11 +536,21 @@ static int __init acpi_pcc_probe(void) ((unsigned long) pcct_entry + pcct_entry->length); } - pcc_mbox_ctrl.num_chans = count; + pcc_mbox_ctrl.num_chans = sum; pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); return 0; + +err: + kfree(pcc_doorbell_irq); +err_free_db_ack_vaddr: + kfree(pcc_doorbell_ack_vaddr); +err_free_db_vaddr: + kfree(pcc_doorbell_vaddr); +err_free_mbox: + kfree(pcc_mbox_channels); + return rc; } /** -- cgit v0.10.2 From 5bbb86aa4b8d84395e42cd05448820651d79f349 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Tue, 16 Aug 2016 14:39:38 -0600 Subject: ACPI / CPPC: restructure read/writes for efficient sys mapped reg ops For cases where sys mapped CPC registers need to be accessed frequently, it helps immensly to pre-map them rather than map and unmap for each operation. e.g. case where feedback counters are sys mem map registers. Restructure cpc_read/write and the cpc_regs structure to allow pre-mapping the system addresses and unmap them when the CPU exits. Signed-off-by: Ashwin Chaugule Signed-off-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 2e98173..fea58e2 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -62,7 +62,6 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); /* This layer handles all the PCC specifics for CPPC. */ static struct mbox_chan *pcc_channel; static void __iomem *pcc_comm_addr; -static u64 comm_base_addr; static int pcc_subspace_idx = -1; static bool pcc_channel_acquired; static ktime_t deadline; @@ -394,7 +393,6 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map); static int register_pcc_channel(int pcc_subspace_idx) { struct acpi_pcct_hw_reduced *cppc_ss; - unsigned int len; u64 usecs_lat; if (pcc_subspace_idx >= 0) { @@ -419,12 +417,6 @@ static int register_pcc_channel(int pcc_subspace_idx) return -ENODEV; } - /* - * This is the shared communication region - * for the OS and Platform to communicate over. - */ - comm_base_addr = cppc_ss->base_address; - len = cppc_ss->length; /* * cppc_ss->latency is just a Nominal value. In reality @@ -436,7 +428,7 @@ static int register_pcc_channel(int pcc_subspace_idx) pcc_mrtt = cppc_ss->min_turnaround_time; pcc_mpar = cppc_ss->max_access_rate; - pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len); + pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length); if (!pcc_comm_addr) { pr_err("Failed to ioremap PCC comm region mem\n"); return -ENOMEM; @@ -545,6 +537,8 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) goto out_free; } + cpc_ptr->num_entries = num_ent; + /* Second entry should be revision. */ cpc_obj = &out_obj->package.elements[1]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { @@ -585,7 +579,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) pr_debug("Mismatched PCC ids.\n"); goto out_free; } - } else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { + } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + if (gas_t->address) { + void __iomem *addr; + + addr = ioremap(gas_t->address, gas_t->bit_width/8); + if (!addr) + goto out_free; + cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr; + } + } else { /* Support only PCC and SYS MEM type regs */ pr_debug("Unsupported register type: %d\n", gas_t->space_id); goto out_free; @@ -623,6 +626,13 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) return 0; out_free: + /* Free all the mapped sys mem areas for this CPU */ + for (i = 2; i < cpc_ptr->num_entries; i++) { + void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr; + + if (addr) + iounmap(addr); + } kfree(cpc_ptr); out_buf_free: @@ -640,7 +650,17 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe); void acpi_cppc_processor_exit(struct acpi_processor *pr) { struct cpc_desc *cpc_ptr; + unsigned int i; + void __iomem *addr; cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); + + /* Free all the mapped sys mem areas for this CPU */ + for (i = 2; i < cpc_ptr->num_entries; i++) { + addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr; + if (addr) + iounmap(addr); + } + kfree(cpc_ptr); } EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); @@ -651,15 +671,27 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); * we can directly write to it. */ -static int cpc_read(struct cpc_reg *reg, u64 *val) +static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) { int ret_val = 0; + void __iomem *vaddr = 0; + struct cpc_reg *reg = ®_res->cpc_entry.reg; + + if (reg_res->type == ACPI_TYPE_INTEGER) { + *val = reg_res->cpc_entry.int_value; + return ret_val; + } *val = 0; - if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { - void __iomem *vaddr = GET_PCC_VADDR(reg->address); + if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) + vaddr = GET_PCC_VADDR(reg->address); + else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) + vaddr = reg_res->sys_mem_vaddr; + else + return acpi_os_read_memory((acpi_physical_address)reg->address, + val, reg->bit_width); - switch (reg->bit_width) { + switch (reg->bit_width) { case 8: *val = readb_relaxed(vaddr); break; @@ -674,23 +706,28 @@ static int cpc_read(struct cpc_reg *reg, u64 *val) break; default: pr_debug("Error: Cannot read %u bit width from PCC\n", - reg->bit_width); + reg->bit_width); ret_val = -EFAULT; - } - } else - ret_val = acpi_os_read_memory((acpi_physical_address)reg->address, - val, reg->bit_width); + } + return ret_val; } -static int cpc_write(struct cpc_reg *reg, u64 val) +static int cpc_write(struct cpc_register_resource *reg_res, u64 val) { int ret_val = 0; + void __iomem *vaddr = 0; + struct cpc_reg *reg = ®_res->cpc_entry.reg; - if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { - void __iomem *vaddr = GET_PCC_VADDR(reg->address); + if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) + vaddr = GET_PCC_VADDR(reg->address); + else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) + vaddr = reg_res->sys_mem_vaddr; + else + return acpi_os_write_memory((acpi_physical_address)reg->address, + val, reg->bit_width); - switch (reg->bit_width) { + switch (reg->bit_width) { case 8: writeb_relaxed(val, vaddr); break; @@ -705,13 +742,11 @@ static int cpc_write(struct cpc_reg *reg, u64 val) break; default: pr_debug("Error: Cannot write %u bit width to PCC\n", - reg->bit_width); + reg->bit_width); ret_val = -EFAULT; break; - } - } else - ret_val = acpi_os_write_memory((acpi_physical_address)reg->address, - val, reg->bit_width); + } + return ret_val; } @@ -754,16 +789,16 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) } } - cpc_read(&highest_reg->cpc_entry.reg, &high); + cpc_read(highest_reg, &high); perf_caps->highest_perf = high; - cpc_read(&lowest_reg->cpc_entry.reg, &low); + cpc_read(lowest_reg, &low); perf_caps->lowest_perf = low; - cpc_read(&ref_perf->cpc_entry.reg, &ref); + cpc_read(ref_perf, &ref); perf_caps->reference_perf = ref; - cpc_read(&nom_perf->cpc_entry.reg, &nom); + cpc_read(nom_perf, &nom); perf_caps->nominal_perf = nom; if (!ref) @@ -804,7 +839,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) /* Are any of the regs PCC ?*/ if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { ret = -EIO; @@ -812,8 +847,8 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) } } - cpc_read(&delivered_reg->cpc_entry.reg, &delivered); - cpc_read(&reference_reg->cpc_entry.reg, &reference); + cpc_read(delivered_reg, &delivered); + cpc_read(reference_reg, &reference); if (!delivered || !reference) { ret = -EFAULT; @@ -868,7 +903,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) * Skip writing MIN/MAX until Linux knows how to come up with * useful values. */ - cpc_write(&desired_reg->cpc_entry.reg, perf_ctrls->desired_perf); + cpc_write(desired_reg, perf_ctrls->desired_perf); /* Is this a PCC reg ?*/ if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { @@ -878,7 +913,6 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) } busy_channel: spin_unlock(&pcc_lock); - return ret; } EXPORT_SYMBOL_GPL(cppc_set_perf); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 284965c..36ff5c6 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -49,6 +49,7 @@ struct cpc_reg { */ struct cpc_register_resource { acpi_object_type type; + u64 __iomem *sys_mem_vaddr; union { struct cpc_reg reg; u64 int_value; -- cgit v0.10.2 From 850d64a4a63ea58c7d58161989d3bd4035ec6dfd Mon Sep 17 00:00:00 2001 From: "Prakash, Prashanth" Date: Tue, 16 Aug 2016 14:39:39 -0600 Subject: ACPI / CPPC: acquire pcc_lock only while accessing PCC subspace We need to acquire pcc_lock only when we are accessing registers that are in the PCC subspsace. Signed-off-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index fea58e2..93826c7 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -763,7 +763,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf, *nom_perf; u64 high, low, ref, nom; - int ret = 0; + int ret = 0, regs_in_pcc = 0; if (!cpc_desc) { pr_debug("No CPC descriptor for CPU:%d\n", cpunum); @@ -775,13 +775,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF]; nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF]; - spin_lock(&pcc_lock); - /* Are any of the regs PCC ?*/ if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + (lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + spin_lock(&pcc_lock); + regs_in_pcc = 1; /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { ret = -EIO; @@ -808,7 +808,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) ret = -EFAULT; out_err: - spin_unlock(&pcc_lock); + if (regs_in_pcc) + spin_unlock(&pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_get_perf_caps); @@ -825,7 +826,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_register_resource *delivered_reg, *reference_reg; u64 delivered, reference; - int ret = 0; + int ret = 0, regs_in_pcc = 0; if (!cpc_desc) { pr_debug("No CPC descriptor for CPU:%d\n", cpunum); @@ -835,11 +836,11 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR]; reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; - spin_lock(&pcc_lock); - /* Are any of the regs PCC ?*/ if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + spin_lock(&pcc_lock); + regs_in_pcc = 1; /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { ret = -EIO; @@ -865,7 +866,8 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) perf_fb_ctrs->prev_reference = reference; out_err: - spin_unlock(&pcc_lock); + if (regs_in_pcc) + spin_unlock(&pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs); @@ -890,10 +892,9 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; - spin_lock(&pcc_lock); - /* If this is PCC reg, check if channel is free before writing */ if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + spin_lock(&pcc_lock); ret = check_pcc_chan(); if (ret) goto busy_channel; @@ -912,7 +913,8 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) ret = -EIO; } busy_channel: - spin_unlock(&pcc_lock); + if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) + spin_unlock(&pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_set_perf); -- cgit v0.10.2 From 80b8286aeec056d21bffed2d1ece3904516e9c91 Mon Sep 17 00:00:00 2001 From: "Prakash, Prashanth" Date: Tue, 16 Aug 2016 14:39:40 -0600 Subject: ACPI / CPPC: support for batching CPPC requests CPPC defined in section 8.4.7 of ACPI 6.0 specification suggests "To amortize the cost of PCC transactions, OSPM should read or write all PCC registers via a single read or write command when possible" This patch enables opportunistic batching of frequency transition requests whenever the request happen to overlap in time. Currently the access to pcc is serialized by a spin lock which does not scale well as we increase the number of cores in the system. This patch improves the scalability by allowing the differnt CPU cores to update PCC subspace in parallel and by batching requests which will reduce the certain types of operation(checking command completion bit, ringing doorbell) by a significant margin. Profiling shows significant improvement in the overall effeciency to service freq. transition requests. With this patch we observe close to 30% of the frequency transition requests being batched with other requests while running apache bench on a ARM platform with 6 independent domains(or sets of related cpus). Signed-off-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 93826c7..5623fca 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -40,15 +40,35 @@ #include #include #include +#include +#include #include + /* - * Lock to provide mutually exclusive access to the PCC - * channel. e.g. When the remote updates the shared region - * with new data, the reader needs to be protected from - * other CPUs activity on the same channel. + * Lock to provide controlled access to the PCC channel. + * + * For performance critical usecases(currently cppc_set_perf) + * We need to take read_lock and check if channel belongs to OSPM before + * reading or writing to PCC subspace + * We need to take write_lock before transferring the channel ownership to + * the platform via a Doorbell + * This allows us to batch a number of CPPC requests if they happen to + * originate in about the same time + * + * For non-performance critical usecases(init) + * Take write_lock for all purposes which gives exclusive access */ -static DEFINE_SPINLOCK(pcc_lock); +static DECLARE_RWSEM(pcc_lock); + +/* Indicates if there are any pending/batched PCC write commands */ +static bool pending_pcc_write_cmd; + +/* Wait queue for CPUs whose requests were batched */ +static DECLARE_WAIT_QUEUE_HEAD(pcc_write_wait_q); + +/* Used to identify if a batched request is delivered to platform */ +static unsigned int pcc_write_cnt; /* * The cpc_desc structure contains the ACPI register details @@ -70,6 +90,11 @@ static unsigned int pcc_mpar, pcc_mrtt; /* pcc mapped address + header size + offset within PCC subspace */ #define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs)) +/* Check if a CPC regsiter is in PCC */ +#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ + (cpc)->cpc_entry.reg.space_id == \ + ACPI_ADR_SPACE_PLATFORM_COMM) + /* * Arbitrary Retries in case the remote processor is slow to respond * to PCC commands. Keeping it high enough to cover emulators where @@ -104,9 +129,13 @@ static int check_pcc_chan(void) return ret; } +/* + * This function transfers the ownership of the PCC to the platform + * So it must be called while holding write_lock(pcc_lock) + */ static int send_pcc_cmd(u16 cmd) { - int ret = -EIO; + int ret = -EIO, i; struct acpi_pcct_shared_memory *generic_comm_base = (struct acpi_pcct_shared_memory *) pcc_comm_addr; static ktime_t last_cmd_cmpl_time, last_mpar_reset; @@ -118,10 +147,19 @@ static int send_pcc_cmd(u16 cmd) * the channel before writing to PCC space */ if (cmd == CMD_READ) { + /* + * If there are pending cpc_writes, then we stole the channel + * before write completion, so first send a WRITE command to + * platform + */ + if (pending_pcc_write_cmd) + send_pcc_cmd(CMD_WRITE); + ret = check_pcc_chan(); if (ret) - return ret; - } + goto end; + } else /* CMD_WRITE */ + pending_pcc_write_cmd = FALSE; /* * Handle the Minimum Request Turnaround Time(MRTT) @@ -150,7 +188,8 @@ static int send_pcc_cmd(u16 cmd) time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset); if (time_delta < 60 * MSEC_PER_SEC) { pr_debug("PCC cmd not sent due to MPAR limit"); - return -EIO; + ret = -EIO; + goto end; } last_mpar_reset = ktime_get(); mpar_count = pcc_mpar; @@ -169,7 +208,7 @@ static int send_pcc_cmd(u16 cmd) if (ret < 0) { pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n", cmd, ret); - return ret; + goto end; } /* @@ -191,6 +230,23 @@ static int send_pcc_cmd(u16 cmd) } mbox_client_txdone(pcc_channel, ret); + +end: + if (cmd == CMD_WRITE) { + if (unlikely(ret)) { + for_each_possible_cpu(i) { + struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i); + if (!desc) + continue; + + if (desc->write_cmd_id == pcc_write_cnt) + desc->write_cmd_status = ret; + } + } + pcc_write_cnt++; + wake_up_all(&pcc_write_wait_q); + } + return ret; } @@ -776,12 +832,10 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF]; /* Are any of the regs PCC ?*/ - if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { - spin_lock(&pcc_lock); + if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) || + CPC_IN_PCC(ref_perf) || CPC_IN_PCC(nom_perf)) { regs_in_pcc = 1; + down_write(&pcc_lock); /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { ret = -EIO; @@ -809,7 +863,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) out_err: if (regs_in_pcc) - spin_unlock(&pcc_lock); + up_write(&pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_get_perf_caps); @@ -837,9 +891,8 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; /* Are any of the regs PCC ?*/ - if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || - (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { - spin_lock(&pcc_lock); + if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg)) { + down_write(&pcc_lock); regs_in_pcc = 1; /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { @@ -867,7 +920,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) out_err: if (regs_in_pcc) - spin_unlock(&pcc_lock); + up_write(&pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs); @@ -892,12 +945,36 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; - /* If this is PCC reg, check if channel is free before writing */ - if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { - spin_lock(&pcc_lock); - ret = check_pcc_chan(); - if (ret) - goto busy_channel; + /* + * This is Phase-I where we want to write to CPC registers + * -> We want all CPUs to be able to execute this phase in parallel + * + * Since read_lock can be acquired by multiple CPUs simultaneously we + * achieve that goal here + */ + if (CPC_IN_PCC(desired_reg)) { + down_read(&pcc_lock); /* BEGIN Phase-I */ + /* + * If there are pending write commands i.e pending_pcc_write_cmd + * is TRUE, then we know OSPM owns the channel as another CPU + * has already checked for command completion bit and updated + * the corresponding CPC registers + */ + if (!pending_pcc_write_cmd) { + ret = check_pcc_chan(); + if (ret) { + up_read(&pcc_lock); + return ret; + } + /* + * Update the pending_write to make sure a PCC CMD_READ + * will not arrive and steal the channel during the + * transition to write lock + */ + pending_pcc_write_cmd = TRUE; + } + cpc_desc->write_cmd_id = pcc_write_cnt; + cpc_desc->write_cmd_status = 0; } /* @@ -906,15 +983,68 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) */ cpc_write(desired_reg, perf_ctrls->desired_perf); - /* Is this a PCC reg ?*/ - if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { - /* Ring doorbell so Remote can get our perf request. */ - if (send_pcc_cmd(CMD_WRITE) < 0) - ret = -EIO; + if (CPC_IN_PCC(desired_reg)) + up_read(&pcc_lock); /* END Phase-I */ + /* + * This is Phase-II where we transfer the ownership of PCC to Platform + * + * Short Summary: Basically if we think of a group of cppc_set_perf + * requests that happened in short overlapping interval. The last CPU to + * come out of Phase-I will enter Phase-II and ring the doorbell. + * + * We have the following requirements for Phase-II: + * 1. We want to execute Phase-II only when there are no CPUs + * currently executing in Phase-I + * 2. Once we start Phase-II we want to avoid all other CPUs from + * entering Phase-I. + * 3. We want only one CPU among all those who went through Phase-I + * to run phase-II + * + * If write_trylock fails to get the lock and doesn't transfer the + * PCC ownership to the platform, then one of the following will be TRUE + * 1. There is at-least one CPU in Phase-I which will later execute + * write_trylock, so the CPUs in Phase-I will be responsible for + * executing the Phase-II. + * 2. Some other CPU has beaten this CPU to successfully execute the + * write_trylock and has already acquired the write_lock. We know for a + * fact it(other CPU acquiring the write_lock) couldn't have happened + * before this CPU's Phase-I as we held the read_lock. + * 3. Some other CPU executing pcc CMD_READ has stolen the + * down_write, in which case, send_pcc_cmd will check for pending + * CMD_WRITE commands by checking the pending_pcc_write_cmd. + * So this CPU can be certain that its request will be delivered + * So in all cases, this CPU knows that its request will be delivered + * by another CPU and can return + * + * After getting the down_write we still need to check for + * pending_pcc_write_cmd to take care of the following scenario + * The thread running this code could be scheduled out between + * Phase-I and Phase-II. Before it is scheduled back on, another CPU + * could have delivered the request to Platform by triggering the + * doorbell and transferred the ownership of PCC to platform. So this + * avoids triggering an unnecessary doorbell and more importantly before + * triggering the doorbell it makes sure that the PCC channel ownership + * is still with OSPM. + * pending_pcc_write_cmd can also be cleared by a different CPU, if + * there was a pcc CMD_READ waiting on down_write and it steals the lock + * before the pcc CMD_WRITE is completed. pcc_send_cmd checks for this + * case during a CMD_READ and if there are pending writes it delivers + * the write command before servicing the read command + */ + if (CPC_IN_PCC(desired_reg)) { + if (down_write_trylock(&pcc_lock)) { /* BEGIN Phase-II */ + /* Update only if there are pending write commands */ + if (pending_pcc_write_cmd) + send_pcc_cmd(CMD_WRITE); + up_write(&pcc_lock); /* END Phase-II */ + } else + /* Wait until pcc_write_cnt is updated by send_pcc_cmd */ + wait_event(pcc_write_wait_q, + cpc_desc->write_cmd_id != pcc_write_cnt); + + /* send_pcc_cmd updates the status in case of failure */ + ret = cpc_desc->write_cmd_status; } -busy_channel: - if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) - spin_unlock(&pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_set_perf); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 36ff5c6..d2101bc 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -61,6 +61,8 @@ struct cpc_desc { int num_entries; int version; int cpu_id; + int write_cmd_status; + int write_cmd_id; struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT]; struct acpi_psd_package domain_info; }; -- cgit v0.10.2 From be8b88d7d9877114172b32817d8eb3e85d3d8f99 Mon Sep 17 00:00:00 2001 From: "Prakash, Prashanth" Date: Tue, 16 Aug 2016 14:39:41 -0600 Subject: ACPI / CPPC: set a non-zero value for transition_latency Compute the expected transition latency for frequency transitions using the values from the PCCT tables when the desired perf register is in PCC. Signed-off-by: Prashanth Prakash Reviewed-by: Alexey Klimov Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 5623fca..6c54a8f 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -85,7 +85,7 @@ static void __iomem *pcc_comm_addr; static int pcc_subspace_idx = -1; static bool pcc_channel_acquired; static ktime_t deadline; -static unsigned int pcc_mpar, pcc_mrtt; +static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; /* pcc mapped address + header size + offset within PCC subspace */ #define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs)) @@ -473,7 +473,6 @@ static int register_pcc_channel(int pcc_subspace_idx) return -ENODEV; } - /* * cppc_ss->latency is just a Nominal value. In reality * the remote processor could be much slower to reply. @@ -483,6 +482,7 @@ static int register_pcc_channel(int pcc_subspace_idx) deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC); pcc_mrtt = cppc_ss->min_turnaround_time; pcc_mpar = cppc_ss->max_access_rate; + pcc_nominal = cppc_ss->latency; pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length); if (!pcc_comm_addr) { @@ -1048,3 +1048,45 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) return ret; } EXPORT_SYMBOL_GPL(cppc_set_perf); + +/** + * cppc_get_transition_latency - returns frequency transition latency in ns + * + * ACPI CPPC does not explicitly specifiy how a platform can specify the + * transition latency for perfromance change requests. The closest we have + * is the timing information from the PCCT tables which provides the info + * on the number and frequency of PCC commands the platform can handle. + */ +unsigned int cppc_get_transition_latency(int cpu_num) +{ + /* + * Expected transition latency is based on the PCCT timing values + * Below are definition from ACPI spec: + * pcc_nominal- Expected latency to process a command, in microseconds + * pcc_mpar - The maximum number of periodic requests that the subspace + * channel can support, reported in commands per minute. 0 + * indicates no limitation. + * pcc_mrtt - The minimum amount of time that OSPM must wait after the + * completion of a command before issuing the next command, + * in microseconds. + */ + unsigned int latency_ns = 0; + struct cpc_desc *cpc_desc; + struct cpc_register_resource *desired_reg; + + cpc_desc = per_cpu(cpc_desc_ptr, cpu_num); + if (!cpc_desc) + return CPUFREQ_ETERNAL; + + desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; + if (!CPC_IN_PCC(desired_reg)) + return CPUFREQ_ETERNAL; + + if (pcc_mpar) + latency_ns = 60 * (1000 * 1000 * 1000 / pcc_mpar); + + latency_ns = max(latency_ns, (pcc_nominal + pcc_mrtt) * 1000); + + return latency_ns; +} +EXPORT_SYMBOL_GPL(cppc_get_transition_latency); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 8882b8e..e6a3359 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -98,6 +98,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->max = cpu->perf_caps.highest_perf; policy->cpuinfo.min_freq = policy->min; policy->cpuinfo.max_freq = policy->max; + policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num); policy->shared_type = cpu->shared_type; if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index d2101bc..42cbeb9 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -131,5 +131,6 @@ extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs); extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); extern int acpi_get_psd_map(struct cpudata **); +extern unsigned int cppc_get_transition_latency(int cpu); #endif /* _CPPC_ACPI_H*/ -- cgit v0.10.2 From 158c998ea44ba30ae3d1bde535581c4436417530 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Tue, 16 Aug 2016 14:39:42 -0600 Subject: ACPI / CPPC: add sysfs support to compute delivered performance The CPPC tables contain entries for per CPU feedback counters which allows us to compute the delivered performance over a given interval of time. The math for delivered performance per the CPPCv5.0+ spec is: reference perf * delta(delivered perf ctr)/delta(ref perf ctr) Maintaining deltas of the counters in the kernel is messy, as it depends on when the reads are triggered. (e.g. via the cpufreq ->get() interface). Also the ->get() interace only returns one value, so cant return raw values. So instead, leave it to userspace to keep track of raw values and do its math for CPUs it cares about. delivered and reference perf counters are exposed via the same sysfs file to avoid the potential "skid", if these values are read individually from userspace. Signed-off-by: Prashanth Prakash Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 6c54a8f..f00fac3 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -95,6 +95,17 @@ static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; (cpc)->cpc_entry.reg.space_id == \ ACPI_ADR_SPACE_PLATFORM_COMM) +/* Evalutes to True if reg is a NULL register descriptor */ +#define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \ + (reg)->address == 0 && \ + (reg)->bit_width == 0 && \ + (reg)->bit_offset == 0 && \ + (reg)->access_width == 0) + +/* Evalutes to True if an optional cpc field is supported */ +#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \ + !!(cpc)->cpc_entry.int_value : \ + !IS_NULL_REG(&(cpc)->cpc_entry.reg)) /* * Arbitrary Retries in case the remote processor is slow to respond * to PCC commands. Keeping it high enough to cover emulators where @@ -102,6 +113,71 @@ static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; */ #define NUM_RETRIES 500 +struct cppc_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, + struct attribute *attr, char *buf); + ssize_t (*store)(struct kobject *kobj, + struct attribute *attr, const char *c, ssize_t count); +}; + +#define define_one_cppc_ro(_name) \ +static struct cppc_attr _name = \ +__ATTR(_name, 0444, show_##_name, NULL) + +#define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj) + +static ssize_t show_feedback_ctrs(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); + struct cppc_perf_fb_ctrs fb_ctrs = {0}; + + cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs); + + return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n", + fb_ctrs.reference, fb_ctrs.delivered); +} +define_one_cppc_ro(feedback_ctrs); + +static ssize_t show_reference_perf(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); + struct cppc_perf_fb_ctrs fb_ctrs = {0}; + + cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", + fb_ctrs.reference_perf); +} +define_one_cppc_ro(reference_perf); + +static ssize_t show_wraparound_time(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpc_desc *cpc_ptr = to_cpc_desc(kobj); + struct cppc_perf_fb_ctrs fb_ctrs = {0}; + + cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", fb_ctrs.ctr_wrap_time); + +} +define_one_cppc_ro(wraparound_time); + +static struct attribute *cppc_attrs[] = { + &feedback_ctrs.attr, + &reference_perf.attr, + &wraparound_time.attr, + NULL +}; + +static struct kobj_type cppc_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = cppc_attrs, +}; + static int check_pcc_chan(void) { int ret = -EIO; @@ -555,6 +631,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) union acpi_object *out_obj, *cpc_obj; struct cpc_desc *cpc_ptr; struct cpc_reg *gas_t; + struct device *cpu_dev; acpi_handle handle = pr->handle; unsigned int num_ent, i, cpc_rev; acpi_status status; @@ -678,6 +755,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) /* Everything looks okay */ pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); + /* Add per logical CPU nodes for reading its feedback counters. */ + cpu_dev = get_cpu_device(pr->id); + if (!cpu_dev) + goto out_free; + + ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj, + "acpi_cppc"); + if (ret) + goto out_free; + kfree(output.pointer); return 0; @@ -708,6 +795,7 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr) struct cpc_desc *cpc_ptr; unsigned int i; void __iomem *addr; + cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); /* Free all the mapped sys mem areas for this CPU */ @@ -717,6 +805,7 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr) iounmap(addr); } + kobject_put(&cpc_ptr->kobj); kfree(cpc_ptr); } EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); @@ -818,7 +907,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf, *nom_perf; - u64 high, low, ref, nom; + u64 high, low, nom; int ret = 0, regs_in_pcc = 0; if (!cpc_desc) { @@ -849,15 +938,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) cpc_read(lowest_reg, &low); perf_caps->lowest_perf = low; - cpc_read(ref_perf, &ref); - perf_caps->reference_perf = ref; - cpc_read(nom_perf, &nom); perf_caps->nominal_perf = nom; - if (!ref) - perf_caps->reference_perf = perf_caps->nominal_perf; - if (!high || !low || !nom) ret = -EFAULT; @@ -878,8 +961,9 @@ EXPORT_SYMBOL_GPL(cppc_get_perf_caps); int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) { struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); - struct cpc_register_resource *delivered_reg, *reference_reg; - u64 delivered, reference; + struct cpc_register_resource *delivered_reg, *reference_reg, + *ref_perf_reg, *ctr_wrap_reg; + u64 delivered, reference, ref_perf, ctr_wrap_time; int ret = 0, regs_in_pcc = 0; if (!cpc_desc) { @@ -889,9 +973,19 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR]; reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; + ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF]; + ctr_wrap_reg = &cpc_desc->cpc_regs[CTR_WRAP_TIME]; + + /* + * If refernce perf register is not supported then we should + * use the nominal perf value + */ + if (!CPC_SUPPORTED(ref_perf_reg)) + ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF]; /* Are any of the regs PCC ?*/ - if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg)) { + if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) || + CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) { down_write(&pcc_lock); regs_in_pcc = 1; /* Ring doorbell once to update PCC subspace */ @@ -903,21 +997,26 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) cpc_read(delivered_reg, &delivered); cpc_read(reference_reg, &reference); + cpc_read(ref_perf_reg, &ref_perf); + + /* + * Per spec, if ctr_wrap_time optional register is unsupported, then the + * performance counters are assumed to never wrap during the lifetime of + * platform + */ + ctr_wrap_time = (u64)(~((u64)0)); + if (CPC_SUPPORTED(ctr_wrap_reg)) + cpc_read(ctr_wrap_reg, &ctr_wrap_time); - if (!delivered || !reference) { + if (!delivered || !reference || !ref_perf) { ret = -EFAULT; goto out_err; } perf_fb_ctrs->delivered = delivered; perf_fb_ctrs->reference = reference; - - perf_fb_ctrs->delivered -= perf_fb_ctrs->prev_delivered; - perf_fb_ctrs->reference -= perf_fb_ctrs->prev_reference; - - perf_fb_ctrs->prev_delivered = delivered; - perf_fb_ctrs->prev_reference = reference; - + perf_fb_ctrs->reference_perf = ref_perf; + perf_fb_ctrs->ctr_wrap_time = ctr_wrap_time; out_err: if (regs_in_pcc) up_write(&pcc_lock); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 42cbeb9..f50b533 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -65,6 +65,7 @@ struct cpc_desc { int write_cmd_id; struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT]; struct acpi_psd_package domain_info; + struct kobject kobj; }; /* These are indexes into the per-cpu cpc_regs[]. Order is important. */ @@ -99,7 +100,6 @@ enum cppc_regs { struct cppc_perf_caps { u32 highest_perf; u32 nominal_perf; - u32 reference_perf; u32 lowest_perf; }; @@ -111,9 +111,9 @@ struct cppc_perf_ctrls { struct cppc_perf_fb_ctrs { u64 reference; - u64 prev_reference; u64 delivered; - u64 prev_delivered; + u64 reference_perf; + u64 ctr_wrap_time; }; /* Per CPU container for runtime CPPC management. */ -- cgit v0.10.2 From 8482ef8c6e684a1bba703c330e0bafe2d1d29ef7 Mon Sep 17 00:00:00 2001 From: "Prakash, Prashanth" Date: Tue, 16 Aug 2016 14:39:43 -0600 Subject: ACPI / CPPC: move all PCC related information into pcc_data There are several global variables in cppc driver that are related to PCC channel used for CPPC. This patch collects all such information into a single consolidated structure(cppc_pcc_data). Signed-off-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f00fac3..80c123f 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -45,30 +45,41 @@ #include -/* - * Lock to provide controlled access to the PCC channel. - * - * For performance critical usecases(currently cppc_set_perf) - * We need to take read_lock and check if channel belongs to OSPM before - * reading or writing to PCC subspace - * We need to take write_lock before transferring the channel ownership to - * the platform via a Doorbell - * This allows us to batch a number of CPPC requests if they happen to - * originate in about the same time - * - * For non-performance critical usecases(init) - * Take write_lock for all purposes which gives exclusive access - */ -static DECLARE_RWSEM(pcc_lock); +struct cppc_pcc_data { + struct mbox_chan *pcc_channel; + void __iomem *pcc_comm_addr; + int pcc_subspace_idx; + bool pcc_channel_acquired; + ktime_t deadline; + unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; -/* Indicates if there are any pending/batched PCC write commands */ -static bool pending_pcc_write_cmd; + bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ + unsigned int pcc_write_cnt; /* Running count of PCC write commands */ -/* Wait queue for CPUs whose requests were batched */ -static DECLARE_WAIT_QUEUE_HEAD(pcc_write_wait_q); + /* + * Lock to provide controlled access to the PCC channel. + * + * For performance critical usecases(currently cppc_set_perf) + * We need to take read_lock and check if channel belongs to OSPM + * before reading or writing to PCC subspace + * We need to take write_lock before transferring the channel + * ownership to the platform via a Doorbell + * This allows us to batch a number of CPPC requests if they happen + * to originate in about the same time + * + * For non-performance critical usecases(init) + * Take write_lock for all purposes which gives exclusive access + */ + struct rw_semaphore pcc_lock; + + /* Wait queue for CPUs whose requests were batched */ + wait_queue_head_t pcc_write_wait_q; +}; -/* Used to identify if a batched request is delivered to platform */ -static unsigned int pcc_write_cnt; +/* Structure to represent the single PCC channel */ +static struct cppc_pcc_data pcc_data = { + .pcc_subspace_idx = -1, +}; /* * The cpc_desc structure contains the ACPI register details @@ -79,16 +90,8 @@ static unsigned int pcc_write_cnt; */ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); -/* This layer handles all the PCC specifics for CPPC. */ -static struct mbox_chan *pcc_channel; -static void __iomem *pcc_comm_addr; -static int pcc_subspace_idx = -1; -static bool pcc_channel_acquired; -static ktime_t deadline; -static unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; - /* pcc mapped address + header size + offset within PCC subspace */ -#define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs)) +#define GET_PCC_VADDR(offs) (pcc_data.pcc_comm_addr + 0x8 + (offs)) /* Check if a CPC regsiter is in PCC */ #define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \ @@ -181,8 +184,8 @@ static struct kobj_type cppc_ktype = { static int check_pcc_chan(void) { int ret = -EIO; - struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_comm_addr; - ktime_t next_deadline = ktime_add(ktime_get(), deadline); + struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr; + ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline); /* Retry in case the remote processor was too slow to catch up. */ while (!ktime_after(ktime_get(), next_deadline)) { @@ -213,7 +216,7 @@ static int send_pcc_cmd(u16 cmd) { int ret = -EIO, i; struct acpi_pcct_shared_memory *generic_comm_base = - (struct acpi_pcct_shared_memory *) pcc_comm_addr; + (struct acpi_pcct_shared_memory *) pcc_data.pcc_comm_addr; static ktime_t last_cmd_cmpl_time, last_mpar_reset; static int mpar_count; unsigned int time_delta; @@ -228,24 +231,24 @@ static int send_pcc_cmd(u16 cmd) * before write completion, so first send a WRITE command to * platform */ - if (pending_pcc_write_cmd) + if (pcc_data.pending_pcc_write_cmd) send_pcc_cmd(CMD_WRITE); ret = check_pcc_chan(); if (ret) goto end; } else /* CMD_WRITE */ - pending_pcc_write_cmd = FALSE; + pcc_data.pending_pcc_write_cmd = FALSE; /* * Handle the Minimum Request Turnaround Time(MRTT) * "The minimum amount of time that OSPM must wait after the completion * of a command before issuing the next command, in microseconds" */ - if (pcc_mrtt) { + if (pcc_data.pcc_mrtt) { time_delta = ktime_us_delta(ktime_get(), last_cmd_cmpl_time); - if (pcc_mrtt > time_delta) - udelay(pcc_mrtt - time_delta); + if (pcc_data.pcc_mrtt > time_delta) + udelay(pcc_data.pcc_mrtt - time_delta); } /* @@ -259,7 +262,7 @@ static int send_pcc_cmd(u16 cmd) * not send the request to the platform after hitting the MPAR limit in * any 60s window */ - if (pcc_mpar) { + if (pcc_data.pcc_mpar) { if (mpar_count == 0) { time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset); if (time_delta < 60 * MSEC_PER_SEC) { @@ -268,7 +271,7 @@ static int send_pcc_cmd(u16 cmd) goto end; } last_mpar_reset = ktime_get(); - mpar_count = pcc_mpar; + mpar_count = pcc_data.pcc_mpar; } mpar_count--; } @@ -280,7 +283,7 @@ static int send_pcc_cmd(u16 cmd) writew_relaxed(0, &generic_comm_base->status); /* Ring doorbell */ - ret = mbox_send_message(pcc_channel, &cmd); + ret = mbox_send_message(pcc_data.pcc_channel, &cmd); if (ret < 0) { pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n", cmd, ret); @@ -299,13 +302,13 @@ static int send_pcc_cmd(u16 cmd) * command for proper handling of MRTT, so we need to check * for pcc_mrtt in addition to CMD_READ */ - if (cmd == CMD_READ || pcc_mrtt) { + if (cmd == CMD_READ || pcc_data.pcc_mrtt) { ret = check_pcc_chan(); - if (pcc_mrtt) + if (pcc_data.pcc_mrtt) last_cmd_cmpl_time = ktime_get(); } - mbox_client_txdone(pcc_channel, ret); + mbox_client_txdone(pcc_data.pcc_channel, ret); end: if (cmd == CMD_WRITE) { @@ -315,12 +318,12 @@ end: if (!desc) continue; - if (desc->write_cmd_id == pcc_write_cnt) + if (desc->write_cmd_id == pcc_data.pcc_write_cnt) desc->write_cmd_status = ret; } } - pcc_write_cnt++; - wake_up_all(&pcc_write_wait_q); + pcc_data.pcc_write_cnt++; + wake_up_all(&pcc_data.pcc_write_wait_q); } return ret; @@ -528,10 +531,10 @@ static int register_pcc_channel(int pcc_subspace_idx) u64 usecs_lat; if (pcc_subspace_idx >= 0) { - pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl, + pcc_data.pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_subspace_idx); - if (IS_ERR(pcc_channel)) { + if (IS_ERR(pcc_data.pcc_channel)) { pr_err("Failed to find PCC communication channel\n"); return -ENODEV; } @@ -542,7 +545,7 @@ static int register_pcc_channel(int pcc_subspace_idx) * PCC channels) and stored pointers to the * subspace communication region in con_priv. */ - cppc_ss = pcc_channel->con_priv; + cppc_ss = (pcc_data.pcc_channel)->con_priv; if (!cppc_ss) { pr_err("No PCC subspace found for CPPC\n"); @@ -555,19 +558,19 @@ static int register_pcc_channel(int pcc_subspace_idx) * So add an arbitrary amount of wait on top of Nominal. */ usecs_lat = NUM_RETRIES * cppc_ss->latency; - deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC); - pcc_mrtt = cppc_ss->min_turnaround_time; - pcc_mpar = cppc_ss->max_access_rate; - pcc_nominal = cppc_ss->latency; + pcc_data.deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC); + pcc_data.pcc_mrtt = cppc_ss->min_turnaround_time; + pcc_data.pcc_mpar = cppc_ss->max_access_rate; + pcc_data.pcc_nominal = cppc_ss->latency; - pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length); - if (!pcc_comm_addr) { + pcc_data.pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length); + if (!pcc_data.pcc_comm_addr) { pr_err("Failed to ioremap PCC comm region mem\n"); return -ENOMEM; } /* Set flag so that we dont come here for each CPU. */ - pcc_channel_acquired = true; + pcc_data.pcc_channel_acquired = true; } return 0; @@ -706,9 +709,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) * so extract it only once. */ if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { - if (pcc_subspace_idx < 0) - pcc_subspace_idx = gas_t->access_width; - else if (pcc_subspace_idx != gas_t->access_width) { + if (pcc_data.pcc_subspace_idx < 0) + pcc_data.pcc_subspace_idx = gas_t->access_width; + else if (pcc_data.pcc_subspace_idx != gas_t->access_width) { pr_debug("Mismatched PCC ids.\n"); goto out_free; } @@ -743,10 +746,13 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) goto out_free; /* Register PCC channel once for all CPUs. */ - if (!pcc_channel_acquired) { - ret = register_pcc_channel(pcc_subspace_idx); + if (!pcc_data.pcc_channel_acquired) { + ret = register_pcc_channel(pcc_data.pcc_subspace_idx); if (ret) goto out_free; + + init_rwsem(&pcc_data.pcc_lock); + init_waitqueue_head(&pcc_data.pcc_write_wait_q); } /* Plug PSD data into this CPUs CPC descriptor. */ @@ -924,7 +930,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) || CPC_IN_PCC(ref_perf) || CPC_IN_PCC(nom_perf)) { regs_in_pcc = 1; - down_write(&pcc_lock); + down_write(&pcc_data.pcc_lock); /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { ret = -EIO; @@ -946,7 +952,7 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) out_err: if (regs_in_pcc) - up_write(&pcc_lock); + up_write(&pcc_data.pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_get_perf_caps); @@ -986,7 +992,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) /* Are any of the regs PCC ?*/ if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) || CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) { - down_write(&pcc_lock); + down_write(&pcc_data.pcc_lock); regs_in_pcc = 1; /* Ring doorbell once to update PCC subspace */ if (send_pcc_cmd(CMD_READ) < 0) { @@ -1019,7 +1025,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) perf_fb_ctrs->ctr_wrap_time = ctr_wrap_time; out_err: if (regs_in_pcc) - up_write(&pcc_lock); + up_write(&pcc_data.pcc_lock); return ret; } EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs); @@ -1052,17 +1058,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) * achieve that goal here */ if (CPC_IN_PCC(desired_reg)) { - down_read(&pcc_lock); /* BEGIN Phase-I */ + down_read(&pcc_data.pcc_lock); /* BEGIN Phase-I */ /* * If there are pending write commands i.e pending_pcc_write_cmd * is TRUE, then we know OSPM owns the channel as another CPU * has already checked for command completion bit and updated * the corresponding CPC registers */ - if (!pending_pcc_write_cmd) { + if (!pcc_data.pending_pcc_write_cmd) { ret = check_pcc_chan(); if (ret) { - up_read(&pcc_lock); + up_read(&pcc_data.pcc_lock); return ret; } /* @@ -1070,9 +1076,9 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) * will not arrive and steal the channel during the * transition to write lock */ - pending_pcc_write_cmd = TRUE; + pcc_data.pending_pcc_write_cmd = TRUE; } - cpc_desc->write_cmd_id = pcc_write_cnt; + cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt; cpc_desc->write_cmd_status = 0; } @@ -1083,7 +1089,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) cpc_write(desired_reg, perf_ctrls->desired_perf); if (CPC_IN_PCC(desired_reg)) - up_read(&pcc_lock); /* END Phase-I */ + up_read(&pcc_data.pcc_lock); /* END Phase-I */ /* * This is Phase-II where we transfer the ownership of PCC to Platform * @@ -1131,15 +1137,15 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) * the write command before servicing the read command */ if (CPC_IN_PCC(desired_reg)) { - if (down_write_trylock(&pcc_lock)) { /* BEGIN Phase-II */ + if (down_write_trylock(&pcc_data.pcc_lock)) { /* BEGIN Phase-II */ /* Update only if there are pending write commands */ - if (pending_pcc_write_cmd) + if (pcc_data.pending_pcc_write_cmd) send_pcc_cmd(CMD_WRITE); - up_write(&pcc_lock); /* END Phase-II */ + up_write(&pcc_data.pcc_lock); /* END Phase-II */ } else /* Wait until pcc_write_cnt is updated by send_pcc_cmd */ - wait_event(pcc_write_wait_q, - cpc_desc->write_cmd_id != pcc_write_cnt); + wait_event(pcc_data.pcc_write_wait_q, + cpc_desc->write_cmd_id != pcc_data.pcc_write_cnt); /* send_pcc_cmd updates the status in case of failure */ ret = cpc_desc->write_cmd_status; @@ -1181,10 +1187,11 @@ unsigned int cppc_get_transition_latency(int cpu_num) if (!CPC_IN_PCC(desired_reg)) return CPUFREQ_ETERNAL; - if (pcc_mpar) - latency_ns = 60 * (1000 * 1000 * 1000 / pcc_mpar); + if (pcc_data.pcc_mpar) + latency_ns = 60 * (1000 * 1000 * 1000 / pcc_data.pcc_mpar); - latency_ns = max(latency_ns, (pcc_nominal + pcc_mrtt) * 1000); + latency_ns = max(latency_ns, pcc_data.pcc_nominal * 1000); + latency_ns = max(latency_ns, pcc_data.pcc_mrtt * 1000); return latency_ns; } -- cgit v0.10.2 From 139aee73f0c23b95a7e919b8f7e51ccf2d221181 Mon Sep 17 00:00:00 2001 From: "Prakash, Prashanth" Date: Tue, 16 Aug 2016 14:39:44 -0600 Subject: ACPI / CPPC: check for error bit in PCC status field PCC status field exposes an error bit(2) to indicate any errors during the execution of last comamnd. This patch checks the error bit before notifying success/failure to the cpufreq driver. Signed-off-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 80c123f..ed58883 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -54,6 +54,7 @@ struct cppc_pcc_data { unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ + bool platform_owns_pcc; /* Ownership of PCC subspace */ unsigned int pcc_write_cnt; /* Running count of PCC write commands */ /* @@ -79,6 +80,7 @@ struct cppc_pcc_data { /* Structure to represent the single PCC channel */ static struct cppc_pcc_data pcc_data = { .pcc_subspace_idx = -1, + .platform_owns_pcc = true, }; /* @@ -181,12 +183,15 @@ static struct kobj_type cppc_ktype = { .default_attrs = cppc_attrs, }; -static int check_pcc_chan(void) +static int check_pcc_chan(bool chk_err_bit) { - int ret = -EIO; + int ret = -EIO, status = 0; struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr; ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline); + if (!pcc_data.platform_owns_pcc) + return 0; + /* Retry in case the remote processor was too slow to catch up. */ while (!ktime_after(ktime_get(), next_deadline)) { /* @@ -194,8 +199,11 @@ static int check_pcc_chan(void) * platform and should have set the command completion bit when * PCC can be used by OSPM */ - if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) { + status = readw_relaxed(&generic_comm_base->status); + if (status & PCC_CMD_COMPLETE_MASK) { ret = 0; + if (chk_err_bit && (status & PCC_ERROR_MASK)) + ret = -EIO; break; } /* @@ -205,6 +213,11 @@ static int check_pcc_chan(void) udelay(3); } + if (likely(!ret)) + pcc_data.platform_owns_pcc = false; + else + pr_err("PCC check channel failed. Status=%x\n", status); + return ret; } @@ -234,7 +247,7 @@ static int send_pcc_cmd(u16 cmd) if (pcc_data.pending_pcc_write_cmd) send_pcc_cmd(CMD_WRITE); - ret = check_pcc_chan(); + ret = check_pcc_chan(false); if (ret) goto end; } else /* CMD_WRITE */ @@ -282,6 +295,8 @@ static int send_pcc_cmd(u16 cmd) /* Flip CMD COMPLETE bit */ writew_relaxed(0, &generic_comm_base->status); + pcc_data.platform_owns_pcc = true; + /* Ring doorbell */ ret = mbox_send_message(pcc_data.pcc_channel, &cmd); if (ret < 0) { @@ -290,23 +305,11 @@ static int send_pcc_cmd(u16 cmd) goto end; } - /* - * For READs we need to ensure the cmd completed to ensure - * the ensuing read()s can proceed. For WRITEs we dont care - * because the actual write()s are done before coming here - * and the next READ or WRITE will check if the channel - * is busy/free at the entry of this call. - * - * If Minimum Request Turnaround Time is non-zero, we need - * to record the completion time of both READ and WRITE - * command for proper handling of MRTT, so we need to check - * for pcc_mrtt in addition to CMD_READ - */ - if (cmd == CMD_READ || pcc_data.pcc_mrtt) { - ret = check_pcc_chan(); - if (pcc_data.pcc_mrtt) - last_cmd_cmpl_time = ktime_get(); - } + /* wait for completion and check for PCC errro bit */ + ret = check_pcc_chan(true); + + if (pcc_data.pcc_mrtt) + last_cmd_cmpl_time = ktime_get(); mbox_client_txdone(pcc_data.pcc_channel, ret); @@ -1059,25 +1062,18 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) */ if (CPC_IN_PCC(desired_reg)) { down_read(&pcc_data.pcc_lock); /* BEGIN Phase-I */ - /* - * If there are pending write commands i.e pending_pcc_write_cmd - * is TRUE, then we know OSPM owns the channel as another CPU - * has already checked for command completion bit and updated - * the corresponding CPC registers - */ - if (!pcc_data.pending_pcc_write_cmd) { - ret = check_pcc_chan(); + if (pcc_data.platform_owns_pcc) { + ret = check_pcc_chan(false); if (ret) { up_read(&pcc_data.pcc_lock); return ret; } - /* - * Update the pending_write to make sure a PCC CMD_READ - * will not arrive and steal the channel during the - * transition to write lock - */ - pcc_data.pending_pcc_write_cmd = TRUE; } + /* + * Update the pending_write to make sure a PCC CMD_READ will not + * arrive and steal the channel during the switch to write lock + */ + pcc_data.pending_pcc_write_cmd = true; cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt; cpc_desc->write_cmd_status = 0; } diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index f50b533..0e83cc3 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -24,7 +24,9 @@ #define CPPC_NUM_ENT 21 #define CPPC_REV 2 -#define PCC_CMD_COMPLETE 1 +#define PCC_CMD_COMPLETE_MASK (1 << 0) +#define PCC_ERROR_MASK (1 << 2) + #define MAX_CPC_REG_ENT 19 /* CPPC specific PCC commands. */ -- cgit v0.10.2 From dfa46c50f65b6ca10cea389327a6f1f1749bc633 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 17 Aug 2016 16:22:58 +0800 Subject: ACPI / button: Fix an issue in button.lid_init_state=ignore mode On most platforms, _LID returning value, lid open/close events are all reliable, but there are exceptions. Some AML tables report wrong initial lid state [1], and some of them never report lid open state [2]. The usage model on such buggy platforms is: 1. The initial lid state returned from _LID is not reliable; 2. The lid open event is not reliable; 3. The lid close event is always reliable, used by the platform firmware to trigger OSPM power saving operations. This usage model is not compliant to the Linux SW_LID model as the Linux userspace is very strict to the reliability of the open events. In order not to trigger issues on such buggy platforms, the ACPI button driver currently implements a lid_init_state=open quirk to send additional "open" event after resuming. However, this is still not sufficient because: 1. Some special usage models (e.x., the dark resume scenario) cannot be supported by this mode. 2. If a "close" event is not used to trigger "suspend", then the subsequent "close" events cannot be seen by the userspace. So we need to stop sending the additional "open" event and switch the driver to lid_init_state=ignore mode and make sure the platform triggered events can be reliably delivered to the userspace. The userspace programs then can be changed to not to be strict to the "open" events on such buggy platforms. Why will the subsequent "close" events be lost? This is because the input layer automatically filters redundant events for switch events. Thus given that the buggy AML tables do not guarantee paired "open"/"close" events, the ACPI button driver currently is not able to guarantee that the platform triggered reliable events can be always be seen by the userspace via SW_LID. This patch adds a mechanism to insert lid events as a compensation for the platform triggered ones to form a complete event switches in order to make sure that the platform triggered events can always be reliably delivered to the userspace. This essentially guarantees that the platform triggered reliable "close" events will always be relibly delivered to the userspace. However this mechanism is not suitable for lid_init_state=open/method as it should not send the complement switch event for the unreliable initial lid state notification. 2 unreliable events can trigger unexpected behavior. Thus this patch only implements this mechanism for lid_init_state=ignore. Known issues: 1. Possible alternative approach This approach is based on the fact that Linux requires a switch event type for LID events. Another approach is to use key event type to implement ACPI lid events. With SW event type, since ACPI button driver inserts wrong lid events, there could be a potential issue that an "open" event issued from some AML update methods could result in a wrong "close" event to be delivered to the userspace. While using KEY event type, there is no such problem. However there may not be such a kind of real case, and if there is such a case, it is worked around in this patch as the complement switch event is only generated for "close" event in order to deliver the reliable "close" event to the userspace. Link: https://bugzilla.kernel.org/show_bug.cgi?id=89211 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=106151 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=106941 # [2] Signed-off-by: Lv Zheng Suggested-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 31abb0b..e19f530 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -19,6 +19,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) "ACPI : button: " fmt + #include #include #include @@ -104,6 +106,8 @@ struct acpi_button { struct input_dev *input; char phys[32]; /* for input device */ unsigned long pushed; + int last_state; + ktime_t last_time; bool suspended; }; @@ -111,6 +115,10 @@ static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); static struct acpi_device *lid_device; static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; +static unsigned long lid_report_interval __read_mostly = 500; +module_param(lid_report_interval, ulong, 0644); +MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events"); + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -134,10 +142,79 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state) { struct acpi_button *button = acpi_driver_data(device); int ret; + ktime_t next_report; + bool do_update; + + /* + * In lid_init_state=ignore mode, if user opens/closes lid + * frequently with "open" missing, and "last_time" is also updated + * frequently, "close" cannot be delivered to the userspace. + * So "last_time" is only updated after a timeout or an actual + * switch. + */ + if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE || + button->last_state != !!state) + do_update = true; + else + do_update = false; + + next_report = ktime_add(button->last_time, + ms_to_ktime(lid_report_interval)); + if (button->last_state == !!state && + ktime_after(ktime_get(), next_report)) { + /* Complain the buggy firmware */ + pr_warn_once("The lid device is not compliant to SW_LID.\n"); - /* input layer checks if event is redundant */ - input_report_switch(button->input, SW_LID, !state); - input_sync(button->input); + /* + * Send the unreliable complement switch event: + * + * On most platforms, the lid device is reliable. However + * there are exceptions: + * 1. Platforms returning initial lid state as "close" by + * default after booting/resuming: + * https://bugzilla.kernel.org/show_bug.cgi?id=89211 + * https://bugzilla.kernel.org/show_bug.cgi?id=106151 + * 2. Platforms never reporting "open" events: + * https://bugzilla.kernel.org/show_bug.cgi?id=106941 + * On these buggy platforms, the usage model of the ACPI + * lid device actually is: + * 1. The initial returning value of _LID may not be + * reliable. + * 2. The open event may not be reliable. + * 3. The close event is reliable. + * + * But SW_LID is typed as input switch event, the input + * layer checks if the event is redundant. Hence if the + * state is not switched, the userspace cannot see this + * platform triggered reliable event. By inserting a + * complement switch event, it then is guaranteed that the + * platform triggered reliable one can always be seen by + * the userspace. + */ + if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) { + do_update = true; + /* + * Do generate complement switch event for "close" + * as "close" is reliable and wrong "open" won't + * trigger unexpected behaviors. + * Do not generate complement switch event for + * "open" as "open" is not reliable and wrong + * "close" will trigger unexpected behaviors. + */ + if (!state) { + input_report_switch(button->input, + SW_LID, state); + input_sync(button->input); + } + } + } + /* Send the platform triggered reliable event */ + if (do_update) { + input_report_switch(button->input, SW_LID, !state); + input_sync(button->input); + button->last_state = !!state; + button->last_time = ktime_get(); + } if (state) pm_wakeup_event(&device->dev, 0); @@ -411,6 +488,8 @@ static int acpi_button_add(struct acpi_device *device) strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID); sprintf(class, "%s/%s", ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); + button->last_state = !!acpi_lid_evaluate_state(device); + button->last_time = ktime_get(); } else { printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); error = -ENODEV; -- cgit v0.10.2 From f9151fcfcdfc662d4bfd74abf60c5e42722acf40 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 17 Aug 2016 16:23:14 +0800 Subject: ACPI / button: Add document for ACPI control method lid device restrictions This patch adds documentation for the usage model of the control method lid device on some buggy platforms. Signed-off-by: Lv Zheng Acked-by: Benjamin Tissoires Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/acpi-lid.txt b/Documentation/acpi/acpi-lid.txt new file mode 100644 index 0000000..effe7af --- /dev/null +++ b/Documentation/acpi/acpi-lid.txt @@ -0,0 +1,96 @@ +Special Usage Model of the ACPI Control Method Lid Device + +Copyright (C) 2016, Intel Corporation +Author: Lv Zheng + + +Abstract: + +Platforms containing lids convey lid state (open/close) to OSPMs using a +control method lid device. To implement this, the AML tables issue +Notify(lid_device, 0x80) to notify the OSPMs whenever the lid state has +changed. The _LID control method for the lid device must be implemented to +report the "current" state of the lid as either "opened" or "closed". + +For most platforms, both the _LID method and the lid notifications are +reliable. However, there are exceptions. In order to work with these +exceptional buggy platforms, special restrictions and expections should be +taken into account. This document describes the restrictions and the +expections of the Linux ACPI lid device driver. + + +1. Restrictions of the returning value of the _LID control method + +The _LID control method is described to return the "current" lid state. +However the word of "current" has ambiguity, some buggy AML tables return +the lid state upon the last lid notification instead of returning the lid +state upon the last _LID evaluation. There won't be difference when the +_LID control method is evaluated during the runtime, the problem is its +initial returning value. When the AML tables implement this control method +with cached value, the initial returning value is likely not reliable. +There are platforms always retun "closed" as initial lid state. + +2. Restrictions of the lid state change notifications + +There are buggy AML tables never notifying when the lid device state is +changed to "opened". Thus the "opened" notification is not guaranteed. But +it is guaranteed that the AML tables always notify "closed" when the lid +state is changed to "closed". The "closed" notification is normally used to +trigger some system power saving operations on Windows. Since it is fully +tested, it is reliable from all AML tables. + +3. Expections for the userspace users of the ACPI lid device driver + +The ACPI button driver exports the lid state to the userspace via the +following file: + /proc/acpi/button/lid/LID0/state +This file actually calls the _LID control method described above. And given +the previous explanation, it is not reliable enough on some platforms. So +it is advised for the userspace program to not to solely rely on this file +to determine the actual lid state. + +The ACPI button driver emits the following input event to the userspace: + SW_LID +The ACPI lid device driver is implemented to try to deliver the platform +triggered events to the userspace. However, given the fact that the buggy +firmware cannot make sure "opened"/"closed" events are paired, the ACPI +button driver uses the following 3 modes in order not to trigger issues. + +If the userspace hasn't been prepared to ignore the unreliable "opened" +events and the unreliable initial state notification, Linux users can use +the following kernel parameters to handle the possible issues: +A. button.lid_init_state=method: + When this option is specified, the ACPI button driver reports the + initial lid state using the returning value of the _LID control method + and whether the "opened"/"closed" events are paired fully relies on the + firmware implementation. + This option can be used to fix some platforms where the returning value + of the _LID control method is reliable but the initial lid state + notification is missing. + This option is the default behavior during the period the userspace + isn't ready to handle the buggy AML tables. +B. button.lid_init_state=open: + When this option is specified, the ACPI button driver always reports the + initial lid state as "opened" and whether the "opened"/"closed" events + are paired fully relies on the firmware implementation. + This may fix some platforms where the returning value of the _LID + control method is not reliable and the initial lid state notification is + missing. + +If the userspace has been prepared to ignore the unreliable "opened" events +and the unreliable initial state notification, Linux users should always +use the following kernel parameter: +C. button.lid_init_state=ignore: + When this option is specified, the ACPI button driver never reports the + initial lid state and there is a compensation mechanism implemented to + ensure that the reliable "closed" notifications can always be delievered + to the userspace by always pairing "closed" input events with complement + "opened" input events. But there is still no guarantee that the "opened" + notifications can be delivered to the userspace when the lid is actually + opens given that some AML tables do not send "opened" notifications + reliably. + In this mode, if everything is correctly implemented by the platform + firmware, the old userspace programs should still work. Otherwise, the + new userspace programs are required to work with the ACPI button driver. + This option will be the default behavior after the userspace is ready to + handle the buggy AML tables. -- cgit v0.10.2 From 7ff55d174cbfdd06245c20a7488da4e9499cfd11 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 23 Aug 2016 11:33:26 +0300 Subject: ACPI / APD: Provide build-in properties of the UART The UART driver, dw8250.c, needs some details regarding the Designware UART. For ACPI enumerated devices the values are hard-coded, but since the driver also reads the values from device properties, providing them with build-in properties. This allows us to later remove the hard-coded values from the driver. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 1daf9c4..5f112d8 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -42,6 +42,7 @@ struct apd_private_data; struct apd_device_desc { unsigned int flags; unsigned int fixed_clk_rate; + struct property_entry *properties; int (*setup)(struct apd_private_data *pdata); }; @@ -76,9 +77,17 @@ static struct apd_device_desc cz_i2c_desc = { .fixed_clk_rate = 133000000, }; +static struct property_entry uart_properties[] = { + PROPERTY_ENTRY_U32("reg-io-width", 4), + PROPERTY_ENTRY_U32("reg-shift", 2), + PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), + { }, +}; + static struct apd_device_desc cz_uart_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 48000000, + .properties = uart_properties, }; #endif @@ -125,6 +134,12 @@ static int acpi_apd_create_device(struct acpi_device *adev, goto err_out; } + if (dev_desc->properties) { + ret = device_add_properties(&adev->dev, dev_desc->properties); + if (ret) + goto err_out; + } + adev->driver_data = pdata; pdev = acpi_create_platform_device(adev); if (!IS_ERR_OR_NULL(pdev)) -- cgit v0.10.2 From a5565cf238daa6b1d5a260d844f857176d280886 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 23 Aug 2016 11:33:27 +0300 Subject: ACPI / LPSS: Provide build-in properties of the UART The UART driver, dw8250.c, needs some details regarding the Designware UART. For ACPI enumerated devices the values are hard-coded, but since the driver also reads the values from device properties, providing them with build-in properties. This allows us to later remove the hard-coded values from the driver. Signed-off-by: Heikki Krogerus Acked-by: Mika Westerberg Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 357a0b8..5520102 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -75,6 +75,7 @@ struct lpss_device_desc { const char *clk_con_id; unsigned int prv_offset; size_t prv_size_override; + struct property_entry *properties; void (*setup)(struct lpss_private_data *pdata); }; @@ -163,11 +164,19 @@ static const struct lpss_device_desc lpt_i2c_dev_desc = { .prv_offset = 0x800, }; +static struct property_entry uart_properties[] = { + PROPERTY_ENTRY_U32("reg-io-width", 4), + PROPERTY_ENTRY_U32("reg-shift", 2), + PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), + { }, +}; + static const struct lpss_device_desc lpt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, + .properties = uart_properties, }; static const struct lpss_device_desc lpt_sdio_dev_desc = { @@ -189,6 +198,7 @@ static const struct lpss_device_desc byt_uart_dev_desc = { .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, + .properties = uart_properties, }; static const struct lpss_device_desc bsw_uart_dev_desc = { @@ -197,6 +207,7 @@ static const struct lpss_device_desc bsw_uart_dev_desc = { .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, + .properties = uart_properties, }; static const struct lpss_device_desc byt_spi_dev_desc = { @@ -440,6 +451,12 @@ static int acpi_lpss_create_device(struct acpi_device *adev, goto err_out; } + if (dev_desc->properties) { + ret = device_add_properties(&adev->dev, dev_desc->properties); + if (ret) + goto err_out; + } + adev->driver_data = pdata; pdev = acpi_create_platform_device(adev); if (!IS_ERR_OR_NULL(pdev)) { -- cgit v0.10.2 From fa162a05de280e7e62f30eac4dc591e8f5052483 Mon Sep 17 00:00:00 2001 From: Al Stone Date: Fri, 19 Aug 2016 18:48:11 -0600 Subject: ACPI / tables: fix incorrect counts returned by acpi_parse_entries_array() The static function acpi_parse_entries_array() is provided an array of type struct acpi_subtable_proc that has a callback function and a count. The count should reflect how many times the callback has been called. However, the current code only increments the 0th element of the array, regardless of the number of entries in the array, or which callback has been invoked. The result is that we know the total number of callbacks made but we cannot determine which callbacks were made, nor how often. The fix is to index into the array of structs and increment the proper counts. There is one place in the x86 code for acpi_parse_madt_lapic_entries() where the counts for each callback are used. If no LAPICs *and* no X2APICs are found, an ENODEV is supposed to be returned; as it stands, the count of X2APICs will always be zero, regardless of what is in the MADT. Should there be no LAPICs, ENODEV will be returned in error, if there are X2APICs in the MADT. Otherwise, there are no other functional consequences of the count being done as it currently is; all other uses simply check that the return value from acpi_parse_entries_array() or passed back via its callers is either non-zero, an error, or in one case just ignored. In future patches, I will also need these counts to be correct; I need to count the number of instances of subtables of certain types within the MADT to determine whether or not an ACPI IORT is required or not, and report when it is not present when it should be. Signed-off-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 9f0ad6e..bf273c7 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -281,7 +281,7 @@ acpi_parse_entries_array(char *id, unsigned long table_size, proc[i].handler(entry, table_end)) return -EINVAL; - proc->count++; + proc[i].count++; break; } if (i != proc_num) -- cgit v0.10.2 From 8726d4f441505def8a488d50e50568403f6ad191 Mon Sep 17 00:00:00 2001 From: Al Stone Date: Fri, 19 Aug 2016 18:48:12 -0600 Subject: ACPI / tables: fix acpi_parse_entries_array() so it traverses all subtables The acpi_parse_entries_array() function currently returns the very first time there is any error found by one of the callback functions, or if one of the callbacks returns a non-zero value. However, the ACPI subtables being traversed could still have valid entries that could be used by one of the callback functions. And, if the comments are correct, that is what should happen -- always traverse all of the subtables, calling as many of the callbacks as possible. This patch makes the function consistent with its description so that it will properly invoke all callbacks for all matching entries, for all subtables, instead of stopping abruptly as it does today. This does change the semantics of using acpi_parse_entries_array(). In examining all users of the function, none of them rely on the current behavior; that is, there appears to be no assumption that either all subtables are traversed and all callbacks invoked, or that the function will return immediately on any error from a callback. Each callback operates independently. Hence, there should be no functional change due to this change in semantics. Future patches being prepared will rely on this new behavior; indeed, they were written assuming the acpi_parse_entries_array() function operated as its comments describe. For example, a callback that counts the number of subtables of a specific type can now be assured that as many subtables as possible have been enumerated. Signed-off-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index bf273c7..b7dac30 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -246,6 +246,7 @@ acpi_parse_entries_array(char *id, unsigned long table_size, struct acpi_subtable_header *entry; unsigned long table_end; int count = 0; + int errs = 0; int i; if (acpi_disabled) @@ -278,8 +279,10 @@ acpi_parse_entries_array(char *id, unsigned long table_size, if (entry->type != proc[i].id) continue; if (!proc[i].handler || - proc[i].handler(entry, table_end)) - return -EINVAL; + (!errs && proc[i].handler(entry, table_end))) { + errs++; + continue; + } proc[i].count++; break; @@ -305,7 +308,7 @@ acpi_parse_entries_array(char *id, unsigned long table_size, id, proc->id, count - max_entries, count); } - return count; + return errs ? -EINVAL : count; } int __init -- cgit v0.10.2 From 99b0efd7c886f4c985cb2727a86548413922cbe2 Mon Sep 17 00:00:00 2001 From: Al Stone Date: Fri, 19 Aug 2016 18:48:13 -0600 Subject: ACPI / tables: do not report the number of entries ignored by acpi_parse_entries() The function acpi_parse_entries_array() has a limiting parameter, max_entries, which tells the function to stop looking at subtables once that limit has been reached. If the limit is reached, it is reported. However, the logic is incorrect in that the loop to examine all subtables will always report that zero subtables have been ignored since it does not continue once the max_entries have been reached. One approach to fixing this would be to correct the logic so that all subtables are examined, even if we have hit the max_entries, but without executing all the callback functions. This could be risky since we cannot guarantee that no callback will ever have side effects that another callback depends on to work correctly. So, the simplest approach is to just remove the part of the error message that will always be incorrect. Signed-off-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index b7dac30..4dfbf49 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -304,8 +304,8 @@ acpi_parse_entries_array(char *id, unsigned long table_size, } if (max_entries && count > max_entries) { - pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", - id, proc->id, count - max_entries, count); + pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n", + id, proc->id, count); } return errs ? -EINVAL : count; -- cgit v0.10.2 From 20a875e2e86e73d13ec256781a7d55a7885868ec Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 23 Aug 2016 11:33:28 +0300 Subject: serial: 8250_dw: Add quirk for APM X-Gene SoC The APM X-Gene SoC UART is the only board that still needs the hard-coded values, so handle it separately in dw8250_quirks(). The other ACPI platforms are able to provide the values with device properties. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index e199696..5c0c123 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -298,12 +298,17 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_out = dw8250_serial_out32be; } } else if (has_acpi_companion(p->dev)) { - p->iotype = UPIO_MEM32; - p->regshift = 2; - p->serial_in = dw8250_serial_in32; + const struct acpi_device_id *id; + + id = acpi_match_device(p->dev->driver->acpi_match_table, + p->dev); + if (id && !strcmp(id->id, "APMC0D08")) { + p->iotype = UPIO_MEM32; + p->regshift = 2; + p->serial_in = dw8250_serial_in32; + data->uart_16550_compatible = true; + } p->set_termios = dw8250_set_termios; - /* So far none of there implement the Busy Functionality */ - data->uart_16550_compatible = true; } /* Platforms with iDMA */ -- cgit v0.10.2 From 65e958910a0342c2d802dea05eaf0de16c1b872a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 1 Sep 2016 13:37:08 -0700 Subject: ACPI / CPPC: Allow build with ACPI_CPU_FREQ_PSS config Some newer x86 platforms have support for both _CPC and _PSS object. So kernel config can have both ACPI_CPU_FREQ_PSS and ACPI_CPPC_LIB. So remove restriction for ACPI_CPPC_LIB to build only when ACPI_CPU_FREQ_PSS is not defined. Also for legacy systems with only _PSS, we shouldn't bail out if acpi_cppc_processor_probe() fails, if ACPI_CPU_FREQ_PSS is also defined. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 445ce28..c6bb6aa 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -227,7 +227,6 @@ config ACPI_MCFG config ACPI_CPPC_LIB bool depends on ACPI_PROCESSOR - depends on !ACPI_CPU_FREQ_PSS select MAILBOX select PCC help diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0553aee..f5c92be 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -245,7 +245,7 @@ static int __acpi_processor_start(struct acpi_device *device) return 0; result = acpi_cppc_processor_probe(pr); - if (result) + if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS)) return -ENODEV; if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) -- cgit v0.10.2 From 5448f14698b916e5b04112b104433b90247e01c7 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 1 Sep 2016 13:37:09 -0700 Subject: ACPI / CPPC: Don't return on CPPC probe failure It is still possible to continue even CPPC data is invalid or missing. Suggested-by: Alexey Klimov Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index f5c92be..8f8552a 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -246,7 +246,7 @@ static int __acpi_processor_start(struct acpi_device *device) result = acpi_cppc_processor_probe(pr); if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS)) - return -ENODEV; + dev_warn(&device->dev, "CPPC data invalid or not present\n"); if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) acpi_processor_power_init(pr); -- cgit v0.10.2 From a6cbcdd5ab5f242d49f511127f0a601b71be2cc4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 1 Sep 2016 13:37:10 -0700 Subject: ACPI / CPPC: Add support for functional fixed hardware address The CPPC registers can also be accessed via functional fixed hardware addresse(FFH) in X86. Add support by modifying cpc_read and cpc_write to be able to read/write MSRs on x86 platform on per cpu basis. Also with this change, acpi_cppc_processor_probe doesn't bail out if address space id is not equal to PCC or memory address space and FFH is supported on the system. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index 3242e59..26b78d8 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_APEI) += apei.o +obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_msr.o ifneq ($(CONFIG_ACPI_PROCESSOR),) obj-y += cstate.o diff --git a/arch/x86/kernel/acpi/cppc_msr.c b/arch/x86/kernel/acpi/cppc_msr.c new file mode 100644 index 0000000..6fb478b --- /dev/null +++ b/arch/x86/kernel/acpi/cppc_msr.c @@ -0,0 +1,58 @@ +/* + * cppc_msr.c: MSR Interface for CPPC + * Copyright (c) 2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +#include +#include + +/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */ + +bool cpc_ffh_supported(void) +{ + return true; +} + +int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val) +{ + int err; + + err = rdmsrl_safe_on_cpu(cpunum, reg->address, val); + if (!err) { + u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, + reg->bit_offset); + + *val &= mask; + *val >>= reg->bit_offset; + } + return err; +} + +int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val) +{ + u64 rd_val; + int err; + + err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val); + if (!err) { + u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, + reg->bit_offset); + + val <<= reg->bit_offset; + val &= mask; + rd_val &= ~mask; + rd_val |= val; + err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val); + } + return err; +} diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index ed58883..715fe80 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -579,6 +579,19 @@ static int register_pcc_channel(int pcc_subspace_idx) return 0; } +/** + * cpc_ffh_supported() - check if FFH reading supported + * + * Check if the architecture has support for functional fixed hardware + * read/write capability. + * + * Return: true for supported, false for not supported + */ +bool __weak cpc_ffh_supported(void) +{ + return false; +} + /* * An example CPC table looks like the following. * @@ -728,9 +741,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr; } } else { - /* Support only PCC and SYS MEM type regs */ - pr_debug("Unsupported register type: %d\n", gas_t->space_id); - goto out_free; + if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) { + /* Support only PCC ,SYS MEM and FFH type regs */ + pr_debug("Unsupported register type: %d\n", gas_t->space_id); + goto out_free; + } } cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; @@ -819,13 +834,43 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr) } EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); +/** + * cpc_read_ffh() - Read FFH register + * @cpunum: cpu number to read + * @reg: cppc register information + * @val: place holder for return value + * + * Read bit_width bits from a specified address and bit_offset + * + * Return: 0 for success and error code + */ +int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val) +{ + return -ENOTSUPP; +} + +/** + * cpc_write_ffh() - Write FFH register + * @cpunum: cpu number to write + * @reg: cppc register information + * @val: value to write + * + * Write value of bit_width bits to a specified address and bit_offset + * + * Return: 0 for success and error code + */ +int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val) +{ + return -ENOTSUPP; +} + /* * Since cpc_read and cpc_write are called while holding pcc_lock, it should be * as fast as possible. We have already mapped the PCC subspace during init, so * we can directly write to it. */ -static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) +static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val) { int ret_val = 0; void __iomem *vaddr = 0; @@ -841,6 +886,8 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) vaddr = GET_PCC_VADDR(reg->address); else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) vaddr = reg_res->sys_mem_vaddr; + else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) + return cpc_read_ffh(cpu, reg, val); else return acpi_os_read_memory((acpi_physical_address)reg->address, val, reg->bit_width); @@ -867,7 +914,7 @@ static int cpc_read(struct cpc_register_resource *reg_res, u64 *val) return ret_val; } -static int cpc_write(struct cpc_register_resource *reg_res, u64 val) +static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) { int ret_val = 0; void __iomem *vaddr = 0; @@ -877,6 +924,8 @@ static int cpc_write(struct cpc_register_resource *reg_res, u64 val) vaddr = GET_PCC_VADDR(reg->address); else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) vaddr = reg_res->sys_mem_vaddr; + else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) + return cpc_write_ffh(cpu, reg, val); else return acpi_os_write_memory((acpi_physical_address)reg->address, val, reg->bit_width); @@ -941,13 +990,13 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) } } - cpc_read(highest_reg, &high); + cpc_read(cpunum, highest_reg, &high); perf_caps->highest_perf = high; - cpc_read(lowest_reg, &low); + cpc_read(cpunum, lowest_reg, &low); perf_caps->lowest_perf = low; - cpc_read(nom_perf, &nom); + cpc_read(cpunum, nom_perf, &nom); perf_caps->nominal_perf = nom; if (!high || !low || !nom) @@ -1004,9 +1053,9 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) } } - cpc_read(delivered_reg, &delivered); - cpc_read(reference_reg, &reference); - cpc_read(ref_perf_reg, &ref_perf); + cpc_read(cpunum, delivered_reg, &delivered); + cpc_read(cpunum, reference_reg, &reference); + cpc_read(cpunum, ref_perf_reg, &ref_perf); /* * Per spec, if ctr_wrap_time optional register is unsupported, then the @@ -1015,7 +1064,7 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) */ ctr_wrap_time = (u64)(~((u64)0)); if (CPC_SUPPORTED(ctr_wrap_reg)) - cpc_read(ctr_wrap_reg, &ctr_wrap_time); + cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time); if (!delivered || !reference || !ref_perf) { ret = -EFAULT; @@ -1082,7 +1131,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) * Skip writing MIN/MAX until Linux knows how to come up with * useful values. */ - cpc_write(desired_reg, perf_ctrls->desired_perf); + cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); if (CPC_IN_PCC(desired_reg)) up_read(&pcc_data.pcc_lock); /* END Phase-I */ -- cgit v0.10.2 From 41dd64038970139c562d07ee7ff4e41245611b4a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 1 Sep 2016 13:37:11 -0700 Subject: ACPI / CPPC: Add prefix cppc to cpudata structure name Since struct cpudata is defined in a header file, add prefix cppc_ to make it not a generic name. Otherwise it causes compile issue in locally define structure with the same name. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 715fe80..3d1ae6d 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -409,13 +409,13 @@ end: * * Return: 0 for success or negative value for err. */ -int acpi_get_psd_map(struct cpudata **all_cpu_data) +int acpi_get_psd_map(struct cppc_cpudata **all_cpu_data) { int count_target; int retval = 0; unsigned int i, j; cpumask_var_t covered_cpus; - struct cpudata *pr, *match_pr; + struct cppc_cpudata *pr, *match_pr; struct acpi_psd_package *pdomain; struct acpi_psd_package *match_pdomain; struct cpc_desc *cpc_ptr, *match_cpc_ptr; diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index e6a3359..6588ec5 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -30,13 +30,13 @@ * performance capabilities, desired performance level * requested etc. */ -static struct cpudata **all_cpu_data; +static struct cppc_cpudata **all_cpu_data; static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - struct cpudata *cpu; + struct cppc_cpudata *cpu; struct cpufreq_freqs freqs; int ret = 0; @@ -66,7 +66,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy) static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) { int cpu_num = policy->cpu; - struct cpudata *cpu = all_cpu_data[cpu_num]; + struct cppc_cpudata *cpu = all_cpu_data[cpu_num]; int ret; cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; @@ -79,7 +79,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) { - struct cpudata *cpu; + struct cppc_cpudata *cpu; unsigned int cpu_num = policy->cpu; int ret = 0; @@ -135,7 +135,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = { static int __init cppc_cpufreq_init(void) { int i, ret = 0; - struct cpudata *cpu; + struct cppc_cpudata *cpu; if (acpi_disabled) return -ENODEV; @@ -145,7 +145,7 @@ static int __init cppc_cpufreq_init(void) return -ENOMEM; for_each_possible_cpu(i) { - all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); + all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL); if (!all_cpu_data[i]) goto out; @@ -176,7 +176,7 @@ out: static void __exit cppc_cpufreq_exit(void) { - struct cpudata *cpu; + struct cppc_cpudata *cpu; int i; cpufreq_unregister_driver(&cppc_cpufreq_driver); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 0e83cc3..427a7c3 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -119,7 +119,7 @@ struct cppc_perf_fb_ctrs { }; /* Per CPU container for runtime CPPC management. */ -struct cpudata { +struct cppc_cpudata { int cpu; struct cppc_perf_caps perf_caps; struct cppc_perf_ctrls perf_ctrls; @@ -132,7 +132,7 @@ struct cpudata { extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs); extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); -extern int acpi_get_psd_map(struct cpudata **); +extern int acpi_get_psd_map(struct cppc_cpudata **); extern unsigned int cppc_get_transition_latency(int cpu); #endif /* _CPPC_ACPI_H*/ -- cgit v0.10.2 From 72c77b7ea9ce781f4987840984a462e4456ba98e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 16:50:08 +0800 Subject: ACPI / EC: Cleanup first_ec/boot_ec code In order to support full ECDT (driving the ECDT EC after probing the namespace EC), we need to change our EC device alloc/free algorithm, ensure not to free old boot EC before qualifying new boot EC. This patch achieves this by cleaning up first_ec/boot_ec logic: 1. first_ec: used to perform transactions, so it is assigned in new acpi_ec_setup() function. 2. boot_ec: used to track early EC device, so it is assigned in new acpi_config_boot_ec() function which explictly tells the driver to save the EC device as early EC device. Link: https://bugzilla.kernel.org/show_bug.cgi?id=115021 Reported-and-tested-by: Luya Tshimbalanga Tested-by: Jonh Henderson Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 1925589..9eab651 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -184,6 +184,7 @@ static void acpi_ec_event_processor(struct work_struct *work); struct acpi_ec *boot_ec, *first_ec; EXPORT_SYMBOL(first_ec); +static bool boot_ec_is_ecdt = false; static struct workqueue_struct *ec_query_wq; static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ @@ -1304,7 +1305,16 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, static acpi_status ec_parse_io_ports(struct acpi_resource *resource, void *context); -static struct acpi_ec *make_acpi_ec(void) +static void acpi_ec_free(struct acpi_ec *ec) +{ + if (first_ec == ec) + first_ec = NULL; + if (boot_ec == ec) + boot_ec = NULL; + kfree(ec); +} + +static struct acpi_ec *acpi_ec_alloc(void) { struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); @@ -1365,6 +1375,11 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return AE_CTRL_TERMINATE; } +/* + * Note: This function returns an error code only when the address space + * handler is not installed, which means "not able to handle + * transactions". + */ static int ec_install_handlers(struct acpi_ec *ec) { acpi_status status; @@ -1440,21 +1455,49 @@ static void ec_remove_handlers(struct acpi_ec *ec) } } -static struct acpi_ec *acpi_ec_alloc(void) +static int acpi_ec_setup(struct acpi_ec *ec) { - struct acpi_ec *ec; + int ret; - /* Check for boot EC */ - if (boot_ec) { - ec = boot_ec; - boot_ec = NULL; - ec_remove_handlers(ec); - if (first_ec == ec) - first_ec = NULL; - } else { - ec = make_acpi_ec(); + ret = ec_install_handlers(ec); + if (ret) + return ret; + + /* First EC capable of handling transactions */ + if (!first_ec) { + first_ec = ec; + acpi_handle_info(first_ec->handle, "Used as first EC\n"); } - return ec; + + acpi_handle_info(ec->handle, + "GPE=0x%lx, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", + ec->gpe, ec->command_addr, ec->data_addr); + return ret; +} + +static int acpi_config_boot_ec(struct acpi_ec *ec, bool is_ecdt) +{ + int ret; + + if (boot_ec) + ec_remove_handlers(boot_ec); + + /* Unset old boot EC */ + if (boot_ec != ec) + acpi_ec_free(boot_ec); + + ret = acpi_ec_setup(ec); + if (ret) + return ret; + + /* Set new boot EC */ + if (!boot_ec) { + boot_ec = ec; + boot_ec_is_ecdt = is_ecdt; + acpi_handle_info(boot_ec->handle, "Used as boot %s EC\n", + is_ecdt ? "ECDT" : "DSDT"); + } + return ret; } static int acpi_ec_add(struct acpi_device *device) @@ -1470,7 +1513,7 @@ static int acpi_ec_add(struct acpi_device *device) return -ENOMEM; if (ec_parse_device(device->handle, 0, ec, NULL) != AE_CTRL_TERMINATE) { - kfree(ec); + acpi_ec_free(ec); return -EINVAL; } @@ -1478,8 +1521,6 @@ static int acpi_ec_add(struct acpi_device *device) acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, acpi_ec_register_query_methods, NULL, ec, NULL); - if (!first_ec) - first_ec = ec; device->driver_data = ec; ret = !!request_region(ec->data_addr, 1, "EC data"); @@ -1487,10 +1528,7 @@ static int acpi_ec_add(struct acpi_device *device) ret = !!request_region(ec->command_addr, 1, "EC cmd"); WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr); - pr_info("GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", - ec->gpe, ec->command_addr, ec->data_addr); - - ret = ec_install_handlers(ec); + ret = acpi_config_boot_ec(ec, false); /* Reprobe devices depending on the EC */ acpi_walk_dep_device_list(ec->handle); @@ -1513,9 +1551,7 @@ static int acpi_ec_remove(struct acpi_device *device) release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; - if (ec == first_ec) - first_ec = NULL; - kfree(ec); + acpi_ec_free(ec); return 0; } @@ -1567,13 +1603,10 @@ int __init acpi_ec_dsdt_probe(void) ret = -ENODEV; goto error; } - ret = ec_install_handlers(ec); - + ret = acpi_config_boot_ec(ec, false); error: if (ret) - kfree(ec); - else - first_ec = boot_ec = ec; + acpi_ec_free(ec); return ret; } @@ -1671,7 +1704,6 @@ int __init acpi_ec_ecdt_probe(void) goto error; } - pr_info("EC description table is found, configuring boot EC\n"); if (EC_FLAGS_CORRECT_ECDT) { ec->command_addr = ecdt_ptr->data.address; ec->data_addr = ecdt_ptr->control.address; @@ -1681,12 +1713,10 @@ int __init acpi_ec_ecdt_probe(void) } ec->gpe = ecdt_ptr->gpe; ec->handle = ACPI_ROOT_OBJECT; - ret = ec_install_handlers(ec); + ret = acpi_config_boot_ec(ec, true); error: if (ret) - kfree(ec); - else - first_ec = boot_ec = ec; + acpi_ec_free(ec); return ret; } -- cgit v0.10.2 From 46922d2a3aff5122253d97e64500801c08f4f2c0 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 16:50:14 +0800 Subject: ACPI / EC: Fix a memory leakage issue in acpi_ec_add() When the handler installation failed, there was no code to free the allocated EC device. This patch fixes this memory leakage issue. Link: https://bugzilla.kernel.org/show_bug.cgi?id=115021 Reported-and-tested-by: Luya Tshimbalanga Tested-by: Jonh Henderson Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 9eab651..50895ff 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1513,14 +1513,18 @@ static int acpi_ec_add(struct acpi_device *device) return -ENOMEM; if (ec_parse_device(device->handle, 0, ec, NULL) != AE_CTRL_TERMINATE) { - acpi_ec_free(ec); - return -EINVAL; + ret = -EINVAL; + goto err_alloc; } /* Find and register all query methods */ acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, acpi_ec_register_query_methods, NULL, ec, NULL); + ret = acpi_config_boot_ec(ec, false); + if (ret) + goto err_query; + device->driver_data = ec; ret = !!request_region(ec->data_addr, 1, "EC data"); @@ -1528,13 +1532,17 @@ static int acpi_ec_add(struct acpi_device *device) ret = !!request_region(ec->command_addr, 1, "EC cmd"); WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr); - ret = acpi_config_boot_ec(ec, false); - /* Reprobe devices depending on the EC */ acpi_walk_dep_device_list(ec->handle); /* EC is fully operational, allow queries */ acpi_ec_enable_event(ec); + return 0; + +err_query: + acpi_ec_remove_query_handlers(ec, true, 0); +err_alloc: + acpi_ec_free(ec); return ret; } -- cgit v0.10.2 From 2a5708409e4e05446eb1a89ecb48641d6fd5d5a9 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 16:50:21 +0800 Subject: ACPI / EC: Fix a gap that ECDT EC cannot handle EC events It is possible to register _Qxx from namespace and use the ECDT EC to perform event handling. The reported bug reveals that Windows is using ECDT in this way in case the namespace EC is not present. This patch facilitates Linux to support ECDT in this way. Link: https://bugzilla.kernel.org/show_bug.cgi?id=115021 Reported-and-tested-by: Luya Tshimbalanga Tested-by: Jonh Henderson Reviewed-by: Peter Wu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 50895ff..2ae9194 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -109,6 +109,7 @@ enum { EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */ EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */ + EC_FLAGS_EVT_HANDLER_INSTALLED, /* _Qxx handlers installed */ EC_FLAGS_STARTED, /* Driver is started */ EC_FLAGS_STOPPED, /* Driver is stopped */ EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the @@ -1380,7 +1381,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) * handler is not installed, which means "not able to handle * transactions". */ -static int ec_install_handlers(struct acpi_ec *ec) +static int ec_install_handlers(struct acpi_ec *ec, bool handle_events) { acpi_status status; @@ -1409,6 +1410,16 @@ static int ec_install_handlers(struct acpi_ec *ec) set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); } + if (!handle_events) + return 0; + + if (!test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) { + /* Find and register all query methods */ + acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, + acpi_ec_register_query_methods, + NULL, ec, NULL); + set_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags); + } if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) { status = acpi_install_gpe_raw_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, @@ -1419,6 +1430,9 @@ static int ec_install_handlers(struct acpi_ec *ec) if (test_bit(EC_FLAGS_STARTED, &ec->flags) && ec->reference_count >= 1) acpi_ec_enable_gpe(ec, true); + + /* EC is fully operational, allow queries */ + acpi_ec_enable_event(ec); } } @@ -1453,13 +1467,17 @@ static void ec_remove_handlers(struct acpi_ec *ec) pr_err("failed to remove gpe handler\n"); clear_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags); } + if (test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) { + acpi_ec_remove_query_handlers(ec, true, 0); + clear_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags); + } } -static int acpi_ec_setup(struct acpi_ec *ec) +static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events) { int ret; - ret = ec_install_handlers(ec); + ret = ec_install_handlers(ec, handle_events); if (ret) return ret; @@ -1475,18 +1493,33 @@ static int acpi_ec_setup(struct acpi_ec *ec) return ret; } -static int acpi_config_boot_ec(struct acpi_ec *ec, bool is_ecdt) +static int acpi_config_boot_ec(struct acpi_ec *ec, acpi_handle handle, + bool handle_events, bool is_ecdt) { int ret; - if (boot_ec) + /* + * Changing the ACPI handle results in a re-configuration of the + * boot EC. And if it happens after the namespace initialization, + * it causes _REG evaluations. + */ + if (boot_ec && boot_ec->handle != handle) ec_remove_handlers(boot_ec); /* Unset old boot EC */ if (boot_ec != ec) acpi_ec_free(boot_ec); - ret = acpi_ec_setup(ec); + /* + * ECDT device creation is split into acpi_ec_ecdt_probe() and + * acpi_ec_ecdt_start(). This function takes care of completing the + * ECDT parsing logic as the handle update should be performed + * between the installation/uninstallation of the handlers. + */ + if (ec->handle != handle) + ec->handle = handle; + + ret = acpi_ec_setup(ec, handle_events); if (ret) return ret; @@ -1494,9 +1527,12 @@ static int acpi_config_boot_ec(struct acpi_ec *ec, bool is_ecdt) if (!boot_ec) { boot_ec = ec; boot_ec_is_ecdt = is_ecdt; - acpi_handle_info(boot_ec->handle, "Used as boot %s EC\n", - is_ecdt ? "ECDT" : "DSDT"); } + + acpi_handle_info(boot_ec->handle, + "Used as boot %s EC to handle transactions%s\n", + is_ecdt ? "ECDT" : "DSDT", + handle_events ? " and events" : ""); return ret; } @@ -1517,11 +1553,7 @@ static int acpi_ec_add(struct acpi_device *device) goto err_alloc; } - /* Find and register all query methods */ - acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, - acpi_ec_register_query_methods, NULL, ec, NULL); - - ret = acpi_config_boot_ec(ec, false); + ret = acpi_config_boot_ec(ec, device->handle, true, false); if (ret) goto err_query; @@ -1534,9 +1566,6 @@ static int acpi_ec_add(struct acpi_device *device) /* Reprobe devices depending on the EC */ acpi_walk_dep_device_list(ec->handle); - - /* EC is fully operational, allow queries */ - acpi_ec_enable_event(ec); return 0; err_query: @@ -1555,7 +1584,6 @@ static int acpi_ec_remove(struct acpi_device *device) ec = acpi_driver_data(device); ec_remove_handlers(ec); - acpi_ec_remove_query_handlers(ec, true, 0); release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; @@ -1601,9 +1629,8 @@ int __init acpi_ec_dsdt_probe(void) if (!ec) return -ENOMEM; /* - * Finding EC from DSDT if there is no ECDT EC available. When this - * function is invoked, ACPI tables have been fully loaded, we can - * walk namespace now. + * At this point, the namespace is initialized, so start to find + * the namespace objects. */ status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, ec, NULL); @@ -1611,13 +1638,55 @@ int __init acpi_ec_dsdt_probe(void) ret = -ENODEV; goto error; } - ret = acpi_config_boot_ec(ec, false); + /* + * When the DSDT EC is available, always re-configure boot EC to + * have _REG evaluated. _REG can only be evaluated after the + * namespace initialization. + * At this point, the GPE is not fully initialized, so do not to + * handle the events. + */ + ret = acpi_config_boot_ec(ec, ec->handle, false, false); error: if (ret) acpi_ec_free(ec); return ret; } +/* + * If the DSDT EC is not functioning, we still need to prepare a fully + * functioning ECDT EC first in order to handle the events. + * https://bugzilla.kernel.org/show_bug.cgi?id=115021 + */ +int __init acpi_ec_ecdt_start(void) +{ + struct acpi_table_ecdt *ecdt_ptr; + acpi_status status; + acpi_handle handle; + + if (!boot_ec) + return -ENODEV; + /* + * The DSDT EC should have already been started in + * acpi_ec_add(). + */ + if (!boot_ec_is_ecdt) + return -ENODEV; + + status = acpi_get_table(ACPI_SIG_ECDT, 1, + (struct acpi_table_header **)&ecdt_ptr); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* + * At this point, the namespace and the GPE is initialized, so + * start to find the namespace objects and handle the events. + */ + status = acpi_get_handle(NULL, ecdt_ptr->id, &handle); + if (ACPI_FAILURE(status)) + return -ENODEV; + return acpi_config_boot_ec(boot_ec, handle, true, true); +} + #if 0 /* * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not @@ -1720,8 +1789,12 @@ int __init acpi_ec_ecdt_probe(void) ec->data_addr = ecdt_ptr->data.address; } ec->gpe = ecdt_ptr->gpe; - ec->handle = ACPI_ROOT_OBJECT; - ret = acpi_config_boot_ec(ec, true); + + /* + * At this point, the namespace is not initialized, so do not find + * the namespace objects, or handle the events. + */ + ret = acpi_config_boot_ec(ec, ACPI_ROOT_OBJECT, false, true); error: if (ret) acpi_ec_free(ec); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 29f2063..73bee2c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -187,6 +187,7 @@ typedef int (*acpi_ec_query_func) (void *data); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); int acpi_ec_dsdt_probe(void); +int acpi_ec_ecdt_start(void); void acpi_ec_block_transactions(void); void acpi_ec_unblock_transactions(void); int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ad9fc84..763c0da 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2044,6 +2044,7 @@ int __init acpi_scan_init(void) } acpi_update_all_gpes(); + acpi_ec_ecdt_start(); acpi_scan_initialized = true; -- cgit v0.10.2 From 97cb159fd91d00f8d7d1adeb075503dc0d946bff Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 16:50:27 +0800 Subject: ACPI / EC: Fix issues related to boot_ec There are issues related to the boot_ec: 1. If acpi_ec_remove() is invoked, boot_ec will also be freed, this is not expected as the boot_ec could be enumerated via ECDT. 2. Address space handler installation/unstallation lead to unexpected _REG evaluations. This patch adds acpi_is_boot_ec() check to be used to fix the above issues. However, since acpi_ec_remove() actually won't be invoked, this patch doesn't handle the reference counting of "struct acpi_ec", it only ensures the correctness of the boot_ec destruction during the boot. Link: https://bugzilla.kernel.org/show_bug.cgi?id=153511 Reported-and-tested-by: Jonh Henderson Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 2ae9194..6805310 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1536,6 +1536,37 @@ static int acpi_config_boot_ec(struct acpi_ec *ec, acpi_handle handle, return ret; } +static bool acpi_ec_ecdt_get_handle(acpi_handle *phandle) +{ + struct acpi_table_ecdt *ecdt_ptr; + acpi_status status; + acpi_handle handle; + + status = acpi_get_table(ACPI_SIG_ECDT, 1, + (struct acpi_table_header **)&ecdt_ptr); + if (ACPI_FAILURE(status)) + return false; + + status = acpi_get_handle(NULL, ecdt_ptr->id, &handle); + if (ACPI_FAILURE(status)) + return false; + + *phandle = handle; + return true; +} + +static bool acpi_is_boot_ec(struct acpi_ec *ec) +{ + if (!boot_ec) + return false; + if (ec->handle == boot_ec->handle && + ec->gpe == boot_ec->gpe && + ec->command_addr == boot_ec->command_addr && + ec->data_addr == boot_ec->data_addr) + return true; + return false; +} + static int acpi_ec_add(struct acpi_device *device) { struct acpi_ec *ec = NULL; @@ -1553,7 +1584,14 @@ static int acpi_ec_add(struct acpi_device *device) goto err_alloc; } - ret = acpi_config_boot_ec(ec, device->handle, true, false); + if (acpi_is_boot_ec(ec)) { + boot_ec_is_ecdt = false; + acpi_handle_debug(ec->handle, "duplicated.\n"); + acpi_ec_free(ec); + ec = boot_ec; + ret = acpi_config_boot_ec(ec, ec->handle, true, false); + } else + ret = acpi_ec_setup(ec, true); if (ret) goto err_query; @@ -1566,12 +1604,15 @@ static int acpi_ec_add(struct acpi_device *device) /* Reprobe devices depending on the EC */ acpi_walk_dep_device_list(ec->handle); + acpi_handle_debug(ec->handle, "enumerated.\n"); return 0; err_query: - acpi_ec_remove_query_handlers(ec, true, 0); + if (ec != boot_ec) + acpi_ec_remove_query_handlers(ec, true, 0); err_alloc: - acpi_ec_free(ec); + if (ec != boot_ec) + acpi_ec_free(ec); return ret; } @@ -1583,11 +1624,13 @@ static int acpi_ec_remove(struct acpi_device *device) return -EINVAL; ec = acpi_driver_data(device); - ec_remove_handlers(ec); release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; - acpi_ec_free(ec); + if (ec != boot_ec) { + ec_remove_handlers(ec); + acpi_ec_free(ec); + } return 0; } @@ -1659,8 +1702,6 @@ error: */ int __init acpi_ec_ecdt_start(void) { - struct acpi_table_ecdt *ecdt_ptr; - acpi_status status; acpi_handle handle; if (!boot_ec) @@ -1672,17 +1713,11 @@ int __init acpi_ec_ecdt_start(void) if (!boot_ec_is_ecdt) return -ENODEV; - status = acpi_get_table(ACPI_SIG_ECDT, 1, - (struct acpi_table_header **)&ecdt_ptr); - if (ACPI_FAILURE(status)) - return -ENODEV; - /* * At this point, the namespace and the GPE is initialized, so * start to find the namespace objects and handle the events. */ - status = acpi_get_handle(NULL, ecdt_ptr->id, &handle); - if (ACPI_FAILURE(status)) + if (!acpi_ec_ecdt_get_handle(&handle)) return -ENODEV; return acpi_config_boot_ec(boot_ec, handle, true, true); } -- cgit v0.10.2 From 60361b75848c8614233e3374ef5a0056527f0385 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 7 Sep 2016 14:05:41 +0800 Subject: ACPICA: Debugger: Add subcommand for predefined name execution ACPICA commit be5808f0e642ff9963d86f362521b4af2340e2f5 "Execute Predefined" will execute all predefined (public) names within the namespace. Link: https://github.com/acpica/acpica/commit/be5808f0 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index f6404ea..94737f8 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -155,7 +155,7 @@ acpi_status acpi_db_disassemble_method(char *name); void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op); -void acpi_db_batch_execute(char *count_arg); +void acpi_db_evaluate_predefined_names(void); /* * dbnames - namespace commands diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c index 12df291..fe3da7c 100644 --- a/drivers/acpi/acpica/dbexec.c +++ b/drivers/acpi/acpica/dbexec.c @@ -392,42 +392,48 @@ acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags) acpi_db_execution_walk, NULL, NULL, NULL); return; - } else { - name_string = ACPI_ALLOCATE(strlen(name) + 1); - if (!name_string) { - return; - } + } - memset(&acpi_gbl_db_method_info, 0, - sizeof(struct acpi_db_method_info)); + name_string = ACPI_ALLOCATE(strlen(name) + 1); + if (!name_string) { + return; + } - strcpy(name_string, name); - acpi_ut_strupr(name_string); - acpi_gbl_db_method_info.name = name_string; - acpi_gbl_db_method_info.args = args; - acpi_gbl_db_method_info.types = types; - acpi_gbl_db_method_info.flags = flags; + memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); + strcpy(name_string, name); + acpi_ut_strupr(name_string); - return_obj.pointer = NULL; - return_obj.length = ACPI_ALLOCATE_BUFFER; + /* Subcommand to Execute all predefined names in the namespace */ - status = acpi_db_execute_setup(&acpi_gbl_db_method_info); - if (ACPI_FAILURE(status)) { - ACPI_FREE(name_string); - return; - } + if (!strncmp(name_string, "PREDEF", 6)) { + acpi_db_evaluate_predefined_names(); + ACPI_FREE(name_string); + return; + } - /* Get the NS node, determines existence also */ + acpi_gbl_db_method_info.name = name_string; + acpi_gbl_db_method_info.args = args; + acpi_gbl_db_method_info.types = types; + acpi_gbl_db_method_info.flags = flags; - status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, - &acpi_gbl_db_method_info.method); - if (ACPI_SUCCESS(status)) { - status = - acpi_db_execute_method(&acpi_gbl_db_method_info, - &return_obj); - } + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + status = acpi_db_execute_setup(&acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { ACPI_FREE(name_string); + return; + } + + /* Get the NS node, determines existence also */ + + status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, + &acpi_gbl_db_method_info.method); + if (ACPI_SUCCESS(status)) { + status = acpi_db_execute_method(&acpi_gbl_db_method_info, + &return_obj); } + ACPI_FREE(name_string); /* * Allow any handlers in separate threads to complete. diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c index 7cd5d2e..068214f 100644 --- a/drivers/acpi/acpica/dbinput.c +++ b/drivers/acpi/acpica/dbinput.c @@ -286,6 +286,8 @@ static const struct acpi_db_command_help acpi_gbl_db_command_help[] = { {1, " \"Ascii String\"", "String method argument\n"}, {1, " (Hex Byte List)", "Buffer method argument\n"}, {1, " [Package Element List]", "Package method argument\n"}, + {5, " Execute predefined", + "Execute all predefined (public) methods\n"}, {1, " Go", "Allow method to run to completion\n"}, {1, " Information", "Display info about the current method\n"}, {1, " Into", "Step into (not over) a method call\n"}, diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c index f17a86f..314b94c 100644 --- a/drivers/acpi/acpica/dbmethod.c +++ b/drivers/acpi/acpica/dbmethod.c @@ -52,6 +52,11 @@ #define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME("dbmethod") +/* Local prototypes */ +static acpi_status +acpi_db_walk_for_execute(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + /******************************************************************************* * * FUNCTION: acpi_db_set_method_breakpoint @@ -66,6 +71,7 @@ ACPI_MODULE_NAME("dbmethod") * AML offset * ******************************************************************************/ + void acpi_db_set_method_breakpoint(char *location, struct acpi_walk_state *walk_state, @@ -367,3 +373,129 @@ acpi_status acpi_db_disassemble_method(char *name) acpi_ut_release_owner_id(&obj_desc->method.owner_id); return (AE_OK); } + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_execute + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Batch execution module. Currently only executes predefined + * ACPI names. + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_execute(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + struct acpi_db_execute_walk *info = + (struct acpi_db_execute_walk *)context; + struct acpi_buffer return_obj; + acpi_status status; + char *pathname; + u32 i; + struct acpi_device_info *obj_info; + struct acpi_object_list param_objects; + union acpi_object params[ACPI_METHOD_NUM_ARGS]; + const union acpi_predefined_info *predefined; + + predefined = acpi_ut_match_predefined_method(node->name.ascii); + if (!predefined) { + return (AE_OK); + } + + if (node->type == ACPI_TYPE_LOCAL_SCOPE) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* Get the object info for number of method parameters */ + + status = acpi_get_object_info(obj_handle, &obj_info); + if (ACPI_FAILURE(status)) { + return (status); + } + + param_objects.pointer = NULL; + param_objects.count = 0; + + if (obj_info->type == ACPI_TYPE_METHOD) { + + /* Setup default parameters */ + + for (i = 0; i < obj_info->param_count; i++) { + params[i].type = ACPI_TYPE_INTEGER; + params[i].integer.value = 1; + } + + param_objects.pointer = params; + param_objects.count = obj_info->param_count; + } + + ACPI_FREE(obj_info); + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + /* Do the actual method execution */ + + acpi_gbl_method_executing = TRUE; + + status = acpi_evaluate_object(node, NULL, ¶m_objects, &return_obj); + + acpi_os_printf("%-32s returned %s\n", pathname, + acpi_format_exception(status)); + acpi_gbl_method_executing = FALSE; + ACPI_FREE(pathname); + + /* Ignore status from method execution */ + + status = AE_OK; + + /* Update count, check if we have executed enough methods */ + + info->count++; + if (info->count >= info->max_count) { + status = AE_CTRL_TERMINATE; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_evaluate_predefined_names + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Namespace batch execution. Execute predefined names in the + * namespace, up to the max count, if specified. + * + ******************************************************************************/ + +void acpi_db_evaluate_predefined_names(void) +{ + struct acpi_db_execute_walk info; + + info.count = 0; + info.max_count = ACPI_UINT32_MAX; + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_walk_for_execute, + NULL, (void *)&info, NULL); + + acpi_os_printf("Evaluated %u predefined names in the namespace\n", + info.count); +} -- cgit v0.10.2 From 5ebd2eaaefc0d4fe37ab72e716e1b8065ed4206c Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 7 Sep 2016 14:14:30 +0800 Subject: ACPICA: Cleanup for all string-to-integer conversions ACPICA commit e2e72a351201fd58e4694418859ae2c247dafca0 Consolidate multiple versions of strtoul64 to one common version. limit possible bases to either 10 or 16. Handles both implicit and explicit conversions. Added a 2-character ascii-to-hex function for GPEs and buffers. Adds a new file, utstrtoul64.c Link: https://github.com/acpica/acpica/commit/e2e72a35 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 227bb7b..32d93ed 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -175,6 +175,7 @@ acpi-y += \ utresrc.o \ utstate.o \ utstring.o \ + utstrtoul64.o \ utxface.o \ utxfinit.o \ utxferror.o \ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index d899296..0a1b53c 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -196,14 +196,15 @@ void acpi_ut_strlwr(char *src_string); int acpi_ut_stricmp(char *string1, char *string2); -acpi_status -acpi_ut_strtoul64(char *string, - u32 base, u32 max_integer_byte_width, u64 *ret_integer); - -/* Values for max_integer_byte_width above */ +acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *ret_integer); -#define ACPI_MAX32_BYTE_WIDTH 4 -#define ACPI_MAX64_BYTE_WIDTH 8 +/* + * Values for Flags above + * Note: LIMIT values correspond to acpi_gbl_integer_byte_width values (4/8) + */ +#define ACPI_STRTOUL_32BIT 0x04 /* 4 bytes */ +#define ACPI_STRTOUL_64BIT 0x08 /* 8 bytes */ +#define ACPI_STRTOUL_BASE16 0x10 /* Default: Base10/16 */ /* * utglobal - Global data structures and procedures @@ -233,6 +234,8 @@ const char *acpi_ut_get_event_name(u32 event_id); char acpi_ut_hex_to_ascii_char(u64 integer, u32 position); +acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte); + u8 acpi_ut_ascii_char_to_hex(int hex_char); u8 acpi_ut_valid_object_type(acpi_object_type type); diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c index 7cd07b2..147ce88 100644 --- a/drivers/acpi/acpica/dbconvert.c +++ b/drivers/acpi/acpica/dbconvert.c @@ -277,9 +277,10 @@ acpi_db_convert_to_object(acpi_object_type type, default: object->type = ACPI_TYPE_INTEGER; - status = - acpi_ut_strtoul64(string, 16, acpi_gbl_integer_byte_width, - &object->integer.value); + status = acpi_ut_strtoul64(string, + (acpi_gbl_integer_byte_width | + ACPI_STRTOUL_BASE16), + &object->integer.value); break; } diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index 402ecc5..438597c 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -133,7 +133,8 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, * Result of predicate evaluation must be an Integer * object. Implicitly convert the argument if necessary. */ - status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc, 16); + status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc, + ACPI_STRTOUL_BASE16); if (ACPI_FAILURE(status)) { goto cleanup; } diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 7dc7547..16ce483 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -323,7 +323,9 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, struct acpi_gpe_walk_info *walk_info = ACPI_CAST_PTR(struct acpi_gpe_walk_info, context); struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; u32 gpe_number; + u8 temp_gpe_number; char name[ACPI_NAME_SIZE + 1]; u8 type; @@ -377,8 +379,8 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, /* 4) The last two characters of the name are the hex GPE Number */ - gpe_number = strtoul(&name[2], NULL, 16); - if (gpe_number == ACPI_UINT32_MAX) { + status = acpi_ut_ascii_to_hex_byte(&name[2], &temp_gpe_number); + if (ACPI_FAILURE(status)) { /* Conversion failed; invalid method, just ignore it */ @@ -390,6 +392,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, /* Ensure that we have a valid GPE number for this GPE block */ + gpe_number = (u32)temp_gpe_number; gpe_event_info = acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block); if (!gpe_event_info) { diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c index 2423fe0..5429c2a 100644 --- a/drivers/acpi/acpica/exconcat.c +++ b/drivers/acpi/acpica/exconcat.c @@ -156,7 +156,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0, status = acpi_ex_convert_to_integer(local_operand1, &temp_operand1, - 16); + ACPI_STRTOUL_BASE16); break; case ACPI_TYPE_BUFFER: diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index b7e9b3d..588ad14 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -124,9 +124,9 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, * of ACPI 3.0) is that the to_integer() operator allows both decimal * and hexadecimal strings (hex prefixed with "0x"). */ - status = acpi_ut_strtoul64((char *)pointer, flags, - acpi_gbl_integer_byte_width, - &result); + status = acpi_ut_strtoul64(ACPI_CAST_PTR(char, pointer), + (acpi_gbl_integer_byte_width | + flags), &result); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -632,7 +632,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, */ status = acpi_ex_convert_to_integer(source_desc, result_desc, - 16); + ACPI_STRTOUL_BASE16); break; case ACPI_TYPE_STRING: diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 4f7e667..37c88b4 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -327,8 +327,8 @@ acpi_ex_do_logical_op(u16 opcode, switch (operand0->common.type) { case ACPI_TYPE_INTEGER: - status = - acpi_ex_convert_to_integer(operand1, &local_operand1, 16); + status = acpi_ex_convert_to_integer(operand1, &local_operand1, + ACPI_STRTOUL_BASE16); break; case ACPI_TYPE_STRING: diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 4e17506..6ae19cb 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -521,9 +521,10 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */ + /* Perform "explicit" conversion */ + status = - acpi_ex_convert_to_integer(operand[0], &return_desc, - ACPI_ANY_BASE); + acpi_ex_convert_to_integer(operand[0], &return_desc, 0); if (return_desc == operand[0]) { /* No conversion performed, add ref to handle return value */ diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index 27b41fd..f29eba1 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -410,12 +410,13 @@ acpi_ex_resolve_operands(u16 opcode, case ARGI_INTEGER: /* - * Need an operand of type ACPI_TYPE_INTEGER, - * But we can implicitly convert from a STRING or BUFFER - * aka - "Implicit Source Operand Conversion" + * Need an operand of type ACPI_TYPE_INTEGER, but we can + * implicitly convert from a STRING or BUFFER. + * + * Known as "Implicit Source Operand Conversion" */ - status = - acpi_ex_convert_to_integer(obj_desc, stack_ptr, 16); + status = acpi_ex_convert_to_integer(obj_desc, stack_ptr, + ACPI_STRTOUL_BASE16); if (ACPI_FAILURE(status)) { if (status == AE_TYPE) { ACPI_ERROR((AE_INFO, diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c index c803bda..2b85dee 100644 --- a/drivers/acpi/acpica/nsconvert.c +++ b/drivers/acpi/acpica/nsconvert.c @@ -79,7 +79,6 @@ acpi_ns_convert_to_integer(union acpi_operand_object *original_object, /* String-to-Integer conversion */ status = acpi_ut_strtoul64(original_object->string.pointer, - ACPI_ANY_BASE, acpi_gbl_integer_byte_width, &value); if (ACPI_FAILURE(status)) { return (status); diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c index 4354fb8..3a4ca95 100644 --- a/drivers/acpi/acpica/uthex.c +++ b/drivers/acpi/acpica/uthex.c @@ -75,9 +75,40 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position) /******************************************************************************* * + * FUNCTION: acpi_ut_ascii_to_hex_byte + * + * PARAMETERS: two_ascii_chars - Pointer to two ASCII characters + * return_byte - Where converted byte is returned + * + * RETURN: Status and converted hex byte + * + * DESCRIPTION: Perform ascii-to-hex translation, exactly two ASCII characters + * to a single converted byte value. + * + ******************************************************************************/ + +acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte) +{ + + /* Both ASCII characters must be valid hex digits */ + + if (!isxdigit(two_ascii_chars[0]) || !isxdigit(two_ascii_chars[1])) { + return (AE_BAD_HEX_CONSTANT); + } + + *return_byte = + acpi_ut_ascii_char_to_hex(two_ascii_chars[1]) | + (acpi_ut_ascii_char_to_hex(two_ascii_chars[0]) << 4); + + return (AE_OK); +} + +/******************************************************************************* + * * FUNCTION: acpi_ut_ascii_char_to_hex * - * PARAMETERS: hex_char - Hex character in Ascii + * PARAMETERS: hex_char - Hex character in Ascii. Must be: + * 0-9 or A-F or a-f * * RETURN: The binary value of the ascii/hex character * @@ -88,13 +119,19 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position) u8 acpi_ut_ascii_char_to_hex(int hex_char) { - if (hex_char <= 0x39) { - return ((u8)(hex_char - 0x30)); + /* Values 0-9 */ + + if (hex_char <= '9') { + return ((u8)(hex_char - '0')); } - if (hex_char <= 0x46) { + /* Upper case A-F */ + + if (hex_char <= 'F') { return ((u8)(hex_char - 0x37)); } + /* Lower case a-f */ + return ((u8)(hex_char - 0x57)); } diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c index 3465fe2..2514239 100644 --- a/drivers/acpi/acpica/utnonansi.c +++ b/drivers/acpi/acpica/utnonansi.c @@ -48,8 +48,8 @@ ACPI_MODULE_NAME("utnonansi") /* - * Non-ANSI C library functions - strlwr, strupr, stricmp, and a 64-bit - * version of strtoul. + * Non-ANSI C library functions - strlwr, strupr, stricmp, and "safe" + * string functions. */ /******************************************************************************* * @@ -200,356 +200,3 @@ acpi_ut_safe_strncat(char *dest, return (FALSE); } #endif - -/******************************************************************************* - * - * FUNCTION: acpi_ut_strtoul64 - * - * PARAMETERS: string - Null terminated string - * base - Radix of the string: 16 or 10 or - * ACPI_ANY_BASE - * max_integer_byte_width - Maximum allowable integer,in bytes: - * 4 or 8 (32 or 64 bits) - * ret_integer - Where the converted integer is - * returned - * - * RETURN: Status and Converted value - * - * DESCRIPTION: Convert a string into an unsigned value. Performs either a - * 32-bit or 64-bit conversion, depending on the input integer - * size (often the current mode of the interpreter). - * - * NOTES: Negative numbers are not supported, as they are not supported - * by ACPI. - * - * acpi_gbl_integer_byte_width should be set to the proper width. - * For the core ACPICA code, this width depends on the DSDT - * version. For iASL, the default byte width is always 8 for the - * parser, but error checking is performed later to flag cases - * where a 64-bit constant is defined in a 32-bit DSDT/SSDT. - * - * Does not support Octal strings, not needed at this time. - * - ******************************************************************************/ - -acpi_status -acpi_ut_strtoul64(char *string, - u32 base, u32 max_integer_byte_width, u64 *ret_integer) -{ - u32 this_digit = 0; - u64 return_value = 0; - u64 quotient; - u64 dividend; - u8 valid_digits = 0; - u8 sign_of0x = 0; - u8 term = 0; - - ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string); - - switch (base) { - case ACPI_ANY_BASE: - case 10: - case 16: - - break; - - default: - - /* Invalid Base */ - - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - - if (!string) { - goto error_exit; - } - - /* Skip over any white space in the buffer */ - - while ((*string) && (isspace((int)*string) || *string == '\t')) { - string++; - } - - if (base == ACPI_ANY_BASE) { - /* - * Base equal to ACPI_ANY_BASE means 'Either decimal or hex'. - * We need to determine if it is decimal or hexadecimal. - */ - if ((*string == '0') && (tolower((int)*(string + 1)) == 'x')) { - sign_of0x = 1; - base = 16; - - /* Skip over the leading '0x' */ - string += 2; - } else { - base = 10; - } - } - - /* Any string left? Check that '0x' is not followed by white space. */ - - if (!(*string) || isspace((int)*string) || *string == '\t') { - if (base == ACPI_ANY_BASE) { - goto error_exit; - } else { - goto all_done; - } - } - - /* - * Perform a 32-bit or 64-bit conversion, depending upon the input - * byte width - */ - dividend = (max_integer_byte_width <= ACPI_MAX32_BYTE_WIDTH) ? - ACPI_UINT32_MAX : ACPI_UINT64_MAX; - - /* Main loop: convert the string to a 32- or 64-bit integer */ - - while (*string) { - if (isdigit((int)*string)) { - - /* Convert ASCII 0-9 to Decimal value */ - - this_digit = ((u8)*string) - '0'; - } else if (base == 10) { - - /* Digit is out of range; possible in to_integer case only */ - - term = 1; - } else { - this_digit = (u8)toupper((int)*string); - if (isxdigit((int)this_digit)) { - - /* Convert ASCII Hex char to value */ - - this_digit = this_digit - 'A' + 10; - } else { - term = 1; - } - } - - if (term) { - if (base == ACPI_ANY_BASE) { - goto error_exit; - } else { - break; - } - } else if ((valid_digits == 0) && (this_digit == 0) - && !sign_of0x) { - - /* Skip zeros */ - string++; - continue; - } - - valid_digits++; - - if (sign_of0x && ((valid_digits > 16) || - ((valid_digits > 8) - && (max_integer_byte_width <= - ACPI_MAX32_BYTE_WIDTH)))) { - /* - * This is to_integer operation case. - * No restrictions for string-to-integer conversion, - * see ACPI spec. - */ - goto error_exit; - } - - /* Divide the digit into the correct position */ - - (void)acpi_ut_short_divide((dividend - (u64)this_digit), base, - "ient, NULL); - - if (return_value > quotient) { - if (base == ACPI_ANY_BASE) { - goto error_exit; - } else { - break; - } - } - - return_value *= base; - return_value += this_digit; - string++; - } - - /* All done, normal exit */ - -all_done: - - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n", - ACPI_FORMAT_UINT64(return_value))); - - *ret_integer = return_value; - return_ACPI_STATUS(AE_OK); - -error_exit: - - /* Base was set/validated above (10 or 16) */ - - if (base == 10) { - return_ACPI_STATUS(AE_BAD_DECIMAL_CONSTANT); - } else { - return_ACPI_STATUS(AE_BAD_HEX_CONSTANT); - } -} - -#ifdef _OBSOLETE_FUNCTIONS -/* Removed: 01/2016 */ - -/******************************************************************************* - * - * FUNCTION: strtoul64 - * - * PARAMETERS: string - Null terminated string - * terminater - Where a pointer to the terminating byte - * is returned - * base - Radix of the string - * - * RETURN: Converted value - * - * DESCRIPTION: Convert a string into an unsigned value. - * - ******************************************************************************/ - -acpi_status strtoul64(char *string, u32 base, u64 *ret_integer) -{ - u32 index; - u32 sign; - u64 return_value = 0; - acpi_status status = AE_OK; - - *ret_integer = 0; - - switch (base) { - case 0: - case 8: - case 10: - case 16: - - break; - - default: - /* - * The specified Base parameter is not in the domain of - * this function: - */ - return (AE_BAD_PARAMETER); - } - - /* Skip over any white space in the buffer: */ - - while (isspace((int)*string) || *string == '\t') { - ++string; - } - - /* - * The buffer may contain an optional plus or minus sign. - * If it does, then skip over it but remember what is was: - */ - if (*string == '-') { - sign = ACPI_SIGN_NEGATIVE; - ++string; - } else if (*string == '+') { - ++string; - sign = ACPI_SIGN_POSITIVE; - } else { - sign = ACPI_SIGN_POSITIVE; - } - - /* - * If the input parameter Base is zero, then we need to - * determine if it is octal, decimal, or hexadecimal: - */ - if (base == 0) { - if (*string == '0') { - if (tolower((int)*(++string)) == 'x') { - base = 16; - ++string; - } else { - base = 8; - } - } else { - base = 10; - } - } - - /* - * For octal and hexadecimal bases, skip over the leading - * 0 or 0x, if they are present. - */ - if (base == 8 && *string == '0') { - string++; - } - - if (base == 16 && *string == '0' && tolower((int)*(++string)) == 'x') { - string++; - } - - /* Main loop: convert the string to an unsigned long */ - - while (*string) { - if (isdigit((int)*string)) { - index = ((u8)*string) - '0'; - } else { - index = (u8)toupper((int)*string); - if (isupper((int)index)) { - index = index - 'A' + 10; - } else { - goto error_exit; - } - } - - if (index >= base) { - goto error_exit; - } - - /* Check to see if value is out of range: */ - - if (return_value > ((ACPI_UINT64_MAX - (u64)index) / (u64)base)) { - goto error_exit; - } else { - return_value *= base; - return_value += index; - } - - ++string; - } - - /* If a minus sign was present, then "the conversion is negated": */ - - if (sign == ACPI_SIGN_NEGATIVE) { - return_value = (ACPI_UINT32_MAX - return_value) + 1; - } - - *ret_integer = return_value; - return (status); - -error_exit: - switch (base) { - case 8: - - status = AE_BAD_OCTAL_CONSTANT; - break; - - case 10: - - status = AE_BAD_DECIMAL_CONSTANT; - break; - - case 16: - - status = AE_BAD_HEX_CONSTANT; - break; - - default: - - /* Base validated above */ - - break; - } - - return (status); -} -#endif diff --git a/drivers/acpi/acpica/utstrtoul64.c b/drivers/acpi/acpica/utstrtoul64.c new file mode 100644 index 0000000..b4f341c --- /dev/null +++ b/drivers/acpi/acpica/utstrtoul64.c @@ -0,0 +1,348 @@ +/******************************************************************************* + * + * Module Name: utstrtoul64 - string to 64-bit integer support + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2016, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +/******************************************************************************* + * + * The functions in this module satisfy the need for 64-bit string-to-integer + * conversions on both 32-bit and 64-bit platforms. + * + ******************************************************************************/ + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utstrtoul64") + +/* Local prototypes */ +static u64 acpi_ut_strtoul_base10(char *string, u32 flags); + +static u64 acpi_ut_strtoul_base16(char *string, u32 flags); + +/******************************************************************************* + * + * String conversion rules as written in the ACPI specification. The error + * conditions and behavior are different depending on the type of conversion. + * + * + * Implicit data type conversion: string-to-integer + * -------------------------------------------------- + * + * Base is always 16. This is the ACPI_STRTOUL_BASE16 case. + * + * Example: + * Add ("BA98", Arg0, Local0) + * + * The integer is initialized to the value zero. + * The ASCII string is interpreted as a hexadecimal constant. + * + * 1) A "0x" prefix is not allowed. However, ACPICA allows this for + * compatibility with previous ACPICA. (NO ERROR) + * + * 2) Terminates when the size of an integer is reached (32 or 64 bits). + * (NO ERROR) + * + * 3) The first non-hex character terminates the conversion without error. + * (NO ERROR) + * + * 4) Conversion of a null (zero-length) string to an integer is not + * allowed. However, ACPICA allows this for compatibility with previous + * ACPICA. This conversion returns the value 0. (NO ERROR) + * + * + * Explicit data type conversion: to_integer() with string operand + * --------------------------------------------------------------- + * + * Base is either 10 (default) or 16 (with 0x prefix) + * + * Examples: + * to_integer ("1000") + * to_integer ("0xABCD") + * + * 1) Can be (must be) either a decimal or hexadecimal numeric string. + * A hex value must be prefixed by "0x" or it is interpreted as a decimal. + * + * 2) The value must not exceed the maximum of an integer value. ACPI spec + * states the behavior is "unpredictable", so ACPICA matches the behavior + * of the implicit conversion case.(NO ERROR) + * + * 3) Behavior on the first non-hex character is not specified by the ACPI + * spec, so ACPICA matches the behavior of the implicit conversion case + * and terminates. (NO ERROR) + * + * 4) A null (zero-length) string is illegal. + * However, ACPICA allows this for compatibility with previous ACPICA. + * This conversion returns the value 0. (NO ERROR) + * + ******************************************************************************/ + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul64 + * + * PARAMETERS: string - Null terminated input string + * flags - Conversion info, see below + * return_value - Where the converted integer is + * returned + * + * RETURN: Status and Converted value + * + * DESCRIPTION: Convert a string into an unsigned value. Performs either a + * 32-bit or 64-bit conversion, depending on the input integer + * size in Flags (often the current mode of the interpreter). + * + * Values for Flags: + * ACPI_STRTOUL_32BIT - Max integer value is 32 bits + * ACPI_STRTOUL_64BIT - Max integer value is 64 bits + * ACPI_STRTOUL_BASE16 - Input string is hexadecimal. Default + * is 10/16 based on string prefix (0x). + * + * NOTES: + * Negative numbers are not supported, as they are not supported by ACPI. + * + * Supports only base 16 or base 10 strings/values. Does not + * support Octal strings, as these are not supported by ACPI. + * + * Current users of this support: + * + * interpreter - Implicit and explicit conversions, GPE method names + * debugger - Command line input string conversion + * iASL - Main parser, conversion of constants to integers + * iASL - Data Table Compiler parser (constant math expressions) + * iASL - Preprocessor (constant math expressions) + * acpi_dump - Input table addresses + * acpi_exec - Testing of the acpi_ut_strtoul64 function + * + * Note concerning callers: + * acpi_gbl_integer_byte_width can be used to set the 32/64 limit. If used, + * this global should be set to the proper width. For the core ACPICA code, + * this width depends on the DSDT version. For iASL, the default byte + * width is always 8 for the parser, but error checking is performed later + * to flag cases where a 64-bit constant is defined in a 32-bit DSDT/SSDT. + * + ******************************************************************************/ + +acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *return_value) +{ + acpi_status status = AE_OK; + u32 base; + + ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string); + + /* Parameter validation */ + + if (!string || !return_value) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + *return_value = 0; + + /* Check for zero-length string, returns 0 */ + + if (*string == 0) { + return_ACPI_STATUS(AE_OK); + } + + /* Skip over any white space at start of string */ + + while (isspace((int)*string)) { + string++; + } + + /* End of string? return 0 */ + + if (*string == 0) { + return_ACPI_STATUS(AE_OK); + } + + /* + * 1) The "0x" prefix indicates base 16. Per the ACPI specification, + * the "0x" prefix is only allowed for implicit (non-strict) conversions. + * However, we always allow it for compatibility with older ACPICA. + */ + if ((*string == ACPI_ASCII_ZERO) && + (tolower((int)*(string + 1)) == 'x')) { + string += 2; /* Go past the 0x */ + if (*string == 0) { + return_ACPI_STATUS(AE_OK); /* Return value 0 */ + } + + base = 16; + } + + /* 2) Force to base 16 (implicit conversion case) */ + + else if (flags & ACPI_STRTOUL_BASE16) { + base = 16; + } + + /* 3) Default fallback is to Base 10 */ + + else { + base = 10; + } + + /* Skip all leading zeros */ + + while (*string == ACPI_ASCII_ZERO) { + string++; + if (*string == 0) { + return_ACPI_STATUS(AE_OK); /* Return value 0 */ + } + } + + /* Perform the base 16 or 10 conversion */ + + if (base == 16) { + *return_value = acpi_ut_strtoul_base16(string, flags); + } else { + *return_value = acpi_ut_strtoul_base10(string, flags); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul_base10 + * + * PARAMETERS: string - Null terminated input string + * flags - Conversion info + * + * RETURN: 64-bit converted integer + * + * DESCRIPTION: Performs a base 10 conversion of the input string to an + * integer value, either 32 or 64 bits. + * Note: String must be valid and non-null. + * + ******************************************************************************/ + +static u64 acpi_ut_strtoul_base10(char *string, u32 flags) +{ + int ascii_digit; + u64 next_value; + u64 return_value = 0; + + /* Main loop: convert each ASCII byte in the input string */ + + while (*string) { + ascii_digit = *string; + if (!isdigit(ascii_digit)) { + + /* Not ASCII 0-9, terminate */ + + goto exit; + } + + /* Convert and insert (add) the decimal digit */ + + next_value = + (return_value * 10) + (ascii_digit - ACPI_ASCII_ZERO); + + /* Check for overflow (32 or 64 bit) - return current converted value */ + + if (((flags & ACPI_STRTOUL_32BIT) && (next_value > ACPI_UINT32_MAX)) || (next_value < return_value)) { /* 64-bit overflow case */ + goto exit; + } + + return_value = next_value; + string++; + } + +exit: + return (return_value); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul_base16 + * + * PARAMETERS: string - Null terminated input string + * flags - conversion info + * + * RETURN: 64-bit converted integer + * + * DESCRIPTION: Performs a base 16 conversion of the input string to an + * integer value, either 32 or 64 bits. + * Note: String must be valid and non-null. + * + ******************************************************************************/ + +static u64 acpi_ut_strtoul_base16(char *string, u32 flags) +{ + int ascii_digit; + u32 valid_digits = 1; + u64 return_value = 0; + + /* Main loop: convert each ASCII byte in the input string */ + + while (*string) { + + /* Check for overflow (32 or 64 bit) - return current converted value */ + + if ((valid_digits > 16) || + ((valid_digits > 8) && (flags & ACPI_STRTOUL_32BIT))) { + goto exit; + } + + ascii_digit = *string; + if (!isxdigit(ascii_digit)) { + + /* Not Hex ASCII A-F, a-f, or 0-9, terminate */ + + goto exit; + } + + /* Convert and insert the hex digit */ + + return_value = + (return_value << 4) | + acpi_ut_ascii_char_to_hex(ascii_digit); + + string++; + valid_digits++; + } + +exit: + return (return_value); +} diff --git a/tools/power/acpi/tools/acpidump/Makefile b/tools/power/acpi/tools/acpidump/Makefile index a710f60..04b5db7 100644 --- a/tools/power/acpi/tools/acpidump/Makefile +++ b/tools/power/acpi/tools/acpidump/Makefile @@ -36,10 +36,12 @@ TOOL_OBJS = \ utdebug.o\ utexcep.o\ utglobal.o\ + uthex.o\ utmath.o\ utnonansi.o\ utprint.o\ utstring.o\ + utstrtoul64.o\ utxferror.o\ oslinuxtbl.o\ cmfsize.o\ diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index 1c4e00b..9031be1 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c @@ -287,8 +287,8 @@ int ap_dump_table_by_address(char *ascii_address) /* Convert argument to an integer physical address */ - status = acpi_ut_strtoul64(ascii_address, ACPI_ANY_BASE, - ACPI_MAX64_BYTE_WIDTH, &long_address); + status = acpi_ut_strtoul64(ascii_address, ACPI_STRTOUL_64BIT, + &long_address); if (ACPI_FAILURE(status)) { fprintf(stderr, "%s: Could not convert to a physical address\n", ascii_address); diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index f32968e..7ff46be 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -208,8 +208,8 @@ static int ap_do_options(int argc, char **argv) case 'r': /* Dump tables from specified RSDP */ status = - acpi_ut_strtoul64(acpi_gbl_optarg, ACPI_ANY_BASE, - ACPI_MAX64_BYTE_WIDTH, + acpi_ut_strtoul64(acpi_gbl_optarg, + ACPI_STRTOUL_64BIT, &gbl_rsdp_base); if (ACPI_FAILURE(status)) { fprintf(stderr, -- cgit v0.10.2 From eacce4b6ce073b69830f5d816282af5317a2e98c Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 7 Sep 2016 14:06:10 +0800 Subject: ACPICA: Add a couple of casts to uthex.c ACPICA commit 2ba5d3fdaa24d66d67694cbae6ec66971a7a67c1 Required in some environments. Link: https://github.com/acpica/acpica/commit/2ba5d3fd Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c index 3a4ca95..36d2fc7 100644 --- a/drivers/acpi/acpica/uthex.c +++ b/drivers/acpi/acpica/uthex.c @@ -92,7 +92,8 @@ acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte) /* Both ASCII characters must be valid hex digits */ - if (!isxdigit(two_ascii_chars[0]) || !isxdigit(two_ascii_chars[1])) { + if (!isxdigit((int)two_ascii_chars[0]) || + !isxdigit((int)two_ascii_chars[1])) { return (AE_BAD_HEX_CONSTANT); } -- cgit v0.10.2 From 752db1016019a4e67d86492fdfda724215ee8d9b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:06:17 +0800 Subject: ACPICA: Tables: Remove acpi_tb_install_fixed_table() ACPICA commit 42c7b848d2faa02c7691ef2c53ea741c23cd4665 acpi_tb_install_fixed_table() is now redundant as we've removed the fixed table indexing mechanism: Commit: 8ec3f459073e67e5c6d78507dec693064b3040a2 Subject: ACPICA: Tables: Fix global table list issues by removing fixed table indexes This patch cleans up the code accordingly. No functional change. Lv Zheng. Link: https://bugs.acpica.org/show_bug.cgi?id=1320 Link: https://github.com/acpica/acpica/commit/42c7b848 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 86d4d62..9469cd4 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -155,12 +155,7 @@ void acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc, u8 override, u32 *table_index); -acpi_status -acpi_tb_install_fixed_table(acpi_physical_address address, - char *signature, u32 *table_index); - -acpi_status ACPI_INIT_FUNCTION -acpi_tb_parse_root_table(acpi_physical_address rsdp_address); +acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address); /* * tbxfload diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 016bcdc..e678d6f 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -344,23 +344,27 @@ void acpi_tb_parse_fadt(void) /* Obtain the DSDT and FACS tables via their addresses within the FADT */ - acpi_tb_install_fixed_table((acpi_physical_address)acpi_gbl_FADT.Xdsdt, - ACPI_SIG_DSDT, &acpi_gbl_dsdt_index); + acpi_tb_install_standard_table((acpi_physical_address)acpi_gbl_FADT. + Xdsdt, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL, + FALSE, TRUE, &acpi_gbl_dsdt_index); /* If Hardware Reduced flag is set, there is no FACS */ if (!acpi_gbl_reduced_hardware) { if (acpi_gbl_FADT.facs) { - acpi_tb_install_fixed_table((acpi_physical_address) - acpi_gbl_FADT.facs, - ACPI_SIG_FACS, - &acpi_gbl_facs_index); + acpi_tb_install_standard_table((acpi_physical_address) + acpi_gbl_FADT.facs, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL, + FALSE, TRUE, + &acpi_gbl_facs_index); } if (acpi_gbl_FADT.Xfacs) { - acpi_tb_install_fixed_table((acpi_physical_address) - acpi_gbl_FADT.Xfacs, - ACPI_SIG_FACS, - &acpi_gbl_xfacs_index); + acpi_tb_install_standard_table((acpi_physical_address) + acpi_gbl_FADT.Xfacs, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL, + FALSE, TRUE, + &acpi_gbl_xfacs_index); } } } diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 8b13052..d461864 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -157,68 +157,6 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc, /******************************************************************************* * - * FUNCTION: acpi_tb_install_fixed_table - * - * PARAMETERS: address - Physical address of DSDT or FACS - * signature - Table signature, NULL if no need to - * match - * table_index - Where the table index is returned - * - * RETURN: Status - * - * DESCRIPTION: Install a fixed ACPI table (DSDT/FACS) into the global data - * structure. - * - ******************************************************************************/ - -acpi_status -acpi_tb_install_fixed_table(acpi_physical_address address, - char *signature, u32 *table_index) -{ - struct acpi_table_desc new_table_desc; - acpi_status status; - - ACPI_FUNCTION_TRACE(tb_install_fixed_table); - - if (!address) { - ACPI_ERROR((AE_INFO, - "Null physical address for ACPI table [%s]", - signature)); - return (AE_NO_MEMORY); - } - - /* Fill a table descriptor for validation */ - - status = acpi_tb_acquire_temp_table(&new_table_desc, address, - ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL); - if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, - "Could not acquire table length at %8.8X%8.8X", - ACPI_FORMAT_UINT64(address))); - return_ACPI_STATUS(status); - } - - /* Validate and verify a table before installation */ - - status = acpi_tb_verify_temp_table(&new_table_desc, signature); - if (ACPI_FAILURE(status)) { - goto release_and_exit; - } - - /* Add the table to the global root table list */ - - acpi_tb_install_table_with_override(&new_table_desc, TRUE, table_index); - -release_and_exit: - - /* Release the temporary table descriptor */ - - acpi_tb_release_temp_table(&new_table_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * * FUNCTION: acpi_tb_install_standard_table * * PARAMETERS: address - Address of the table (might be a virtual @@ -230,8 +168,7 @@ release_and_exit: * * RETURN: Status * - * DESCRIPTION: This function is called to install an ACPI table that is - * neither DSDT nor FACS (a "standard" table.) + * DESCRIPTION: This function is called to verify and install an ACPI table. * When this function is called by "Load" or "LoadTable" opcodes, * or by acpi_load_table() API, the "Reload" parameter is set. * After sucessfully returning from this function, table is -- cgit v0.10.2 From 6eecbc9b8a030cf9c0ed52dd19b5d122503b8c4c Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:06:24 +0800 Subject: ACPICA: Tables: Remove wrong table event macros ACPICA commit fcc129d04f865161f308d3b8743496cc3f4d233e There are wrong table event macros, this patch cleans them up. Link: https://bugs.acpica.org/show_bug.cgi?id=1321 Link: https://github.com/acpica/acpica/commit/fcc129d0 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index e96907b..892595f 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1034,12 +1034,6 @@ struct acpi_statistics { u32 method_count; }; -/* Table Event Types */ - -#define ACPI_TABLE_EVENT_LOAD 0x0 -#define ACPI_TABLE_EVENT_UNLOAD 0x1 -#define ACPI_NUM_TABLE_EVENTS 2 - /* * Types specific to the OS service interfaces */ @@ -1091,9 +1085,11 @@ acpi_status (*acpi_exception_handler) (acpi_status aml_status, typedef acpi_status (*acpi_table_handler) (u32 event, void *table, void *context); -#define ACPI_TABLE_LOAD 0x0 -#define ACPI_TABLE_UNLOAD 0x1 -#define ACPI_NUM_TABLE_EVENTS 2 +/* Table Event Types */ + +#define ACPI_TABLE_EVENT_LOAD 0x0 +#define ACPI_TABLE_EVENT_UNLOAD 0x1 +#define ACPI_NUM_TABLE_EVENTS 2 /* Address Spaces (For Operation Regions) */ -- cgit v0.10.2 From bdbe5df025b712220ba8d807662d4449b2092e54 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:06:32 +0800 Subject: ACPICA: Tables: Add new table events indicating table installation/uninstallation ACPICA commit ed6a5fbc694f3a27d93014391aa9a6f6fe490461 This patch adds 2 new table events to indicate table installation/uninstallation. Currently, as ACPICA never uninstalls tables, this patch thus only adds table handler invocation for the table installation event. Lv Zheng. The 2 events are to be used to fix a sysfs table handling issue related to LoadTable opcode (see Link # [1] below). The actual sysfs fixing code is not included, the sysfs fixes will be sent as separate patches. Link: https://bugzilla.kernel.org/show_bug.cgi?id=150841 # [1] Link: https://github.com/acpica/acpica/commit/ed6a5fbc Reported-by: Jason Voelz Reported-by: Francisco Leoner Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index d461864..5fdf251 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -301,6 +301,14 @@ acpi_tb_install_standard_table(acpi_physical_address address, acpi_tb_install_table_with_override(&new_table_desc, override, table_index); + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_INSTALL, + new_table_desc.pointer, + acpi_gbl_table_handler_context); + } + release_and_exit: /* Release the temporary table descriptor */ diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 892595f..1d798ab 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1089,7 +1089,9 @@ acpi_status (*acpi_table_handler) (u32 event, void *table, void *context); #define ACPI_TABLE_EVENT_LOAD 0x0 #define ACPI_TABLE_EVENT_UNLOAD 0x1 -#define ACPI_NUM_TABLE_EVENTS 2 +#define ACPI_TABLE_EVENT_INSTALL 0x2 +#define ACPI_TABLE_EVENT_UNINSTALL 0x3 +#define ACPI_NUM_TABLE_EVENTS 4 /* Address Spaces (For Operation Regions) */ -- cgit v0.10.2 From 4e0b26d3910cd69db27c4af0954f6499b8eef038 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:06:39 +0800 Subject: ACPICA: Tables: Override all 64-bit GAS fields when acpi_gbl_use32_bit_fadt_addresses is TRUE ACPICA commit aaace77db4c3b267a65b75c33f84ace6f65bbcf7 Originally, when acpi_gbl_use32_bit_fadt_addresses is TRUE, GAS override can only happen when the Address field mismatches. According to the investigation result, Windows may favor 32-bit FADT addresses in some cases. So we need this quirk working after enabling full GAS support. This requires us to override GAS access_size/bit_width/bit_offset fields as long as acpi_gbl_use32_bit_fadt_addresses is TRUE. This patch enhances this quirk mechanism to make it working with full GAS support. Lv Zheng. Link: https://bugzilla.kernel.org/show_bug.cgi?id=151501 Link: https://github.com/acpica/acpica/commit/aaace77d Reported-and-tested-by: Andrey Skvortsov Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index e678d6f..046c4d0 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -558,78 +558,74 @@ static void acpi_tb_convert_fadt(void) * * Address32 zero, Address64 [don't care] - Use Address64 * + * No override: if acpi_gbl_use32_bit_fadt_addresses is FALSE, and: * Address32 non-zero, Address64 zero - Copy/use Address32 * Address32 non-zero == Address64 non-zero - Use Address64 * Address32 non-zero != Address64 non-zero - Warning, use Address64 * * Override: if acpi_gbl_use32_bit_fadt_addresses is TRUE, and: + * Address32 non-zero, Address64 zero - Copy/use Address32 + * Address32 non-zero == Address64 non-zero - Copy/use Address32 * Address32 non-zero != Address64 non-zero - Warning, copy/use Address32 * * Note: space_id is always I/O for 32-bit legacy address fields */ if (address32) { - if (!address64->address) { + if (address64->address) { + if (address64->address != (u64)address32) { + + /* Address mismatch */ + + ACPI_BIOS_WARNING((AE_INFO, + "32/64X address mismatch in FADT/%s: " + "0x%8.8X/0x%8.8X%8.8X, using %u-bit address", + name, address32, + ACPI_FORMAT_UINT64 + (address64->address), + acpi_gbl_use32_bit_fadt_addresses + ? 32 : 64)); + } - /* 64-bit address is zero, use 32-bit address */ + /* + * For each extended field, check for length mismatch + * between the legacy length field and the corresponding + * 64-bit X length field. + * Note: If the legacy length field is > 0xFF bits, ignore + * this check. (GPE registers can be larger than the + * 64-bit GAS structure can accomodate, 0xFF bits). + */ + if ((ACPI_MUL_8(length) <= ACPI_UINT8_MAX) && + (address64->bit_width != + ACPI_MUL_8(length))) { + ACPI_BIOS_WARNING((AE_INFO, + "32/64X length mismatch in FADT/%s: %u/%u", + name, + ACPI_MUL_8(length), + address64-> + bit_width)); + } + } + /* + * Hardware register access code always uses the 64-bit fields. + * So if the 64-bit field is zero or is to be overridden, + * initialize it with the 32-bit fields. + * Note that when the 32-bit address favor is specified, the + * 64-bit fields are always re-initialized so that + * access_size/bit_width/bit_offset fields can be correctly + * configured to the values to trigger a 32-bit compatible + * access mode in the hardware register access code. + */ + if (!address64->address + || acpi_gbl_use32_bit_fadt_addresses) { acpi_tb_init_generic_address(address64, ACPI_ADR_SPACE_SYSTEM_IO, - *ACPI_ADD_PTR(u8, - &acpi_gbl_FADT, - fadt_info_table - [i]. - length), + length, (u64)address32, name, flags); - } else if (address64->address != (u64)address32) { - - /* Address mismatch */ - - ACPI_BIOS_WARNING((AE_INFO, - "32/64X address mismatch in FADT/%s: " - "0x%8.8X/0x%8.8X%8.8X, using %u-bit address", - name, address32, - ACPI_FORMAT_UINT64 - (address64->address), - acpi_gbl_use32_bit_fadt_addresses - ? 32 : 64)); - - if (acpi_gbl_use32_bit_fadt_addresses) { - - /* 32-bit address override */ - - acpi_tb_init_generic_address(address64, - ACPI_ADR_SPACE_SYSTEM_IO, - *ACPI_ADD_PTR - (u8, - &acpi_gbl_FADT, - fadt_info_table - [i]. - length), - (u64) - address32, - name, - flags); - } } } - /* - * For each extended field, check for length mismatch between the - * legacy length field and the corresponding 64-bit X length field. - * Note: If the legacy length field is > 0xFF bits, ignore this - * check. (GPE registers can be larger than the 64-bit GAS structure - * can accomodate, 0xFF bits). - */ - if (address64->address && - (ACPI_MUL_8(length) <= ACPI_UINT8_MAX) && - (address64->bit_width != ACPI_MUL_8(length))) { - ACPI_BIOS_WARNING((AE_INFO, - "32/64X length mismatch in FADT/%s: %u/%u", - name, ACPI_MUL_8(length), - address64->bit_width)); - } - if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) { /* * Field is required (Pm1a_event, Pm1a_control). -- cgit v0.10.2 From 955f485dc40cdee265a85ad4af378f2a2cfaf11b Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 7 Sep 2016 14:06:47 +0800 Subject: ACPICA: Update return value for intenal _OSI method ACPICA commit 82101009c7c04845edb3495e66a274a613758bca Instead of 0xFFFFFFFF, _OSI is now defined to return "Ones". This is for compatibility with Windows. The ACPI spec will be updated to reflect this. Link: https://github.com/acpica/acpica/commit/82101009 Reported-by: Daniel Drake Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index 3f5fed6..f0484b0 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -390,11 +390,22 @@ struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name) * PARAMETERS: walk_state - Current walk state * * RETURN: Status + * Integer: TRUE (0) if input string is matched + * FALSE (-1) if string is not matched * * DESCRIPTION: Implementation of the _OSI predefined control method. When * an invocation of _OSI is encountered in the system AML, * control is transferred to this function. * + * (August 2016) + * Note: _OSI is now defined to return "Ones" to indicate a match, for + * compatibility with other ACPI implementations. On a 32-bit DSDT, Ones + * is 0xFFFFFFFF. On a 64-bit DSDT, Ones is 0xFFFFFFFFFFFFFFFF + * (ACPI_UINT64_MAX). + * + * This function always returns ACPI_UINT64_MAX for TRUE, and later code + * will truncate this to 32 bits if necessary. + * ******************************************************************************/ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) @@ -404,7 +415,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) struct acpi_interface_info *interface_info; acpi_interface_handler interface_handler; acpi_status status; - u32 return_value; + u64 return_value; ACPI_FUNCTION_TRACE(ut_osi_implementation); @@ -444,7 +455,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) acpi_gbl_osi_data = interface_info->value; } - return_value = ACPI_UINT32_MAX; + return_value = ACPI_UINT64_MAX; } acpi_os_release_mutex(acpi_gbl_osi_mutex); @@ -456,9 +467,10 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) */ interface_handler = acpi_gbl_interface_handler; if (interface_handler) { - return_value = - interface_handler(string_desc->string.pointer, - return_value); + if (interface_handler + (string_desc->string.pointer, (u32)return_value)) { + return_value = ACPI_UINT64_MAX; + } } ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, -- cgit v0.10.2 From de56ba95e8d6d760910711744a548b50b3a4262d Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:06:54 +0800 Subject: ACPICA: Interpreter: Fix MLC issues by switching to new term_list grammar for table loading ACPICA commit 0e24fb67cde08d7df7671d7d7b183490dc79707e The MLC (Module Level Code) is an ACPICA terminology describing the AML code out of any control method, its support is an indication of the interpreter behavior during the table loading. The original implementation of MLC in ACPICA had several issues: 1. Out of any control method, besides of the object creating opcodes, only the code blocks wrapped by "If/Else/While" opcodes were supported. 2. The supported MLC code blocks were executed after loading the table rather than being executed right in place. ============================================================ The demo of this order issue is as follows: Name (OBJ1, 1) If (CND1 == 1) { Name (OBJ2, 2) } Name (OBJ3, 3) The original MLC support created OBJ2 after OBJ3's creation. ============================================================ Other than these limitations, MLC support in ACPICA looks correct. And supporting this should be easy/natural for ACPICA, but enabling of this was blocked by some ACPICA internal and OSPM specific initialization order issues we've fixed recently. The wrong support started from the following false bug fixing commit: Commit: 7f0c826a437157d2b19662977e9cf3b472cf24a6 Subject: ACPICA: Add support for module-level executable AML code Commit: 9a884ab64a4d092b4c3bf24fd9a30f7fbd4591e7 Subject: ACPICA: Add additional module-level code support ... We can confirm Windows interpreter behavior via reverse engineering means. It can be proven that not only If/Else/While wrapped code blocks, all opcodes can be executed at the module level, including operation region accesses. And it can be proven that the MLC should be executed right in place, not in such a deferred way executed after loading the table. And the above facts indeed reflect the spec words around ACPI definition block tables (DSDT/SSDT/...), the entire table and the Scope object is defined by the AML specification in BNF style as: AMLCode := def_block_header term_list def_scope := scope_op pkg_length name_string term_list The bodies of the scope opening terms (AMLCode/Scope) are all term_list, thus the table loading should be no difference than the control method evaluations as the body of the Method is also defined by the AML specification as term_list: def_method := method_op pkg_length name_string method_flags term_list The only difference is: after evaluating control method, created named objects may be freed due to no reference, while named objects created by the table loading should only be freed after unloading the table. So this patch follows the spec and the de-facto standard behavior, enables the new grammar (term_list) for the table loading. By doing so, beyond the fixes to the above issues, we can see additional differences comparing to the old grammar based table loading: 1. Originally, beyond the scope opening terms (AMLCode/Scope), If/Else/While wrapped code blocks under the scope creating terms (Device/power_resource/Processor/thermal_zone) are also supported as deferred MLC, which violates the spec defined grammar where object_list is enforced. With MLC support improved as non-deferred, the interpreter parses such scope creating terms as term_list rather object_list like the scope opening terms. After probing the Windows behavior and proving that it also parses these terms as term_list, we submitted an ECR (Engineering Change Request) to the ASWG (ACPI Specification Working Group) to clarify this. The ECR is titled as "ASL Grammar Clarification for Executable AML Opcodes" and has been accepted by the ASWG. The new grammar will appear in ACPI specification 6.2. 2. Originally, Buffer/Package/operation_region/create_XXXField/bank_field arguments are evaluated in a deferred way after loading the table. With MLC support improved, they are also parsed right in place during the table loading. This is also Windows compliant and the only difference is the removal of the debugging messages implemented before acpi_ds_execute_arguments(), see Link # [1] for the details. A previous commit should have ensured that acpi_check_address_range() won't regress. Note that enabling this feature may cause regressions due to long term Linux ACPI support on top of the wrong grammar. So this patch also prepares a global option to be used to roll back to the old grammar during the period between a regression is reported and the regression is root-cause-fixed. Lv Zheng. Link: https://bugzilla.kernel.org/show_bug.cgi?id=112911 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=117671 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=153541 # [1] Link: https://github.com/acpica/acpica/issues/122 Link: https://bugs.acpica.org/show_bug.cgi?id=963 Link: https://github.com/acpica/acpica/commit/0e24fb67 Reported-and-tested-by: Chris Bainbridge Reported-by: Ehsan Reported-and-tested-by: Dutch Guy Tested-by: Mika Westerberg Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index f33a4ba..829672a 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -130,6 +130,9 @@ acpi_status acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node); acpi_status +acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node); + +acpi_status acpi_ns_one_complete_parse(u32 pass_number, u32 table_index, struct acpi_namespace_node *start_node); diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index fc30577..939d411 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -78,6 +78,8 @@ extern const u8 acpi_gbl_long_op_index[]; */ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info); +acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info); + /* * psargs - Parse AML opcode arguments */ diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index b6ea9c0..3843f1f 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -553,7 +553,8 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj, * * See acpi_ns_exec_module_code */ - if (obj_desc->method. + if (!acpi_gbl_parse_table_as_term_list && + obj_desc->method. info_flags & ACPI_METHOD_MODULE_LEVEL) { handler_obj = obj_desc->method.dispatch.handler; diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index a1d177d..74dd5ba 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -118,7 +118,8 @@ acpi_ex_add_table(u32 table_index, /* Execute any module-level code that was found in the table */ acpi_ex_exit_interpreter(); - if (acpi_gbl_group_module_level_code) { + if (!acpi_gbl_parse_table_as_term_list + && acpi_gbl_group_module_level_code) { acpi_ns_exec_module_code_list(); } acpi_ex_enter_interpreter(); diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index b5e2b0a..2daa9a09 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -162,7 +162,8 @@ unlock: * other ACPI implementations. Optionally, the execution can be deferred * until later, see acpi_initialize_objects. */ - if (!acpi_gbl_group_module_level_code) { + if (!acpi_gbl_parse_table_as_term_list + && !acpi_gbl_group_module_level_code) { acpi_ns_exec_module_code_list(); } diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index f631a47..e51012b 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -53,6 +53,96 @@ ACPI_MODULE_NAME("nsparse") /******************************************************************************* * + * FUNCTION: ns_execute_table + * + * PARAMETERS: table_desc - An ACPI table descriptor for table to parse + * start_node - Where to enter the table into the namespace + * + * RETURN: Status + * + * DESCRIPTION: Load ACPI/AML table by executing the entire table as a + * term_list. + * + ******************************************************************************/ +acpi_status +acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node) +{ + acpi_status status; + struct acpi_table_header *table; + acpi_owner_id owner_id; + struct acpi_evaluate_info *info = NULL; + u32 aml_length; + u8 *aml_start; + union acpi_operand_object *method_obj = NULL; + + ACPI_FUNCTION_TRACE(ns_execute_table); + + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Table must consist of at least a complete header */ + + if (table->length < sizeof(struct acpi_table_header)) { + return_ACPI_STATUS(AE_BAD_HEADER); + } + + aml_start = (u8 *)table + sizeof(struct acpi_table_header); + aml_length = table->length - sizeof(struct acpi_table_header); + + status = acpi_tb_get_owner_id(table_index, &owner_id); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Create, initialize, and link a new temporary method object */ + + method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); + if (!method_obj) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + status = AE_NO_MEMORY; + goto cleanup; + } + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Create table code block: %p\n", method_obj)); + + method_obj->method.aml_start = aml_start; + method_obj->method.aml_length = aml_length; + method_obj->method.owner_id = owner_id; + method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL; + + info->pass_number = ACPI_IMODE_EXECUTE; + info->node = start_node; + info->obj_desc = method_obj; + info->node_flags = info->node->flags; + info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE); + if (!info->full_pathname) { + status = AE_NO_MEMORY; + goto cleanup; + } + + status = acpi_ps_execute_table(info); + +cleanup: + if (info) { + ACPI_FREE(info->full_pathname); + info->full_pathname = NULL; + } + ACPI_FREE(info); + acpi_ut_remove_reference(method_obj); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * * FUNCTION: ns_one_complete_parse * * PARAMETERS: pass_number - 1 or 2 @@ -63,6 +153,7 @@ ACPI_MODULE_NAME("nsparse") * DESCRIPTION: Perform one complete parse of an ACPI/AML table. * ******************************************************************************/ + acpi_status acpi_ns_one_complete_parse(u32 pass_number, u32 table_index, @@ -170,38 +261,47 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node) ACPI_FUNCTION_TRACE(ns_parse_table); - /* - * AML Parse, pass 1 - * - * In this pass, we load most of the namespace. Control methods - * are not parsed until later. A parse tree is not created. Instead, - * each Parser Op subtree is deleted when it is finished. This saves - * a great deal of memory, and allows a small cache of parse objects - * to service the entire parse. The second pass of the parse then - * performs another complete parse of the AML. - */ - ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n")); - - status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1, - table_index, start_node); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } + if (acpi_gbl_parse_table_as_term_list) { + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start load pass\n")); - /* - * AML Parse, pass 2 - * - * In this pass, we resolve forward references and other things - * that could not be completed during the first pass. - * Another complete parse of the AML is performed, but the - * overhead of this is compensated for by the fact that the - * parse objects are all cached. - */ - ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n")); - status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2, - table_index, start_node); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + status = acpi_ns_execute_table(table_index, start_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } else { + /* + * AML Parse, pass 1 + * + * In this pass, we load most of the namespace. Control methods + * are not parsed until later. A parse tree is not created. + * Instead, each Parser Op subtree is deleted when it is finished. + * This saves a great deal of memory, and allows a small cache of + * parse objects to service the entire parse. The second pass of + * the parse then performs another complete parse of the AML. + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n")); + + status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1, + table_index, start_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * AML Parse, pass 2 + * + * In this pass, we resolve forward references and other things + * that could not be completed during the first pass. + * Another complete parse of the AML is performed, but the + * overhead of this is compensated for by the fact that the + * parse objects are all cached. + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n")); + status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2, + table_index, start_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } } return_ACPI_STATUS(status); diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 0a23897..3aa9162 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -571,7 +571,9 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state) * cleanup to do */ if (((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == - ACPI_PARSE_EXECUTE) || (ACPI_FAILURE(status))) { + ACPI_PARSE_EXECUTE && + !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) || + (ACPI_FAILURE(status))) { acpi_ds_terminate_control_method(walk_state-> method_desc, walk_state); diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index cf30cd82..22a52c28 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -252,6 +252,77 @@ cleanup: /******************************************************************************* * + * FUNCTION: acpi_ps_execute_table + * + * PARAMETERS: info - Method info block, contains: + * node - Node to where the is entered into the + * namespace + * obj_desc - Pseudo method object describing the AML + * code of the entire table + * pass_number - Parse or execute pass + * + * RETURN: Status + * + * DESCRIPTION: Execute a table + * + ******************************************************************************/ + +acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info) +{ + acpi_status status; + union acpi_parse_object *op = NULL; + struct acpi_walk_state *walk_state = NULL; + + ACPI_FUNCTION_TRACE(ps_execute_table); + + /* Create and init a Root Node */ + + op = acpi_ps_create_scope_op(info->obj_desc->method.aml_start); + if (!op) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Create and initialize a new walk state */ + + walk_state = + acpi_ds_create_walk_state(info->obj_desc->method.owner_id, NULL, + NULL, NULL); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + + status = acpi_ds_init_aml_walk(walk_state, op, info->node, + info->obj_desc->method.aml_start, + info->obj_desc->method.aml_length, info, + info->pass_number); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) { + walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL; + } + + /* + * Parse the AML, walk_state will be deleted by parse_aml + */ + status = acpi_ps_parse_aml(walk_state); + walk_state = NULL; + +cleanup: + if (walk_state) { + acpi_ds_delete_walk_state(walk_state); + } + if (op) { + acpi_ps_delete_parse_tree(op); + } + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * * FUNCTION: acpi_ps_update_parameter_list * * PARAMETERS: info - See struct acpi_evaluate_info diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index e0cc919..6088712 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -103,7 +103,8 @@ acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void) "While loading namespace from ACPI tables")); } - if (!acpi_gbl_group_module_level_code) { + if (acpi_gbl_parse_table_as_term_list + || !acpi_gbl_group_module_level_code) { /* * Initialize the objects that remain uninitialized. This * runs the executable AML that may be part of the diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c index d6d9462..a5ca0f5 100644 --- a/drivers/acpi/acpica/utxfinit.c +++ b/drivers/acpi/acpica/utxfinit.c @@ -265,7 +265,8 @@ acpi_status ACPI_INIT_FUNCTION acpi_initialize_objects(u32 flags) * all of the tables have been loaded. It is a legacy option and is * not compatible with other ACPI implementations. See acpi_ns_load_table. */ - if (acpi_gbl_group_module_level_code) { + if (!acpi_gbl_parse_table_as_term_list + && acpi_gbl_group_module_level_code) { acpi_ns_exec_module_code_list(); /* diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 8b44717..cf78ec2 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -195,6 +195,13 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, TRUE); /* + * Optionally support module level code by parsing the entire table as + * a term_list. Default is FALSE, do not execute entire table until some + * lock order issues are fixed. + */ +ACPI_INIT_GLOBAL(u8, acpi_gbl_parse_table_as_term_list, FALSE); + +/* * Optionally use 32-bit FADT addresses if and when there is a conflict * (address mismatch) between the 32-bit and 64-bit versions of the * address. Although ACPICA adheres to the ACPI specification which -- cgit v0.10.2 From c2d981aaed431c6af46f0a17907ed825946a5839 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:07:02 +0800 Subject: ACPICA: Namespace: Add acpi_ns_get_node_unlocked() ACPICA commit 3ef1a1bf5612fe1a629424c09eaaeb6f299d313c Add acpi_ns_get_node_unlocked() to be used when ACPI_MTX_NAMESPACE is locked. Lv Zheng. Link: https://github.com/acpica/acpica/commit/3ef1a1bf Tested-by: Mika Westerberg Tested-by: Greg White Tested-by: Dutch Guy Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 829672a..bb7fca1 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -299,6 +299,11 @@ u8 acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for); acpi_status +acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node, + const char *external_pathname, + u32 flags, struct acpi_namespace_node **out_node); + +acpi_status acpi_ns_get_node(struct acpi_namespace_node *prefix_node, const char *external_pathname, u32 flags, struct acpi_namespace_node **out_node); diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 784a30b..691814d 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -662,7 +662,7 @@ u32 acpi_ns_opens_scope(acpi_object_type type) /******************************************************************************* * - * FUNCTION: acpi_ns_get_node + * FUNCTION: acpi_ns_get_node_unlocked * * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The * \ (backslash) and ^ (carat) prefixes, and the @@ -678,20 +678,21 @@ u32 acpi_ns_opens_scope(acpi_object_type type) * DESCRIPTION: Look up a name relative to a given scope and return the * corresponding Node. NOTE: Scope can be null. * - * MUTEX: Locks namespace + * MUTEX: Doesn't locks namespace * ******************************************************************************/ acpi_status -acpi_ns_get_node(struct acpi_namespace_node *prefix_node, - const char *pathname, - u32 flags, struct acpi_namespace_node **return_node) +acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node, + const char *pathname, + u32 flags, struct acpi_namespace_node **return_node) { union acpi_generic_state scope_info; acpi_status status; char *internal_path; - ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname)); + ACPI_FUNCTION_TRACE_PTR(ns_get_node_unlocked, + ACPI_CAST_PTR(char, pathname)); /* Simplest case is a null pathname */ @@ -718,13 +719,6 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node, return_ACPI_STATUS(status); } - /* Must lock namespace during lookup */ - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto cleanup; - } - /* Setup lookup scope (search starting point) */ scope_info.scope.node = prefix_node; @@ -740,9 +734,49 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node, pathname, acpi_format_exception(status))); } - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - -cleanup: ACPI_FREE(internal_path); return_ACPI_STATUS(status); } + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_node + * + * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The + * \ (backslash) and ^ (carat) prefixes, and the + * . (period) to separate segments are supported. + * prefix_node - Root of subtree to be searched, or NS_ALL for the + * root of the name space. If Name is fully + * qualified (first s8 is '\'), the passed value + * of Scope will not be accessed. + * flags - Used to indicate whether to perform upsearch or + * not. + * return_node - Where the Node is returned + * + * DESCRIPTION: Look up a name relative to a given scope and return the + * corresponding Node. NOTE: Scope can be null. + * + * MUTEX: Locks namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_get_node(struct acpi_namespace_node *prefix_node, + const char *pathname, + u32 flags, struct acpi_namespace_node **return_node) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname)); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ns_get_node_unlocked(prefix_node, pathname, + flags, return_node); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} -- cgit v0.10.2 From 74f51b80a0c4ff84fbeb7f12ea43ce66934d29aa Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:07:10 +0800 Subject: ACPICA: Namespace: Fix dynamic table loading issues ACPICA commit 767ee53354e0c4b7e8e7c57c6dd7bf569f0d52bb There are issues related to the namespace/interpreter locks, which causes several ACPI functionalities not specification compliant. The lock issues were detectec when we were trying to fix the functionalities (please see Link # [1] for the details). What's the lock issues? Let's first look into the namespace/interpreter lock usages inside of the object evaluation and the table loading which are the key AML interpretion code paths: Table loading: acpi_ns_load_table L(Namespace) acpi_ns_parse_table acpi_ns_one_complete_parse(LOAD_PASS1/LOAD_PASS2) acpi_ds_load1_begion_op acpi_ds_load1_end_op acpi_ds_load2_begion_op acpi_ds_load2_end_op U(Namespace) Object evaluation: acpi_ns_evaluate L(Interpreter) acpi_ps_execute_method acpi_ds_exec_begin_op acpi_ds_exec_end_op U(Interpreter) acpi_ns_load_table L(Namespace) U(Namespace) acpi_ev_initialize_region L(Namespace) U(Namespace) address_space.Setup address_space.Handler acpi_os_wait_semaphore acpi_os_acquire_mutex acpi_os_sleep L(Interpreter) U(Interpreter) L(Interpreter) acpi_ex_resolve_node_to_value U(Interpreter) acpi_ns_check_return_value Where: 1. L(Interpreter) means acquire(MTX_INTERPRETER); 2. U(Interpreter) means release(MTX_INTERPRETER); 3. L(Namespace) means acquire(MTX_NAMESPACE); 4. U(Namespace) means release(MTX_NAMESPACE); We can see that acpi_ns_exec_module_code() (which invokes acpi_ns_evaluate) is implemented in a deferred way just in order to avoid to reacquire the namespace lock. This is in fact the root cause of many other ACPICA issues: 1. We now know for sure that the module code should be executed right in place by the Windows AML interpreter. So in the current design, if the region initializations/accesses or the table loadings (where the namespace surely should be locked again) happening during the table loading period, dead lock could happen because ACPICA never unlocks the namespace during the AML interpretion. 2. ACPICA interpreter just ensures that all static namespace nodes (named objects created during the acpi_load_tables()) are created (acpi_ns_lookup()) with the correct lock held, but doesn't ensure that the named objects created by the control method are created with the same correct lock held. It requires the control methods to be executed in a serial way after "loading a table", that's why ACPICA requires method auto serialization. This patch fixes these software design issues by extending interpreter enter/exit APIs to hold both interpreter/namespace locks to ensure the lock order correctness, so that we can get these code paths: Table loading: acpi_ns_load_table E(Interpreter) acpi_ns_parse_table acpi_ns_one_complete_parse acpi_ns_execute_table X(Interpreter) acpi_ns_load_table acpi_ev_initialize_region address_space.Setup address_space.Handler acpi_os_wait_semaphore acpi_os_acquire_mutex acpi_os_sleep E(Interpreter) X(Interpreter) Object evaluation: acpi_ns_evaluate E(Interpreter) acpi_ps_execute_method X(Interpreter) acpi_ns_load_table acpi_ev_initialize_region address_space.Setup address_space.Handler acpi_os_wait_semaphore acpi_os_acquire_mutex acpi_os_sleep E(Interpreter) X(Interpreter) Where: 1. E(Interpreter) means acquire(MTX_INTERPRETER, MTX_NAMESPACE); 2. X(Interpreter) means release(MTX_NAMESPACE, MTX_INTERPRETER); After this change, we can see: 1. All namespace nodes creations are locked by the namespace lock. 2. All namespace nodes referencing are locked with the same lock. 3. But we also can notice a defact that, all namespace nodes deletions could be affected by this change. As a consequence, acpi_ns_delete_namespace_subtree() may delete a static namespace node that is still referenced by the interpreter (for example, the parser scopes). Currently, we needn't worry about the last defact because in ACPICA, table unloading is not fully functioning, its design strictly relies on the fact that when the namespace deletion happens, either the AML table or the OSPMs should have been notified and thus either the AML table or the OSPMs shouldn't reference deletion-related namespace nodes during the namespace deletion. And this change still works with the above restrictions applied. While making this a-step-forward helps us to correct the wrong grammar to pull many things back to the correct rail. And pulling things back to the correct rail in return makes it possible for us to support fully functioning table unloading after doing many cleanups. While this patch is generated, all namespace locks are examined to ensure that they can meet either of the following pattens: 1. L(Namespace) U(Namespace) 2. E(Interpreter) X(Interpreter) 3. E(Interpreter) X(Interpreter) L(Namespace) U(Namespace) E(Interpreter) X(Interpreter) We ensure this by adding X(Interpreter)/E(Interpreter) or removing U(Namespace)/L(Namespace) for those currently are executed in the following order: E(Interpreter) L(Namespace) U(Namespace) X(Interpreter) And adding E(Interpreter)/X(Interpreter) for those currently are executed in the following order: X(Interpreter) E(Interpreter) Originally, the interpreter lock is held for the execution AML opcodes, the namespace lock is held for the named object creation AML opcodes. Since they are actually same in MS interpreter (can all be executed during the table loading), we can combine the 2 locks and tune the locking code better in this way. Lv Zheng. Link: https://bugzilla.kernel.org/show_bug.cgi?id=153541 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=121701 # [1] Link: https://bugs.acpica.org/show_bug.cgi?id=1323 Link: https://github.com/acpica/acpica/commit/767ee533 Reported-and-tested-by: Mika Westerberg Reported-and-tested-by: Greg White Reported-and-tested-by: Dutch Guy Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 47c7b52..25b387a 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -757,8 +757,10 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, /* Delete any direct children of (created by) this method */ + (void)acpi_ex_exit_interpreter(); acpi_ns_delete_namespace_subtree(walk_state-> method_node); + (void)acpi_ex_enter_interpreter(); /* * Delete any objects that were created by this method @@ -769,9 +771,11 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, */ if (method_desc->method. info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) { + (void)acpi_ex_exit_interpreter(); acpi_ns_delete_namespace_by_owner(method_desc-> method. owner_id); + (void)acpi_ex_enter_interpreter(); method_desc->method.info_flags &= ~ACPI_METHOD_MODIFIED_NAMESPACE; } diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c index 762db3f..028b22a 100644 --- a/drivers/acpi/acpica/dswload2.c +++ b/drivers/acpi/acpica/dswload2.c @@ -605,16 +605,13 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } - - acpi_ex_exit_interpreter(); } + acpi_ex_exit_interpreter(); status = acpi_ev_initialize_region (acpi_ns_get_attached_object(node), FALSE); - if (walk_state->method_node) { - acpi_ex_enter_interpreter(); - } + acpi_ex_enter_interpreter(); if (ACPI_FAILURE(status)) { /* diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 74dd5ba..578d5c8 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -198,9 +198,10 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, * Find the node referenced by the root_path_string. This is the * location within the namespace where the table will be loaded. */ - status = - acpi_ns_get_node(start_node, operand[3]->string.pointer, - ACPI_NS_SEARCH_PARENT, &parent_node); + status = acpi_ns_get_node_unlocked(start_node, + operand[3]->string.pointer, + ACPI_NS_SEARCH_PARENT, + &parent_node); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -220,9 +221,10 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, /* Find the node referenced by the parameter_path_string */ - status = - acpi_ns_get_node(start_node, operand[4]->string.pointer, - ACPI_NS_SEARCH_PARENT, ¶meter_node); + status = acpi_ns_get_node_unlocked(start_node, + operand[4]->string.pointer, + ACPI_NS_SEARCH_PARENT, + ¶meter_node); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 6ae19cb..0073004 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -891,14 +891,16 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) * Field, so we need to resolve the node to a value. */ status = - acpi_ns_get_node(walk_state->scope_info-> - scope.node, - operand[0]->string.pointer, - ACPI_NS_SEARCH_PARENT, - ACPI_CAST_INDIRECT_PTR - (struct - acpi_namespace_node, - &return_desc)); + acpi_ns_get_node_unlocked(walk_state-> + scope_info->scope. + node, + operand[0]-> + string.pointer, + ACPI_NS_SEARCH_PARENT, + ACPI_CAST_INDIRECT_PTR + (struct + acpi_namespace_node, + &return_desc)); if (ACPI_FAILURE(status)) { goto cleanup; } diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c index b52e848..c9ca826 100644 --- a/drivers/acpi/acpica/extrace.c +++ b/drivers/acpi/acpica/extrace.c @@ -201,7 +201,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node, union acpi_operand_object *obj_desc, struct acpi_walk_state *walk_state) { - acpi_status status; char *pathname = NULL; u8 enabled = FALSE; @@ -211,11 +210,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node, pathname = acpi_ns_get_normalized_pathname(method_node, TRUE); } - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit; - } - enabled = acpi_ex_interpreter_trace_enabled(pathname); if (enabled && !acpi_gbl_trace_method_object) { acpi_gbl_trace_method_object = obj_desc; @@ -233,9 +227,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node, } } - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - -exit: if (enabled) { ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE, obj_desc ? obj_desc->method.aml_start : NULL, @@ -267,7 +258,6 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node, union acpi_operand_object *obj_desc, struct acpi_walk_state *walk_state) { - acpi_status status; char *pathname = NULL; u8 enabled; @@ -277,26 +267,14 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node, pathname = acpi_ns_get_normalized_pathname(method_node, TRUE); } - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit_path; - } - enabled = acpi_ex_interpreter_trace_enabled(NULL); - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - if (enabled) { ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE, obj_desc ? obj_desc->method.aml_start : NULL, pathname); } - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - goto exit_path; - } - /* Check whether the tracer should be stopped */ if (acpi_gbl_trace_method_object == obj_desc) { @@ -312,9 +290,6 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node, acpi_gbl_trace_method_object = NULL; } - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - -exit_path: if (pathname) { ACPI_FREE(pathname); } diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index 425f133..a8b857a 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -94,6 +94,10 @@ void acpi_ex_enter_interpreter(void) ACPI_ERROR((AE_INFO, "Could not acquire AML Interpreter mutex")); } + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Could not acquire AML Namespace mutex")); + } return_VOID; } @@ -127,6 +131,10 @@ void acpi_ex_exit_interpreter(void) ACPI_FUNCTION_TRACE(ex_exit_interpreter); + status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Could not release AML Namespace mutex")); + } status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index 2daa9a09..334d3c5 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -46,6 +46,7 @@ #include "acnamesp.h" #include "acdispat.h" #include "actables.h" +#include "acinterp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsload") @@ -78,20 +79,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node) ACPI_FUNCTION_TRACE(ns_load_table); - /* - * Parse the table and load the namespace with all named - * objects found within. Control methods are NOT parsed - * at this time. In fact, the control methods cannot be - * parsed until the entire namespace is loaded, because - * if a control method makes a forward reference (call) - * to another control method, we can't continue parsing - * because we don't know how many arguments to parse next! - */ - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - /* If table already loaded into namespace, just return */ if (acpi_tb_is_table_loaded(table_index)) { @@ -107,6 +94,15 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node) goto unlock; } + /* + * Parse the table and load the namespace with all named + * objects found within. Control methods are NOT parsed + * at this time. In fact, the control methods cannot be + * parsed until the entire namespace is loaded, because + * if a control method makes a forward reference (call) + * to another control method, we can't continue parsing + * because we don't know how many arguments to parse next! + */ status = acpi_ns_parse_table(table_index, node); if (ACPI_SUCCESS(status)) { acpi_tb_set_table_loaded_flag(table_index, TRUE); @@ -120,7 +116,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node) * exist. This target of Scope must already exist in the * namespace, as per the ACPI specification. */ - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); acpi_ns_delete_namespace_by_owner(acpi_gbl_root_table_list. tables[table_index].owner_id); @@ -129,8 +124,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node) } unlock: - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index e51012b..4f14e92 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -47,6 +47,7 @@ #include "acparser.h" #include "acdispat.h" #include "actables.h" +#include "acinterp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsparse") @@ -234,7 +235,9 @@ acpi_ns_one_complete_parse(u32 pass_number, ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "*PARSE* pass %u parse\n", pass_number)); + acpi_ex_enter_interpreter(); status = acpi_ps_parse_aml(walk_state); + acpi_ex_exit_interpreter(); cleanup: acpi_ps_delete_parse_tree(parse_root); diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 22a52c28..8d7e5b5 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -308,7 +308,9 @@ acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info) /* * Parse the AML, walk_state will be deleted by parse_aml */ + acpi_ex_enter_interpreter(); status = acpi_ps_parse_aml(walk_state); + acpi_ex_exit_interpreter(); walk_state = NULL; cleanup: diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c index c986ec6..433d822 100644 --- a/drivers/acpi/acpica/utaddress.c +++ b/drivers/acpi/acpica/utaddress.c @@ -77,7 +77,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id, u32 length, struct acpi_namespace_node *region_node) { struct acpi_address_range *range_info; - acpi_status status; ACPI_FUNCTION_TRACE(ut_add_address_range); @@ -97,12 +96,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id, range_info->end_address = (address + length - 1); range_info->region_node = region_node; - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - ACPI_FREE(range_info); - return_ACPI_STATUS(status); - } - range_info->next = acpi_gbl_address_range_list[space_id]; acpi_gbl_address_range_list[space_id] = range_info; @@ -112,7 +105,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id, ACPI_FORMAT_UINT64(address), ACPI_FORMAT_UINT64(range_info->end_address))); - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(AE_OK); } -- cgit v0.10.2 From 441ad11d078f35093ceaf510742df423c2d89a3b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:07:16 +0800 Subject: ACPICA: Dispatcher: Fix a mutex issue for method auto serialization ACPICA commit fd305eda14f1a1e684edef4fac53f194bf00ed3f This patch fixes an issue with acpi_ds_auto_serialized_method(). The parser will invoke acpi_ex_release_all_mutexes(), which in return cause mutexes held in ACPI_ERROR_METHOD() failed. Lv Zheng. Link: https://bugs.acpica.org/show_bug.cgi?id=1324 Link: https://github.com/acpica/acpica/commit/fd305eda Tested-by: Mika Westerberg Tested-by: Greg White Tested-by: Dutch Guy Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 25b387a..32e9ddc 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -99,11 +99,14 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, "Method auto-serialization parse [%4.4s] %p\n", acpi_ut_get_node_name(node), node)); + acpi_ex_enter_interpreter(); + /* Create/Init a root op for the method parse tree */ op = acpi_ps_alloc_op(AML_METHOD_OP, obj_desc->method.aml_start); if (!op) { - return_ACPI_STATUS(AE_NO_MEMORY); + status = AE_NO_MEMORY; + goto unlock; } acpi_ps_set_name(op, node->name.integer); @@ -115,7 +118,8 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL); if (!walk_state) { acpi_ps_free_op(op); - return_ACPI_STATUS(AE_NO_MEMORY); + status = AE_NO_MEMORY; + goto unlock; } status = acpi_ds_init_aml_walk(walk_state, op, node, @@ -134,6 +138,8 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, status = acpi_ps_parse_aml(walk_state); acpi_ps_delete_parse_tree(op); +unlock: + acpi_ex_exit_interpreter(); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 3aa9162..1ce26d9 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -537,9 +537,11 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state) /* Either the method parse or actual execution failed */ + acpi_ex_exit_interpreter(); ACPI_ERROR_METHOD("Method parse/execution failed", walk_state->method_node, NULL, status); + acpi_ex_enter_interpreter(); /* Check for possible multi-thread reentrancy problem */ -- cgit v0.10.2 From ac0f06ebb815dabe42f2b2886ee9f879a2170ce4 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 7 Sep 2016 14:07:24 +0800 Subject: ACPICA: Tables: Tune table mutex to be a leaf lock ACPICA commit f564d57c6501b97a2871f0b4c048e79910f71783 This patch tunes MTX_TABLES into a leaf lock by always ensuring it is released before holding other locks. This patch also collects all table loading related functions into acpi_tb_load_table() (invoked by load_table opcode) and acpi_tb_install_and_load_table() (invoked by Load opcode and acpi_load_table()) so that we can have lock tuning code collected at the boundary of these 2 functions. Lv Zheng. Link: https://github.com/acpica/acpica/commit/f564d57c Tested-by: Mika Westerberg Tested-by: Dutch Guy Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 9469cd4..e85953b 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -123,6 +123,14 @@ acpi_tb_install_standard_table(acpi_physical_address address, void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc); +acpi_status +acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node); + +acpi_status +acpi_tb_install_and_load_table(struct acpi_table_header *table, + acpi_physical_address address, + u8 flags, u8 override, u32 *table_index); + void acpi_tb_terminate(void); acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index); diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 578d5c8..421836a 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -55,9 +55,7 @@ ACPI_MODULE_NAME("exconfig") /* Local prototypes */ static acpi_status -acpi_ex_add_table(u32 table_index, - struct acpi_namespace_node *parent_node, - union acpi_operand_object **ddb_handle); +acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle); static acpi_status acpi_ex_region_read(union acpi_operand_object *obj_desc, @@ -79,13 +77,9 @@ acpi_ex_region_read(union acpi_operand_object *obj_desc, ******************************************************************************/ static acpi_status -acpi_ex_add_table(u32 table_index, - struct acpi_namespace_node *parent_node, - union acpi_operand_object **ddb_handle) +acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle) { union acpi_operand_object *obj_desc; - acpi_status status; - acpi_owner_id owner_id; ACPI_FUNCTION_TRACE(ex_add_table); @@ -100,40 +94,8 @@ acpi_ex_add_table(u32 table_index, obj_desc->common.flags |= AOPOBJ_DATA_VALID; obj_desc->reference.class = ACPI_REFCLASS_TABLE; - *ddb_handle = obj_desc; - - /* Install the new table into the local data structures */ - obj_desc->reference.value = table_index; - - /* Add the table to the namespace */ - - status = acpi_ns_load_table(table_index, parent_node); - if (ACPI_FAILURE(status)) { - acpi_ut_remove_reference(obj_desc); - *ddb_handle = NULL; - return_ACPI_STATUS(status); - } - - /* Execute any module-level code that was found in the table */ - - acpi_ex_exit_interpreter(); - if (!acpi_gbl_parse_table_as_term_list - && acpi_gbl_group_module_level_code) { - acpi_ns_exec_module_code_list(); - } - acpi_ex_enter_interpreter(); - - /* - * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is - * responsible for discovering any new wake GPEs by running _PRW methods - * that may have been loaded by this table. - */ - status = acpi_tb_get_owner_id(table_index, &owner_id); - if (ACPI_SUCCESS(status)) { - acpi_ev_update_gpes(owner_id); - } - + *ddb_handle = obj_desc; return_ACPI_STATUS(AE_OK); } @@ -160,16 +122,17 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, struct acpi_namespace_node *start_node; struct acpi_namespace_node *parameter_node = NULL; union acpi_operand_object *ddb_handle; - struct acpi_table_header *table; u32 table_index; ACPI_FUNCTION_TRACE(ex_load_table_op); /* Find the ACPI table in the RSDT/XSDT */ + acpi_ex_exit_interpreter(); status = acpi_tb_find_table(operand[0]->string.pointer, operand[1]->string.pointer, operand[2]->string.pointer, &table_index); + acpi_ex_enter_interpreter(); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) { return_ACPI_STATUS(status); @@ -232,7 +195,15 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, /* Load the table into the namespace */ - status = acpi_ex_add_table(table_index, parent_node, &ddb_handle); + ACPI_INFO(("Dynamic OEM Table Load:")); + acpi_ex_exit_interpreter(); + status = acpi_tb_load_table(table_index, parent_node); + acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ex_add_table(table_index, &ddb_handle); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -255,19 +226,6 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state, } } - status = acpi_get_table_by_index(table_index, &table); - if (ACPI_SUCCESS(status)) { - ACPI_INFO(("Dynamic OEM Table Load:")); - acpi_tb_print_table_header(0, table); - } - - /* Invoke table handler if present */ - - if (acpi_gbl_table_handler) { - (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, - acpi_gbl_table_handler_context); - } - *return_desc = ddb_handle; return_ACPI_STATUS(status); } @@ -478,13 +436,12 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, /* Install the new table into the local data structures */ ACPI_INFO(("Dynamic OEM Table Load:")); - (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - - status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, - TRUE, TRUE, &table_index); - - (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + acpi_ex_exit_interpreter(); + status = + acpi_tb_install_and_load_table(table, ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, + TRUE, &table_index); + acpi_ex_enter_interpreter(); if (ACPI_FAILURE(status)) { /* Delete allocated table buffer */ @@ -494,25 +451,13 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, } /* - * Note: Now table is "INSTALLED", it must be validated before - * loading. - */ - status = - acpi_tb_validate_table(&acpi_gbl_root_table_list. - tables[table_index]); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* * Add the table to the namespace. * * Note: Load the table objects relative to the root of the namespace. * This appears to go against the ACPI specification, but we do it for * compatibility with other ACPI implementations. */ - status = - acpi_ex_add_table(table_index, acpi_gbl_root_node, &ddb_handle); + status = acpi_ex_add_table(table_index, &ddb_handle); if (ACPI_FAILURE(status)) { /* On error, table_ptr was deallocated above */ @@ -535,14 +480,6 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, /* Remove the reference by added by acpi_ex_store above */ acpi_ut_remove_reference(ddb_handle); - - /* Invoke table handler if present */ - - if (acpi_gbl_table_handler) { - (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, - acpi_gbl_table_handler_context); - } - return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c index 1388a19..7e93fd6 100644 --- a/drivers/acpi/acpica/tbdata.c +++ b/drivers/acpi/acpica/tbdata.c @@ -45,6 +45,7 @@ #include "accommon.h" #include "acnamesp.h" #include "actables.h" +#include "acevents.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbdata") @@ -771,3 +772,142 @@ void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded) (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); } + +/******************************************************************************* + * + * FUNCTION: acpi_tb_load_table + * + * PARAMETERS: table_index - Table index + * parent_node - Where table index is returned + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table + * + ******************************************************************************/ + +acpi_status +acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node) +{ + struct acpi_table_header *table; + acpi_status status; + acpi_owner_id owner_id; + + ACPI_FUNCTION_TRACE(tb_load_table); + + /* + * Note: Now table is "INSTALLED", it must be validated before + * using. + */ + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ns_load_table(table_index, parent_node); + + /* Execute any module-level code that was found in the table */ + + if (!acpi_gbl_parse_table_as_term_list + && acpi_gbl_group_module_level_code) { + acpi_ns_exec_module_code_list(); + } + + /* + * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is + * responsible for discovering any new wake GPEs by running _PRW methods + * that may have been loaded by this table. + */ + status = acpi_tb_get_owner_id(table_index, &owner_id); + if (ACPI_SUCCESS(status)) { + acpi_ev_update_gpes(owner_id); + } + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, + acpi_gbl_table_handler_context); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_install_and_load_table + * + * PARAMETERS: table - Pointer to the table + * address - Physical address of the table + * flags - Allocation flags of the table + * table_index - Where table index is returned + * + * RETURN: Status + * + * DESCRIPTION: Install and load an ACPI table + * + ******************************************************************************/ + +acpi_status +acpi_tb_install_and_load_table(struct acpi_table_header *table, + acpi_physical_address address, + u8 flags, u8 override, u32 *table_index) +{ + acpi_status status; + u32 i; + acpi_owner_id owner_id; + + ACPI_FUNCTION_TRACE(acpi_load_table); + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + + /* Install the table and load it into the namespace */ + + status = acpi_tb_install_standard_table(address, flags, TRUE, + override, &i); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* + * Note: Now table is "INSTALLED", it must be validated before + * using. + */ + status = acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + status = acpi_ns_load_table(i, acpi_gbl_root_node); + + /* Execute any module-level code that was found in the table */ + + if (!acpi_gbl_parse_table_as_term_list + && acpi_gbl_group_module_level_code) { + acpi_ns_exec_module_code_list(); + } + + /* + * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is + * responsible for discovering any new wake GPEs by running _PRW methods + * that may have been loaded by this table. + */ + status = acpi_tb_get_owner_id(i, &owner_id); + if (ACPI_SUCCESS(status)) { + acpi_ev_update_gpes(owner_id); + } + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, + acpi_gbl_table_handler_context); + } + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + +unlock_and_exit: + *table_index = i; + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(status); +} diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index e348d61..a3f7b37 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -68,7 +68,7 @@ acpi_status acpi_tb_find_table(char *signature, char *oem_id, char *oem_table_id, u32 *table_index) { - acpi_status status; + acpi_status status = AE_OK; struct acpi_table_header header; u32 i; @@ -96,6 +96,7 @@ acpi_tb_find_table(char *signature, /* Search for the table */ + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { if (memcmp(&(acpi_gbl_root_table_list.tables[i].signature), header.signature, ACPI_NAME_SIZE)) { @@ -115,7 +116,7 @@ acpi_tb_find_table(char *signature, acpi_tb_validate_table(&acpi_gbl_root_table_list. tables[i]); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + goto unlock_and_exit; } if (!acpi_gbl_root_table_list.tables[i].pointer) { @@ -144,9 +145,12 @@ acpi_tb_find_table(char *signature, ACPI_DEBUG_PRINT((ACPI_DB_TABLES, "Found table [%4.4s]\n", header.signature)); - return_ACPI_STATUS(AE_OK); + goto unlock_and_exit; } } + status = AE_NOT_FOUND; +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); return_ACPI_STATUS(AE_NOT_FOUND); } diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 6088712..870ad64 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -189,11 +189,11 @@ acpi_status acpi_tb_load_namespace(void) memcpy(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT, sizeof(struct acpi_table_header)); - (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); - /* Load and parse tables */ + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); status = acpi_ns_load_table(acpi_gbl_dsdt_index, acpi_gbl_root_node); + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "[DSDT] table load failed")); tables_failed++; @@ -203,7 +203,6 @@ acpi_status acpi_tb_load_namespace(void) /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */ - (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { table = &acpi_gbl_root_table_list.tables[i]; @@ -221,6 +220,7 @@ acpi_status acpi_tb_load_namespace(void) (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); status = acpi_ns_load_table(i, acpi_gbl_root_node); + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "(%4.4s:%8.8s) while loading table", @@ -236,8 +236,6 @@ acpi_status acpi_tb_load_namespace(void) } else { tables_loaded++; } - - (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); } if (!tables_failed) { @@ -325,49 +323,13 @@ acpi_status acpi_load_table(struct acpi_table_header *table) return_ACPI_STATUS(AE_BAD_PARAMETER); } - /* Must acquire the interpreter lock during this operation */ - - status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - /* Install the table and load it into the namespace */ ACPI_INFO(("Host-directed Dynamic ACPI Table Load:")); - (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); - - status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), - ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL, - TRUE, FALSE, &table_index); - - (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - - /* - * Note: Now table is "INSTALLED", it must be validated before - * using. - */ status = - acpi_tb_validate_table(&acpi_gbl_root_table_list. - tables[table_index]); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - - status = acpi_ns_load_table(table_index, acpi_gbl_root_node); - - /* Invoke table handler if present */ - - if (acpi_gbl_table_handler) { - (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, - acpi_gbl_table_handler_context); - } - -unlock_and_exit: - (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + acpi_tb_install_and_load_table(table, ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL, + FALSE, &table_index); return_ACPI_STATUS(status); } -- cgit v0.10.2 From cb7d668fa4f9e9dcc772bd84f13f978a5e8791be Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 7 Sep 2016 14:07:33 +0800 Subject: ACPICA: Update version to 20160831 ACPICA commit 3c8c04c2e8a371018b3a29f5cfe27a241caea48d Version 20160831 Link: https://github.com/acpica/acpica/commit/3c8c04c2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index cf78ec2..c7b3a13 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20160729 +#define ACPI_CA_VERSION 0x20160831 #include #include -- cgit v0.10.2 From 10b68700add43d0c38fedefb7a2b7df931f8e84e Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Mon, 5 Sep 2016 15:12:38 +0100 Subject: ACPI / PCI: fix GIC irq model default PCI IRQ polarity On ACPI ARM based systems the GIC interrupt controller and corresponding interrupt model permit only the high polarity for level interrupts. ACPI firmware describes PCI legacy IRQs through entries in the _PRT objects. Entries in the _PRT can be of two types: - Static: not configurable, trigger/polarity default to level-low, _PRT entry defines the global GSI interrupt number - Configurable: _PRT interrupt entry contains a reference to the corresponding PCI interrupt link device (that in turn provides the interrupt descriptor through its _CRS/_PRS methods) Configurable IRQ entries are not currently allowed by the ACPI specification on ARM since they can only be used for interrupt pins that are routable, as per ACPI specifications (version 6.1, 6.2.13): "[...] There are two ways that _PRT can be used. Typically, the interrupt input that a given PCI interrupt is on is configurable. For example, a given PCI interrupt might be configured for either IRQ 10 or 11 on an 8259 interrupt controller. In this model, each interrupt is represented in the ACPI namespace as a PCI Interrupt Link Device. [...]" ARM platforms GIC configurations do not allow dynamic IRQ routing, since routing is statically laid out at synthesis time; therefore PCI interrupt links cannot be used for PCI legacy IRQ descriptions in the _PRT on ARM systems. On the other hand, current core ACPI code handling PCI legacy IRQs consider IRQ trigger/polarity for static _PRT entries as level-low. On ARM systems with a GIC interrupt controller and corresponding ACPI interrupt model this does not work in that GIC interrupt controller is only capable of handling level interrupts whose polarity is high (for PCI legacy IRQs - that are level-low by specification - this means that the legacy IRQs are inverted before reaching the interrupt controller pin), resulting in IRQ allocation failures such as: genirq: Setting trigger mode 8 for irq 18 failed (gic_set_type+0x0/0x48) Change the default polarity for PCI legacy IRQs to high on systems booting wth ACPI on platforms with a GIC interrupt controller model, fixing the discrepancy between specification and HW behaviour. Signed-off-by: Lorenzo Pieralisi Acked-by: Marc Zyngier Tested-by: Duc Dang Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 2c45dd3..c576a6f 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -411,7 +411,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev) int gsi; u8 pin; int triggering = ACPI_LEVEL_SENSITIVE; - int polarity = ACPI_ACTIVE_LOW; + /* + * On ARM systems with the GIC interrupt model, level interrupts + * are always polarity high by specification; PCI legacy + * IRQs lines are inverted before reaching the interrupt + * controller and must therefore be considered active high + * as default. + */ + int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ? + ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW; char *link = NULL; char link_desc[16]; int rc; -- cgit v0.10.2 From 0f61aaa413989e7838178f5ae77c9c4ad8507013 Mon Sep 17 00:00:00 2001 From: Al Stone Date: Fri, 19 Aug 2016 17:24:01 -0600 Subject: x86: ACPI: remove extraneous white space after semicolon Signed-off-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 90d84c3..5fb8f05 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1513,7 +1513,7 @@ void __init acpi_boot_table_init(void) * If acpi_disabled, bail out */ if (acpi_disabled) - return; + return; /* * Initialize the ACPI boot-time table parser. -- cgit v0.10.2 From c12f29a5d44d4197162d7fd1cc5dc57d8cfffcf8 Mon Sep 17 00:00:00 2001 From: Al Stone Date: Fri, 19 Aug 2016 17:24:02 -0600 Subject: x86: ACPI: make variable names clearer in acpi_parse_madt_lapic_entries() This patch has no functional change; it is purely cosmetic, though it does make it a wee bit easier to understand the code. Before, the count of LAPICs was being stored in the variable 'x2count' and the count of X2APICs was being stored in the variable 'count'. This patch swaps that so that the routine acpi_parse_madt_lapic_entries() will now consistently use x2count to refer to X2APIC info, and count to refer to LAPIC info. Signed-off-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 5fb8f05..ccd27fe 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1031,8 +1031,8 @@ static int __init acpi_parse_madt_lapic_entries(void) return ret; } - x2count = madt_proc[0].count; - count = madt_proc[1].count; + count = madt_proc[0].count; + x2count = madt_proc[1].count; } if (!count && !x2count) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); -- cgit v0.10.2 From a217726a7df146fcfea6f37cbbc80849083260b6 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Sep 2016 15:06:06 +0200 Subject: ACPI / APD: constify local structures For structure types defined in the same file or local header files, find top-level static structure declarations that have the following properties: 1. Never reassigned. 2. Address never taken 3. Not passed to a top-level macro call 4. No pointer or array-typed field passed to a function or stored in a variable. Declare structures having all of these properties as const. Done using Coccinelle. Based on a suggestion by Joe Perches . Signed-off-by: Julia Lawall Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 1f30fff..d58fbf7 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -72,7 +72,7 @@ static int acpi_apd_setup(struct apd_private_data *pdata) } #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE -static struct apd_device_desc cz_i2c_desc = { +static const struct apd_device_desc cz_i2c_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 133000000, }; @@ -84,7 +84,7 @@ static struct property_entry uart_properties[] = { { }, }; -static struct apd_device_desc cz_uart_desc = { +static const struct apd_device_desc cz_uart_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 48000000, .properties = uart_properties, @@ -92,12 +92,12 @@ static struct apd_device_desc cz_uart_desc = { #endif #ifdef CONFIG_ARM64 -static struct apd_device_desc xgene_i2c_desc = { +static const struct apd_device_desc xgene_i2c_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 100000000, }; -static struct apd_device_desc vulcan_spi_desc = { +static const struct apd_device_desc vulcan_spi_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 133000000, }; -- cgit v0.10.2 From ffcbed8418f8d850f27372d5f9b69aa05fd12217 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 8 Sep 2016 15:55:41 +0000 Subject: ACPI / tables: Remove duplicated include from tables.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 4dfbf49..cdd56c4 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "internal.h" #ifdef CONFIG_ACPI_CUSTOM_DSDT -- cgit v0.10.2 From 86ec64bc382ad1e0e6bc66f5f705a53e299a3445 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 13 Sep 2016 17:48:21 +0800 Subject: ACPICA: Tables: Fix a regression in acpi_tb_find_table() In the following commit, the return value of acpi_tb_find_table() is incorrect: commit ac0f06ebb815dabe42f2b2886ee9f879a2170ce4 Author: Lv Zheng Date: Wed Sep 7 14:07:24 2016 +0800 ACPICA: Tables: Tune table mutex to be a leaf lock ACPICA commit f564d57c6501b97a2871f0b4c048e79910f71783 This causes LoadTable opcode to fail. Fix this mistake. Signed-off-by: Lv Zheng [ rjw: Changelog ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index a3f7b37..f6b9b4e 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -152,5 +152,5 @@ acpi_tb_find_table(char *signature, unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); - return_ACPI_STATUS(AE_NOT_FOUND); + return_ACPI_STATUS(status); } -- cgit v0.10.2 From 307ecb0aa3a24528efd1cfcea8215b2d82df6e10 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 13 Sep 2016 17:48:27 +0800 Subject: ACPI / sysfs: Fix an issue for LoadTable opcode OEM tables can be installed via RSDT/XSDT, in this case, they have already been created under /sys/firmware/acpi/tables. For this kind of tables, normally LoadTable opcode will be executed to load them. If LoadTable opcode is executed after acpi_sysfs_init(), acpi_sysfs_table_handler() will be invoked, thus a redundant table file will be created under /sys/firmware/acpi/tables/dynamic. Then running "acpidump" on such platform results in an error, complaining blank empty table (see Link 1 below). The bug can be reproduced by customizing an OEM1 table, allowing it to be overridden via 'table_sigs' (drivers/acpi/tables.c), adding the following code to the customized DSDT to load it: Name (OEMH, Zero) Name (OEMF, One) If (LEqual (OEMF, One)) { Store (LoadTable ("OEM1", "Intel", "Test"), OEMH) Store (Zero, OEMF) } In order to make sure that the OEM1 table is installed after acpi_sysfs_init(), acpi_sysfs_init() can be moved before invoking acpi_load_tables(). Then the following command execution result can be seen: # acpidump > acpidump.txt Could not read table header: /sysfs/firmware/acpi/tables/dynamic/OEM12 Could not get ACPI table at index 17, AE_BAD_HEADER Link: https://bugzilla.kernel.org/show_bug.cgi?id=150841 # [1] Link: https://github.com/acpica/acpica/commit/ed6a5fbc Reported-by: Jason Voelz Reported-by: Francisco Leoner Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 703c993..c88d4bd 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -383,7 +383,7 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) struct acpi_table_attr *table_attr; switch (event) { - case ACPI_TABLE_EVENT_LOAD: + case ACPI_TABLE_EVENT_INSTALL: table_attr = kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); if (!table_attr) @@ -397,7 +397,9 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) } else list_add_tail(&table_attr->node, &acpi_table_attr_list); break; + case ACPI_TABLE_EVENT_LOAD: case ACPI_TABLE_EVENT_UNLOAD: + case ACPI_TABLE_EVENT_UNINSTALL: /* * we do not need to do anything right now * because the table is not deleted from the -- cgit v0.10.2 From 914c619409ea509f335add941ad515fb590ee6c5 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 13 Sep 2016 17:48:34 +0800 Subject: ACPI / sysfs: Update sysfs signature handling code This patch cleans up sysfs table signature handling code: 1. Convert the signature handling code to use the ACPICA APIs to benefit from the future improvements of the APIs. 2. Add 'filename' attribute in order to handle both BE/LE name tags. 3. Add instance check in order to avoid the possible buffer overflow related to the table file name. Signed-off-by: Lv Zheng [ rjw: Changelog ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index c88d4bd..703c26e 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -314,10 +314,14 @@ static struct kobject *tables_kobj; static struct kobject *dynamic_tables_kobj; static struct kobject *hotplug_kobj; +#define ACPI_MAX_TABLE_INSTANCES 999 +#define ACPI_INST_SIZE 4 /* including trailing 0 */ + struct acpi_table_attr { struct bin_attribute attr; - char name[8]; + char name[ACPI_NAME_SIZE]; int instance; + char filename[ACPI_NAME_SIZE+ACPI_INST_SIZE]; struct list_head node; }; @@ -329,14 +333,9 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, container_of(bin_attr, struct acpi_table_attr, attr); struct acpi_table_header *table_header = NULL; acpi_status status; - char name[ACPI_NAME_SIZE]; - if (strncmp(table_attr->name, "NULL", 4)) - memcpy(name, table_attr->name, ACPI_NAME_SIZE); - else - memcpy(name, "\0\0\0\0", 4); - - status = acpi_get_table(name, table_attr->instance, &table_header); + status = acpi_get_table(table_attr->name, table_attr->instance, + &table_header); if (ACPI_FAILURE(status)) return -ENODEV; @@ -344,38 +343,45 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, table_header, table_header->length); } -static void acpi_table_attr_init(struct acpi_table_attr *table_attr, - struct acpi_table_header *table_header) +static int acpi_table_attr_init(struct kobject *tables_obj, + struct acpi_table_attr *table_attr, + struct acpi_table_header *table_header) { struct acpi_table_header *header = NULL; struct acpi_table_attr *attr = NULL; + char instance_str[ACPI_INST_SIZE]; sysfs_attr_init(&table_attr->attr.attr); - if (table_header->signature[0] != '\0') - memcpy(table_attr->name, table_header->signature, - ACPI_NAME_SIZE); - else - memcpy(table_attr->name, "NULL", 4); + ACPI_MOVE_NAME(table_attr->name, table_header->signature); list_for_each_entry(attr, &acpi_table_attr_list, node) { - if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE)) + if (ACPI_COMPARE_NAME(table_attr->name, attr->name)) if (table_attr->instance < attr->instance) table_attr->instance = attr->instance; } table_attr->instance++; + if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) { + pr_warn("%4.4s: too many table instances\n", + table_attr->name); + return -ERANGE; + } + ACPI_MOVE_NAME(table_attr->filename, table_header->signature); + table_attr->filename[ACPI_NAME_SIZE] = '\0'; if (table_attr->instance > 1 || (table_attr->instance == 1 && !acpi_get_table - (table_header->signature, 2, &header))) - sprintf(table_attr->name + ACPI_NAME_SIZE, "%d", - table_attr->instance); + (table_header->signature, 2, &header))) { + snprintf(instance_str, sizeof(instance_str), "%u", + table_attr->instance); + strcat(table_attr->filename, instance_str); + } table_attr->attr.size = table_header->length; table_attr->attr.read = acpi_table_show; - table_attr->attr.attr.name = table_attr->name; + table_attr->attr.attr.name = table_attr->filename; table_attr->attr.attr.mode = 0400; - return; + return sysfs_create_bin_file(tables_obj, &table_attr->attr); } acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) @@ -389,13 +395,12 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) if (!table_attr) return AE_NO_MEMORY; - acpi_table_attr_init(table_attr, table); - if (sysfs_create_bin_file(dynamic_tables_kobj, - &table_attr->attr)) { + if (acpi_table_attr_init(dynamic_tables_kobj, + table_attr, table)) { kfree(table_attr); return AE_ERROR; - } else - list_add_tail(&table_attr->node, &acpi_table_attr_list); + } + list_add_tail(&table_attr->node, &acpi_table_attr_list); break; case ACPI_TABLE_EVENT_LOAD: case ACPI_TABLE_EVENT_UNLOAD: @@ -437,13 +442,12 @@ static int acpi_tables_sysfs_init(void) if (ACPI_FAILURE(status)) continue; - table_attr = NULL; table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL); if (!table_attr) return -ENOMEM; - acpi_table_attr_init(table_attr, table_header); - ret = sysfs_create_bin_file(tables_kobj, &table_attr->attr); + ret = acpi_table_attr_init(tables_kobj, + table_attr, table_header); if (ret) { kfree(table_attr); return ret; -- cgit v0.10.2 From b59c4b3dcd2e63d7a5483df3460d47278df716f7 Mon Sep 17 00:00:00 2001 From: Hoan Tran Date: Wed, 14 Sep 2016 10:54:58 -0700 Subject: ACPI / CPPC: Support PCC with interrupt flag For PCC mailbox with interrupt flag, CPPC should call mbox_chan_txdone() function to notify the mailbox framework about TX completion. Signed-off-by: Hoan Tran Reviewed-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 3d1ae6d..d0d0504 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -311,7 +311,10 @@ static int send_pcc_cmd(u16 cmd) if (pcc_data.pcc_mrtt) last_cmd_cmpl_time = ktime_get(); - mbox_client_txdone(pcc_data.pcc_channel, ret); + if (pcc_data.pcc_channel->mbox->txdone_irq) + mbox_chan_txdone(pcc_data.pcc_channel, ret); + else + mbox_client_txdone(pcc_data.pcc_channel, ret); end: if (cmd == CMD_WRITE) { -- cgit v0.10.2 From afd29f9017a271fb048b69275975c5451fd0e674 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Sep 2016 11:07:03 +0300 Subject: PCI: Add pci_find_resource() Add a new helper function pci_find_resource() that can be used to find out whether a given resource (for example from a child device) is contained within given PCI device's standard resources. Signed-off-by: Mika Westerberg Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index aab9d51..415956c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -480,6 +480,30 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev, EXPORT_SYMBOL(pci_find_parent_resource); /** + * pci_find_resource - Return matching PCI device resource + * @dev: PCI device to query + * @res: Resource to look for + * + * Goes over standard PCI resources (BARs) and checks if the given resource + * is partially or fully contained in any of them. In that case the + * matching resource is returned, %NULL otherwise. + */ +struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res) +{ + int i; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + struct resource *r = &dev->resource[i]; + + if (r->start && resource_contains(r, res)) + return r; + } + + return NULL; +} +EXPORT_SYMBOL(pci_find_resource); + +/** * pci_find_pcie_root_port - return PCIe Root Port * @dev: PCI device to query * diff --git a/include/linux/pci.h b/include/linux/pci.h index 0ab8359..a917d4b2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1126,6 +1126,7 @@ void pdev_enable_device(struct pci_dev *); int pci_enable_resources(struct pci_dev *, int mask); void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), int (*)(const struct pci_dev *, u8, u8)); +struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res); #define HAVE_PCI_REQ_REGIONS 2 int __must_check pci_request_regions(struct pci_dev *, const char *); int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *); @@ -1542,6 +1543,9 @@ static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { return 0; } +static inline struct resource *pci_find_resource(struct pci_dev *dev, + struct resource *res) +{ return NULL; } static inline int pci_request_regions(struct pci_dev *dev, const char *res_name) { return -EIO; } static inline void pci_release_regions(struct pci_dev *dev) { } -- cgit v0.10.2 From a252d881c5585f5a25d4beeca23806b4fea116eb Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Sep 2016 11:07:04 +0300 Subject: ACPI / platform: Pay attention to parent device's resources Given following simplified device hierarchy: // PCI device having BAR0 (RMEM) split between 4 GPIO devices. Device (P2S) { Name (_ADR, 0x000d0000) Device (GPO0) { Name (_HID, "INT3452") Name (_UID, 1) Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadWrite, 0, 0x4000, RMEM + 0x0000) }) } Device (GPO1) { Name (_HID, "INT3452") Name (_UID, 2) Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadWrite, 0, 0x4000, RMEM + 0x4000) }) } Device (GPO2) { Name (_HID, "INT3452") Name (_UID, 3) Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadWrite, 0, 0x4000, RMEM + 0x8000) }) } Device (GPO3) { Name (_HID, "INT3452") Name (_UID, 4) Name (_CRS, ResourceTemplate () { Memory32Fixed (ReadWrite, 0, 0x4000, RMEM + 0xc000) }) } } The current ACPI platform enumeration code allocates resources from the global MMIO resource pool (/proc/iomem) for all the four GPIO devices. After this PCI core calls pcibios_resource_survey() to allocate resources for all PCI devices including the parent device for these GPIO devices (P2S). Since that resource range has already been reserved the allocation fails. The reason for this is that we never bother with parent device's resources when ACPI platform devices are created. Fix this by checking whether there is a parent device and in that case make sure we assign correct parent resource to the resources for the child ACPI platform device. Currently we only deal with parent devices if they are PCI devices but we may expand this later to cover other bus types as well. Reported-by: Aaron Durbin Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 159f7f1..b200ae1 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "internal.h" @@ -30,6 +31,22 @@ static const struct acpi_device_id forbidden_id_list[] = { {"", 0}, }; +static void acpi_platform_fill_resource(struct acpi_device *adev, + const struct resource *src, struct resource *dest) +{ + struct device *parent; + + *dest = *src; + + /* + * If the device has parent we need to take its resources into + * account as well because this device might consume part of those. + */ + parent = acpi_get_first_physical_node(adev->parent); + if (parent && dev_is_pci(parent)) + dest->parent = pci_find_resource(to_pci_dev(parent), dest); +} + /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. @@ -70,7 +87,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) } count = 0; list_for_each_entry(rentry, &resource_list, node) - resources[count++] = *rentry->res; + acpi_platform_fill_resource(adev, rentry->res, + &resources[count++]); acpi_dev_free_resource_list(&resource_list); } -- cgit v0.10.2 From 058dfc7670086edda8d34f0dbe93c596db5d4a6b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 20 Sep 2016 15:30:51 +0300 Subject: ACPI / watchdog: Add support for WDAT hardware watchdog Starting from Intel Skylake the iTCO watchdog timer registers were moved to reside in the same register space with SMBus host controller. Not all needed registers are available though and we need to unhide P2SB (Primary to Sideband) device briefly to be able to read status of required NO_REBOOT bit. The i2c-i801.c SMBus driver used to handle this and creation of the iTCO watchdog platform device. Windows, on the other hand, does not use the iTCO watchdog hardware directly even if it is available. Instead it relies on ACPI Watchdog Action Table (WDAT) table to describe the watchdog hardware to the OS. This table contains necessary information about the the hardware and also set of actions which are executed by a driver as needed. This patch implements a new watchdog driver that takes advantage of the ACPI WDAT table. We split the functionality into two parts: first part enumerates the WDAT table and if found, populates resources and creates platform device for the actual driver. The second part is the driver itself. The reason for the split is that this way we can make the driver itself to be a module and loaded automatically if the WDAT table is found. Otherwise the module is not loaded. Signed-off-by: Mika Westerberg Reviewed-by: Guenter Roeck Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 445ce28..dcfa7e9 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -462,6 +462,9 @@ source "drivers/acpi/nfit/Kconfig" source "drivers/acpi/apei/Kconfig" source "drivers/acpi/dptf/Kconfig" +config ACPI_WATCHDOG + bool + config ACPI_EXTLOG tristate "Extended Error Log support" depends on X86_MCE && X86_LOCAL_APIC diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 5ae9d85..3a1fa8f 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -56,6 +56,7 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o acpi-y += acpi_lpat.o acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o +acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o # These are (potentially) separate modules diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c new file mode 100644 index 0000000..13caebd --- /dev/null +++ b/drivers/acpi/acpi_watchdog.c @@ -0,0 +1,123 @@ +/* + * ACPI watchdog table parsing support. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) "ACPI: watchdog: " fmt + +#include +#include +#include + +#include "internal.h" + +/** + * Returns true if this system should prefer ACPI based watchdog instead of + * the native one (which are typically the same hardware). + */ +bool acpi_has_watchdog(void) +{ + struct acpi_table_header hdr; + + if (acpi_disabled) + return false; + + return ACPI_SUCCESS(acpi_get_table_header(ACPI_SIG_WDAT, 0, &hdr)); +} +EXPORT_SYMBOL_GPL(acpi_has_watchdog); + +void __init acpi_watchdog_init(void) +{ + const struct acpi_wdat_entry *entries; + const struct acpi_table_wdat *wdat; + struct list_head resource_list; + struct resource_entry *rentry; + struct platform_device *pdev; + struct resource *resources; + size_t nresources = 0; + acpi_status status; + int i; + + status = acpi_get_table(ACPI_SIG_WDAT, 0, + (struct acpi_table_header **)&wdat); + if (ACPI_FAILURE(status)) { + /* It is fine if there is no WDAT */ + return; + } + + /* Watchdog disabled by BIOS */ + if (!(wdat->flags & ACPI_WDAT_ENABLED)) + return; + + /* Skip legacy PCI WDT devices */ + if (wdat->pci_segment != 0xff || wdat->pci_bus != 0xff || + wdat->pci_device != 0xff || wdat->pci_function != 0xff) + return; + + INIT_LIST_HEAD(&resource_list); + + entries = (struct acpi_wdat_entry *)(wdat + 1); + for (i = 0; i < wdat->entries; i++) { + const struct acpi_generic_address *gas; + struct resource_entry *rentry; + struct resource res; + bool found; + + gas = &entries[i].register_region; + + res.start = gas->address; + if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + res.flags = IORESOURCE_MEM; + res.end = res.start + ALIGN(gas->access_width, 4); + } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + res.flags = IORESOURCE_IO; + res.end = res.start + gas->access_width; + } else { + pr_warn("Unsupported address space: %u\n", + gas->space_id); + goto fail_free_resource_list; + } + + found = false; + resource_list_for_each_entry(rentry, &resource_list) { + if (resource_contains(rentry->res, &res)) { + found = true; + break; + } + } + + if (!found) { + rentry = resource_list_create_entry(NULL, 0); + if (!rentry) + goto fail_free_resource_list; + + *rentry->res = res; + resource_list_add_tail(rentry, &resource_list); + nresources++; + } + } + + resources = kcalloc(nresources, sizeof(*resources), GFP_KERNEL); + if (!resources) + goto fail_free_resource_list; + + i = 0; + resource_list_for_each_entry(rentry, &resource_list) + resources[i++] = *rentry->res; + + pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE, + resources, nresources); + if (IS_ERR(pdev)) + pr_err("Failed to create platform device\n"); + + kfree(resources); + +fail_free_resource_list: + resource_list_free(&resource_list); +} diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 940218f..fb9a7ad 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -225,4 +225,14 @@ static inline void suspend_nvs_restore(void) {} void acpi_init_properties(struct acpi_device *adev); void acpi_free_properties(struct acpi_device *adev); +/*-------------------------------------------------------------------------- + Watchdog + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_WATCHDOG +void acpi_watchdog_init(void); +#else +static inline void acpi_watchdog_init(void) {} +#endif + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e878fc7..3b85d87 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2002,6 +2002,7 @@ int __init acpi_scan_init(void) acpi_pnp_init(); acpi_int340x_thermal_init(); acpi_amba_init(); + acpi_watchdog_init(); acpi_scan_add_handler(&generic_device_handler); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1bffe00..50dbaa8 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -152,6 +152,19 @@ config TANGOX_WATCHDOG This driver can be built as a module. The module name is tangox_wdt. +config WDAT_WDT + tristate "ACPI Watchdog Action Table (WDAT)" + depends on ACPI + select ACPI_WATCHDOG + help + This driver adds support for systems with ACPI Watchdog Action + Table (WDAT) table. Servers typically have this but it can be + found on some desktop machines as well. This driver will take + over the native iTCO watchdog driver found on many Intel CPUs. + + To compile this driver as module, choose M here: the module will + be called wdat_wdt. + config WM831X_WATCHDOG tristate "WM831x watchdog" depends on MFD_WM831X diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c22ad3e..cba0043 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -202,6 +202,7 @@ obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o +obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c new file mode 100644 index 0000000..6b64645 --- /dev/null +++ b/drivers/watchdog/wdat_wdt.c @@ -0,0 +1,525 @@ +/* + * ACPI Hardware Watchdog (WDAT) driver. + * + * Copyright (C) 2016, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED + +/** + * struct wdat_instruction - Single ACPI WDAT instruction + * @entry: Copy of the ACPI table instruction + * @reg: Register the instruction is accessing + * @node: Next instruction in action sequence + */ +struct wdat_instruction { + struct acpi_wdat_entry entry; + void __iomem *reg; + struct list_head node; +}; + +/** + * struct wdat_wdt - ACPI WDAT watchdog device + * @pdev: Parent platform device + * @wdd: Watchdog core device + * @period: How long is one watchdog period in ms + * @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5 + * @stopped: Was the watchdog stopped by the driver in suspend + * @actions: An array of instruction lists indexed by an action number from + * the WDAT table. There can be %NULL entries for not implemented + * actions. + */ +struct wdat_wdt { + struct platform_device *pdev; + struct watchdog_device wdd; + unsigned int period; + bool stopped_in_sleep; + bool stopped; + struct list_head *instructions[MAX_WDAT_ACTIONS]; +}; + +#define to_wdat_wdt(wdd) container_of(wdd, struct wdat_wdt, wdd) + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int wdat_wdt_read(struct wdat_wdt *wdat, + const struct wdat_instruction *instr, u32 *value) +{ + const struct acpi_generic_address *gas = &instr->entry.register_region; + + switch (gas->access_width) { + case 1: + *value = ioread8(instr->reg); + break; + case 2: + *value = ioread16(instr->reg); + break; + case 3: + *value = ioread32(instr->reg); + break; + default: + return -EINVAL; + } + + dev_dbg(&wdat->pdev->dev, "Read %#x from 0x%08llx\n", *value, + gas->address); + + return 0; +} + +static int wdat_wdt_write(struct wdat_wdt *wdat, + const struct wdat_instruction *instr, u32 value) +{ + const struct acpi_generic_address *gas = &instr->entry.register_region; + + switch (gas->access_width) { + case 1: + iowrite8((u8)value, instr->reg); + break; + case 2: + iowrite16((u16)value, instr->reg); + break; + case 3: + iowrite32(value, instr->reg); + break; + default: + return -EINVAL; + } + + dev_dbg(&wdat->pdev->dev, "Wrote %#x to 0x%08llx\n", value, + gas->address); + + return 0; +} + +static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action, + u32 param, u32 *retval) +{ + struct wdat_instruction *instr; + + if (action >= ARRAY_SIZE(wdat->instructions)) + return -EINVAL; + + if (!wdat->instructions[action]) + return -EOPNOTSUPP; + + dev_dbg(&wdat->pdev->dev, "Running action %#x\n", action); + + /* Run each instruction sequentially */ + list_for_each_entry(instr, wdat->instructions[action], node) { + const struct acpi_wdat_entry *entry = &instr->entry; + const struct acpi_generic_address *gas; + u32 flags, value, mask, x, y; + bool preserve; + int ret; + + gas = &entry->register_region; + + preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER; + flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER; + value = entry->value; + mask = entry->mask; + + switch (flags) { + case ACPI_WDAT_READ_VALUE: + ret = wdat_wdt_read(wdat, instr, &x); + if (ret) + return ret; + x >>= gas->bit_offset; + x &= mask; + if (retval) + *retval = x == value; + break; + + case ACPI_WDAT_READ_COUNTDOWN: + ret = wdat_wdt_read(wdat, instr, &x); + if (ret) + return ret; + x >>= gas->bit_offset; + x &= mask; + if (retval) + *retval = x; + break; + + case ACPI_WDAT_WRITE_VALUE: + x = value & mask; + x <<= gas->bit_offset; + if (preserve) { + ret = wdat_wdt_read(wdat, instr, &y); + if (ret) + return ret; + y = y & ~(mask << gas->bit_offset); + x |= y; + } + ret = wdat_wdt_write(wdat, instr, x); + if (ret) + return ret; + break; + + case ACPI_WDAT_WRITE_COUNTDOWN: + x = param; + x &= mask; + x <<= gas->bit_offset; + if (preserve) { + ret = wdat_wdt_read(wdat, instr, &y); + if (ret) + return ret; + y = y & ~(mask << gas->bit_offset); + x |= y; + } + ret = wdat_wdt_write(wdat, instr, x); + if (ret) + return ret; + break; + + default: + dev_err(&wdat->pdev->dev, "Unknown instruction: %u\n", + flags); + return -EINVAL; + } + } + + return 0; +} + +static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat) +{ + int ret; + + /* + * WDAT specification says that the watchdog is required to reboot + * the system when it fires. However, it also states that it is + * recommeded to make it configurable through hardware register. We + * enable reboot now if it is configrable, just in case. + */ + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, 0); + if (ret && ret != -EOPNOTSUPP) { + dev_err(&wdat->pdev->dev, + "Failed to enable reboot when watchdog triggers\n"); + return ret; + } + + return 0; +} + +static void wdat_wdt_boot_status(struct wdat_wdt *wdat) +{ + u32 boot_status = 0; + int ret; + + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status); + if (ret && ret != -EOPNOTSUPP) { + dev_err(&wdat->pdev->dev, "Failed to read boot status\n"); + return; + } + + if (boot_status) + wdat->wdd.bootstatus = WDIOF_CARDRESET; + + /* Clear the boot status in case BIOS did not do it */ + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, 0); + if (ret && ret != -EOPNOTSUPP) + dev_err(&wdat->pdev->dev, "Failed to clear boot status\n"); +} + +static void wdat_wdt_set_running(struct wdat_wdt *wdat) +{ + u32 running = 0; + int ret; + + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0, + &running); + if (ret && ret != -EOPNOTSUPP) + dev_err(&wdat->pdev->dev, "Failed to read running state\n"); + + if (running) + set_bit(WDOG_HW_RUNNING, &wdat->wdd.status); +} + +static int wdat_wdt_start(struct watchdog_device *wdd) +{ + return wdat_wdt_run_action(to_wdat_wdt(wdd), + ACPI_WDAT_SET_RUNNING_STATE, 0, NULL); +} + +static int wdat_wdt_stop(struct watchdog_device *wdd) +{ + return wdat_wdt_run_action(to_wdat_wdt(wdd), + ACPI_WDAT_SET_STOPPED_STATE, 0, NULL); +} + +static int wdat_wdt_ping(struct watchdog_device *wdd) +{ + return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, 0, NULL); +} + +static int wdat_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct wdat_wdt *wdat = to_wdat_wdt(wdd); + unsigned int periods; + int ret; + + periods = timeout * 1000 / wdat->period; + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL); + if (!ret) + wdd->timeout = timeout; + return ret; +} + +static unsigned int wdat_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct wdat_wdt *wdat = to_wdat_wdt(wdd); + u32 periods = 0; + + wdat_wdt_run_action(wdat, ACPI_WDAT_GET_COUNTDOWN, 0, &periods); + return periods * wdat->period / 1000; +} + +static const struct watchdog_info wdat_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "wdat_wdt", +}; + +static const struct watchdog_ops wdat_wdt_ops = { + .owner = THIS_MODULE, + .start = wdat_wdt_start, + .stop = wdat_wdt_stop, + .ping = wdat_wdt_ping, + .set_timeout = wdat_wdt_set_timeout, + .get_timeleft = wdat_wdt_get_timeleft, +}; + +static int wdat_wdt_probe(struct platform_device *pdev) +{ + const struct acpi_wdat_entry *entries; + const struct acpi_table_wdat *tbl; + struct wdat_wdt *wdat; + struct resource *res; + void __iomem **regs; + acpi_status status; + int i, ret; + + status = acpi_get_table(ACPI_SIG_WDAT, 0, + (struct acpi_table_header **)&tbl); + if (ACPI_FAILURE(status)) + return -ENODEV; + + wdat = devm_kzalloc(&pdev->dev, sizeof(*wdat), GFP_KERNEL); + if (!wdat) + return -ENOMEM; + + regs = devm_kcalloc(&pdev->dev, pdev->num_resources, sizeof(*regs), + GFP_KERNEL); + if (!regs) + return -ENOMEM; + + /* WDAT specification wants to have >= 1ms period */ + if (tbl->timer_period < 1) + return -EINVAL; + if (tbl->min_count > tbl->max_count) + return -EINVAL; + + wdat->period = tbl->timer_period; + wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count; + wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count; + wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED; + wdat->wdd.info = &wdat_wdt_info; + wdat->wdd.ops = &wdat_wdt_ops; + wdat->pdev = pdev; + + /* Request and map all resources */ + for (i = 0; i < pdev->num_resources; i++) { + void __iomem *reg; + + res = &pdev->resource[i]; + if (resource_type(res) == IORESOURCE_MEM) { + reg = devm_ioremap_resource(&pdev->dev, res); + } else if (resource_type(res) == IORESOURCE_IO) { + reg = devm_ioport_map(&pdev->dev, res->start, 1); + } else { + dev_err(&pdev->dev, "Unsupported resource\n"); + return -EINVAL; + } + + if (!reg) + return -ENOMEM; + + regs[i] = reg; + } + + entries = (struct acpi_wdat_entry *)(tbl + 1); + for (i = 0; i < tbl->entries; i++) { + const struct acpi_generic_address *gas; + struct wdat_instruction *instr; + struct list_head *instructions; + unsigned int action; + struct resource r; + int j; + + action = entries[i].action; + if (action >= MAX_WDAT_ACTIONS) { + dev_dbg(&pdev->dev, "Skipping unknown action: %u\n", + action); + continue; + } + + instr = devm_kzalloc(&pdev->dev, sizeof(*instr), GFP_KERNEL); + if (!instr) + return -ENOMEM; + + INIT_LIST_HEAD(&instr->node); + instr->entry = entries[i]; + + gas = &entries[i].register_region; + + memset(&r, 0, sizeof(r)); + r.start = gas->address; + r.end = r.start + gas->access_width; + if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + r.flags = IORESOURCE_MEM; + } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + r.flags = IORESOURCE_IO; + } else { + dev_dbg(&pdev->dev, "Unsupported address space: %d\n", + gas->space_id); + continue; + } + + /* Find the matching resource */ + for (j = 0; j < pdev->num_resources; j++) { + res = &pdev->resource[j]; + if (resource_contains(res, &r)) { + instr->reg = regs[j] + r.start - res->start; + break; + } + } + + if (!instr->reg) { + dev_err(&pdev->dev, "I/O resource not found\n"); + return -EINVAL; + } + + instructions = wdat->instructions[action]; + if (!instructions) { + instructions = devm_kzalloc(&pdev->dev, + sizeof(*instructions), GFP_KERNEL); + if (!instructions) + return -ENOMEM; + + INIT_LIST_HEAD(instructions); + wdat->instructions[action] = instructions; + } + + list_add_tail(&instr->node, instructions); + } + + wdat_wdt_boot_status(wdat); + wdat_wdt_set_running(wdat); + + ret = wdat_wdt_enable_reboot(wdat); + if (ret) + return ret; + + platform_set_drvdata(pdev, wdat); + + watchdog_set_nowayout(&wdat->wdd, nowayout); + return devm_watchdog_register_device(&pdev->dev, &wdat->wdd); +} + +#ifdef CONFIG_PM_SLEEP +static int wdat_wdt_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct wdat_wdt *wdat = platform_get_drvdata(pdev); + int ret; + + if (!watchdog_active(&wdat->wdd)) + return 0; + + /* + * We need to stop the watchdog if firmare is not doing it or if we + * are going suspend to idle (where firmware is not involved). If + * firmware is stopping the watchdog we kick it here one more time + * to give it some time. + */ + wdat->stopped = false; + if (acpi_target_system_state() == ACPI_STATE_S0 || + !wdat->stopped_in_sleep) { + ret = wdat_wdt_stop(&wdat->wdd); + if (!ret) + wdat->stopped = true; + } else { + ret = wdat_wdt_ping(&wdat->wdd); + } + + return ret; +} + +static int wdat_wdt_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct wdat_wdt *wdat = platform_get_drvdata(pdev); + int ret; + + if (!watchdog_active(&wdat->wdd)) + return 0; + + if (!wdat->stopped) { + /* + * Looks like the boot firmware reinitializes the watchdog + * before it hands off to the OS on resume from sleep so we + * stop and reprogram the watchdog here. + */ + ret = wdat_wdt_stop(&wdat->wdd); + if (ret) + return ret; + + ret = wdat_wdt_set_timeout(&wdat->wdd, wdat->wdd.timeout); + if (ret) + return ret; + + ret = wdat_wdt_enable_reboot(wdat); + if (ret) + return ret; + } + + return wdat_wdt_start(&wdat->wdd); +} +#endif + +static const struct dev_pm_ops wdat_wdt_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq, + wdat_wdt_resume_noirq) +}; + +static struct platform_driver wdat_wdt_driver = { + .probe = wdat_wdt_probe, + .driver = { + .name = "wdat_wdt", + .pm = &wdat_wdt_pm_ops, + }, +}; + +module_platform_driver(wdat_wdt_driver); + +MODULE_AUTHOR("Mika Westerberg "); +MODULE_DESCRIPTION("ACPI Hardware Watchdog (WDAT) driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:wdat_wdt"); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index c5eaf2f..8ff6ca4 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1074,4 +1074,10 @@ void acpi_table_upgrade(void); static inline void acpi_table_upgrade(void) { } #endif +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_WATCHDOG) +extern bool acpi_has_watchdog(void); +#else +static inline bool acpi_has_watchdog(void) { return false; } +#endif + #endif /*_LINUX_ACPI_H*/ -- cgit v0.10.2 From 9febcdc071aaec30d8dee4a21d91f6c7e9c62503 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 23 Sep 2016 11:26:35 +0800 Subject: ACPICA: Tables: Fix "UNLOAD" code path lock issues ACPICA commit 39227380f5b99c51b897a3ffedd88508aa26789b The previous lock fixes didn't cover "Unload" opcode and table unload APIs, this patch fixes lock issues in the "Unload" code path. BZ 1325, Lv Zheng. Link: https://github.com/acpica/acpica/commit/39227380 Link: https://bugs.acpica.org/show_bug.cgi?id=1325 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 421836a..718428b 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -532,10 +532,17 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) table_index = table_desc->reference.value; + /* + * Release the interpreter lock so that the table lock won't have + * strict order requirement against it. + */ + acpi_ex_exit_interpreter(); + /* Ensure the table is still loaded */ if (!acpi_tb_is_table_loaded(table_index)) { - return_ACPI_STATUS(AE_NOT_EXIST); + status = AE_NOT_EXIST; + goto lock_and_exit; } /* Invoke table handler if present */ @@ -553,16 +560,24 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) status = acpi_tb_delete_namespace_by_owner(table_index); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + goto lock_and_exit; } (void)acpi_tb_release_owner_id(table_index); acpi_tb_set_table_loaded_flag(table_index, FALSE); +lock_and_exit: + + /* Re-acquire the interpreter lock */ + + acpi_ex_enter_interpreter(); + /* * Invalidate the handle. We do this because the handle may be stored * in a named object and may not be actually deleted until much later. */ - ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID; - return_ACPI_STATUS(AE_OK); + if (ACPI_SUCCESS(status)) { + ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID; + } + return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c index 7e93fd6..d9ca8c2 100644 --- a/drivers/acpi/acpica/tbdata.c +++ b/drivers/acpi/acpica/tbdata.c @@ -614,17 +614,12 @@ 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. */ - (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); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } - + acpi_ns_delete_namespace_by_owner(owner_id); acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock); - - status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 870ad64..5569f63 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -378,9 +378,9 @@ acpi_status acpi_unload_parent_table(acpi_handle object) return_ACPI_STATUS(AE_TYPE); } - /* Must acquire the interpreter lock during this operation */ + /* Must acquire the table lock during this operation */ - status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); + status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -407,8 +407,10 @@ acpi_status acpi_unload_parent_table(acpi_handle object) /* Ensure the table is actually loaded */ + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); if (!acpi_tb_is_table_loaded(i)) { status = AE_NOT_EXIST; + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); break; } @@ -434,10 +436,11 @@ acpi_status acpi_unload_parent_table(acpi_handle object) status = acpi_tb_release_owner_id(i); acpi_tb_set_table_loaded_flag(i, FALSE); + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); break; } - (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); return_ACPI_STATUS(status); } -- cgit v0.10.2 From 7a0b71dc80f2b829c8587267d188712f614a4727 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 23 Sep 2016 11:26:43 +0800 Subject: ACPICA: Parser: Fix a regression in LoadTable support ACPICA commit a78506e0ce8ab1d20db2a055d99cf9143e89eb29 LoadTable allows an alternative RootPathString than the default "\", while the new table execution support fails to keep this logic. This regression can be detected by ASLTS - TLT0.tst4, this patch fixes this regression. Linux upstream is not affected by this regression as we haven't enabled the new table execution support there. BZ 1326, Lv Zheng. Link: https://github.com/acpica/acpica/commit/a78506e0 Link: https://bugs.acpica.org/show_bug.cgi?id=1326 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 8d7e5b5..f3c8726 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -305,6 +305,17 @@ acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info) walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL; } + /* Info->Node is the default location to load the table */ + + if (info->node && info->node != acpi_gbl_root_node) { + status = + acpi_ds_scope_stack_push(info->node, ACPI_TYPE_METHOD, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + /* * Parse the AML, walk_state will be deleted by parse_aml */ -- cgit v0.10.2 From 1ef356681ef412abfd93d1c36f15917a126833f5 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 23 Sep 2016 11:26:49 +0800 Subject: ACPI / bus: Adjust ACPI subsystem initialization for new table loading mode This patch enables the following initialization order for the new table loading mode (which is enabled by setting acpi_gbl_parse_table_as_term_list to TRUE): 1. Install default region handlers (SystemMemory, SystemIo, PciConfig, EmbeddedControl via ECDT) without evaluating _REG; 2. Load the table and execute the module level AML opcodes instantly. Signed-off-by: Lv Zheng [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 85b7d07..658b4c4 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -985,7 +985,8 @@ void __init acpi_early_init(void) goto error0; } - if (acpi_gbl_group_module_level_code) { + if (!acpi_gbl_parse_table_as_term_list && + acpi_gbl_group_module_level_code) { status = acpi_load_tables(); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX @@ -1074,7 +1075,8 @@ static int __init acpi_bus_init(void) status = acpi_ec_ecdt_probe(); /* Ignore result. Not having an ECDT is not fatal. */ - if (!acpi_gbl_group_module_level_code) { + if (acpi_gbl_parse_table_as_term_list || + !acpi_gbl_group_module_level_code) { status = acpi_load_tables(); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX -- cgit v0.10.2 From 3413f702fae096184ad8bd9be9d7ecbc7c182bd7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 20 Sep 2016 15:30:52 +0300 Subject: mfd: lpc_ich: Do not create iTCO watchdog when WDAT table exists ACPI WDAT table is the preferred way to use hardware watchdog over the native iTCO_wdt. Windows only uses this table for its hardware watchdog implementation so we should be relatively safe to trust it has been validated by OEMs Prevent iTCO watchdog creation if we detect that there is ACPI WDAT table. Signed-off-by: Mika Westerberg Reviewed-by: Guenter Roeck Acked-by: Lee Jones Signed-off-by: Rafael J. Wysocki diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index bd3aa45..c8dee47 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -984,6 +984,10 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) int ret; struct resource *res; + /* If we have ACPI based watchdog use that instead */ + if (acpi_has_watchdog()) + return -ENODEV; + /* Setup power management base register */ pci_read_config_dword(dev, priv->abase, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; -- cgit v0.10.2 From 1f6dbb022bd477239bfbb52ef868f694b5f1d36a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 20 Sep 2016 15:30:53 +0300 Subject: i2c: i801: Do not create iTCO watchdog when WDAT table exists ACPI WDAT table is the preferred way to use hardware watchdog over the native iTCO_wdt. Windows only uses this table for its hardware watchdog implementation so we should be relatively safe to trust it has been validated by OEMs Prevent iTCO watchdog creation if we detect that there is ACPI WDAT table. Signed-off-by: Mika Westerberg Reviewed-by: Guenter Roeck Acked-by: Wolfram Sang Signed-off-by: Rafael J. Wysocki diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 5ef9b73..26298af 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1486,7 +1486,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) priv->features |= FEATURE_IRQ; priv->features |= FEATURE_SMBUS_PEC; priv->features |= FEATURE_BLOCK_BUFFER; - priv->features |= FEATURE_TCO; + /* If we have ACPI based watchdog use that instead */ + if (!acpi_has_watchdog()) + priv->features |= FEATURE_TCO; priv->features |= FEATURE_HOST_NOTIFY; break; -- cgit v0.10.2 From bba6529ea039925ed422696be5d52677bd16643c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 20 Sep 2016 15:30:54 +0300 Subject: platform/x86: intel_pmc_ipc: Do not create iTCO watchdog when WDAT table exists ACPI WDAT table is the preferred way to use hardware watchdog over the native iTCO_wdt. Windows only uses this table for its hardware watchdog implementation so we should be relatively safe to trust it has been validated by OEMs. Prevent iTCO watchdog creation if we detect that there is an ACPI WDAT table. Signed-off-by: Mika Westerberg Reviewed-by: Guenter Roeck Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index b86e1bc..a511d51 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -651,11 +651,15 @@ static int ipc_create_pmc_devices(void) { int ret; - ret = ipc_create_tco_device(); - if (ret) { - dev_err(ipcdev.dev, "Failed to add tco platform device\n"); - return ret; + /* If we have ACPI based watchdog use that instead */ + if (!acpi_has_watchdog()) { + ret = ipc_create_tco_device(); + if (ret) { + dev_err(ipcdev.dev, "Failed to add tco platform device\n"); + return ret; + } } + ret = ipc_create_punit_device(); if (ret) { dev_err(ipcdev.dev, "Failed to add punit platform device\n"); -- cgit v0.10.2 From 356ed043517dbceb646a9353831abde91062a395 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Sep 2016 23:15:54 +0200 Subject: watchdog: wdat_wdt: fix return value check in wdat_wdt_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Mika Westerberg Reviewed-by: Guenter Roeck Signed-off-by: Rafael J. Wysocki diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index 6b64645..4594723 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -351,16 +351,17 @@ static int wdat_wdt_probe(struct platform_device *pdev) res = &pdev->resource[i]; if (resource_type(res) == IORESOURCE_MEM) { reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); } else if (resource_type(res) == IORESOURCE_IO) { reg = devm_ioport_map(&pdev->dev, res->start, 1); + if (!reg) + return -ENOMEM; } else { dev_err(&pdev->dev, "Unsupported resource\n"); return -EINVAL; } - if (!reg) - return -ENOMEM; - regs[i] = reg; } -- cgit v0.10.2 From cda3b9178510297ac968b7e129e20caaaa66c581 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Sep 2016 23:17:11 +0200 Subject: watchdog: wdat_wdt: Fix warning for using 0 as NULL Fixes the following sparse warnings: drivers/watchdog/wdat_wdt.c:210:66: warning: Using plain integer as NULL pointer drivers/watchdog/wdat_wdt.c:235:66: warning: Using plain integer as NULL pointer Signed-off-by: Wei Yongjun Acked-by: Mika Westerberg Reviewed-by: Guenter Roeck Signed-off-by: Rafael J. Wysocki diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index 4594723..e473e3b 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -207,7 +207,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat) * recommeded to make it configurable through hardware register. We * enable reboot now if it is configrable, just in case. */ - ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, 0); + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL); if (ret && ret != -EOPNOTSUPP) { dev_err(&wdat->pdev->dev, "Failed to enable reboot when watchdog triggers\n"); @@ -232,7 +232,7 @@ static void wdat_wdt_boot_status(struct wdat_wdt *wdat) wdat->wdd.bootstatus = WDIOF_CARDRESET; /* Clear the boot status in case BIOS did not do it */ - ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, 0); + ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, NULL); if (ret && ret != -EOPNOTSUPP) dev_err(&wdat->pdev->dev, "Failed to clear boot status\n"); } -- cgit v0.10.2 From 060d791f760bd80c0f96de668a662cab0286146a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 23 Sep 2016 17:57:06 +0300 Subject: ACPI / documentation: Use recommended name in GPIO property names The recommended property name for all kinds of GPIOs is to end it with "-gpios" even if there is only one GPIO. Update the documentation to follow this fact. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/gpio-properties.txt b/Documentation/acpi/gpio-properties.txt index f35dad1..5aafe0b3 100644 --- a/Documentation/acpi/gpio-properties.txt +++ b/Documentation/acpi/gpio-properties.txt @@ -28,8 +28,8 @@ index, like the ASL example below shows: ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { - Package () {"reset-gpio", Package() {^BTH, 1, 1, 0 }}, - Package () {"shutdown-gpio", Package() {^BTH, 0, 0, 0 }}, + Package () {"reset-gpios", Package() {^BTH, 1, 1, 0 }}, + Package () {"shutdown-gpios", Package() {^BTH, 0, 0, 0 }}, } }) } @@ -48,7 +48,7 @@ Since ACPI GpioIo() resource does not have a field saying whether it is active low or high, the "active_low" argument can be used here. Setting it to 1 marks the GPIO as active low. -In our Bluetooth example the "reset-gpio" refers to the second GpioIo() +In our Bluetooth example the "reset-gpios" refers to the second GpioIo() resource, second pin in that resource with the GPIO number of 31. ACPI GPIO Mappings Provided by Drivers @@ -83,8 +83,8 @@ static const struct acpi_gpio_params reset_gpio = { 1, 1, false }; static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false }; static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = { - { "reset-gpio", &reset_gpio, 1 }, - { "shutdown-gpio", &shutdown_gpio, 1 }, + { "reset-gpios", &reset_gpio, 1 }, + { "shutdown-gpios", &shutdown_gpio, 1 }, { }, }; -- cgit v0.10.2