@@ -56,6 +56,8 @@
*/
acpi_status acpi_ns_initialize_objects(void);
+acpi_status acpi_ns_early_initialize_devices(void);
+
acpi_status acpi_ns_initialize_devices(u32 flags);
acpi_status
@@ -86,6 +86,59 @@ acpi_status acpi_ns_initialize_objects(void)
return_ACPI_STATUS(AE_OK);
}
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_early_initialize_devices
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: acpi_status
+ *
+ * DESCRIPTION: Call _INI on various root devices, this must be done
+ * before any other evaluations as those might depend on this.
+ *
+ ******************************************************************************/
+static acpi_string const acpi_ns_early_init_paths[] = {
+ /* Global \_INI method. Provided for Windows compatibility (Vista+),
+ * not part of the ACPI specification. */
+ ACPI_NS_ROOT_PATH,
+ /* There appears to be a strict order requirement for \_SB._INI,
+ * which should be evaluated before any _REG evaluations. */
+ "\\_SB",
+};
+
+acpi_status acpi_ns_early_initialize_devices(void)
+{
+ struct acpi_evaluate_info *info;
+ acpi_status status;
+ acpi_handle handle;
+ int i;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Initializing Early ACPI Devices\n"));
+
+ info = ACPI_ALLOCATE(sizeof(struct acpi_evaluate_info));
+ if (!info)
+ return AE_NO_MEMORY;
+
+ for (i = 0; i < ARRAY_SIZE(acpi_ns_early_init_paths); i++) {
+ status = acpi_get_handle(NULL, acpi_ns_early_init_paths[i], &handle);
+ if (!ACPI_SUCCESS(status))
+ continue;
+
+ memset(info, 0, sizeof(struct acpi_evaluate_info));
+ info->prefix_node = handle;
+ info->relative_pathname = METHOD_NAME__INI;
+ info->parameters = NULL;
+ info->flags = ACPI_IGNORE_RETURN_VALUE;
+
+ acpi_ns_evaluate(info);
+ }
+
+ ACPI_FREE(info);
+ return AE_OK;
+}
+
/*******************************************************************************
*
* FUNCTION: acpi_ns_initialize_devices
@@ -105,12 +158,41 @@ acpi_status acpi_ns_initialize_objects(void)
acpi_status acpi_ns_initialize_devices(u32 flags)
{
acpi_status status = AE_OK;
- struct acpi_device_walk_info info;
- acpi_handle handle;
ACPI_FUNCTION_TRACE(ns_initialize_devices);
+ if (!(flags & (ACPI_NO_DEVICE_INIT | ACPI_NO_EARLY_DEVICE_INIT))) {
+ status = acpi_ns_early_initialize_devices();
+ if (ACPI_FAILURE(status))
+ goto error_exit;
+ }
+
+ /*
+ * Run all _REG methods
+ *
+ * Note: Any objects accessed by the _REG methods will be automatically
+ * initialized, even if they contain executable AML (see the call to
+ * acpi_ns_initialize_objects below).
+ *
+ * Note: According to the ACPI specification, we actually needn't execute
+ * _REG for system_memory/system_io operation regions, but for PCI_Config
+ * operation regions, it is required to evaluate _REG for those on a PCI
+ * root bus that doesn't contain _BBN object. So this code is kept here
+ * in order not to break things.
+ */
+ if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[Init] Executing _REG OpRegion methods\n"));
+
+ status = acpi_ev_initialize_op_regions();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
+ }
+
if (!(flags & ACPI_NO_DEVICE_INIT)) {
+ struct acpi_device_walk_info info;
+
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"[Init] Initializing ACPI Devices\n"));
@@ -143,68 +225,6 @@ acpi_status acpi_ns_initialize_devices(u32 flags)
goto error_exit;
}
- /*
- * Execute the "global" _INI method that may appear at the root.
- * This support is provided for Windows compatibility (Vista+) and
- * is not part of the ACPI specification.
- */
- info.evaluate_info->prefix_node = acpi_gbl_root_node;
- info.evaluate_info->relative_pathname = METHOD_NAME__INI;
- info.evaluate_info->parameters = NULL;
- info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE;
-
- status = acpi_ns_evaluate(info.evaluate_info);
- if (ACPI_SUCCESS(status)) {
- info.num_INI++;
- }
-
- /*
- * Execute \_SB._INI.
- * There appears to be a strict order requirement for \_SB._INI,
- * which should be evaluated before any _REG evaluations.
- */
- status = acpi_get_handle(NULL, "\\_SB", &handle);
- if (ACPI_SUCCESS(status)) {
- memset(info.evaluate_info, 0,
- sizeof(struct acpi_evaluate_info));
- info.evaluate_info->prefix_node = handle;
- info.evaluate_info->relative_pathname =
- METHOD_NAME__INI;
- info.evaluate_info->parameters = NULL;
- info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE;
-
- status = acpi_ns_evaluate(info.evaluate_info);
- if (ACPI_SUCCESS(status)) {
- info.num_INI++;
- }
- }
- }
-
- /*
- * Run all _REG methods
- *
- * Note: Any objects accessed by the _REG methods will be automatically
- * initialized, even if they contain executable AML (see the call to
- * acpi_ns_initialize_objects below).
- *
- * Note: According to the ACPI specification, we actually needn't execute
- * _REG for system_memory/system_io operation regions, but for PCI_Config
- * operation regions, it is required to evaluate _REG for those on a PCI
- * root bus that doesn't contain _BBN object. So this code is kept here
- * in order not to break things.
- */
- if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) {
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "[Init] Executing _REG OpRegion methods\n"));
-
- status = acpi_ev_initialize_op_regions();
- if (ACPI_FAILURE(status)) {
- goto error_exit;
- }
- }
-
- if (!(flags & ACPI_NO_DEVICE_INIT)) {
-
/* Walk namespace to execute all _INIs on present devices */
status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
@@ -497,6 +517,21 @@ acpi_ns_find_ini_methods(acpi_handle obj_handle,
return (AE_OK);
}
+static u8 acpi_ns_is_early_init_device(struct acpi_namespace_node *device_node)
+{
+ char path[ACPI_PATH_SEGMENT_LENGTH + 2];
+ int i;
+
+ acpi_ns_build_normalized_path(device_node, path, sizeof(path), TRUE);
+
+ for (i = 0; i < ARRAY_SIZE(acpi_ns_early_init_paths); i++) {
+ if (!strcmp(path, acpi_ns_early_init_paths[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*******************************************************************************
*
* FUNCTION: acpi_ns_init_one_device
@@ -630,8 +665,7 @@ acpi_ns_init_one_device(acpi_handle obj_handle,
* Note: We know there is an _INI within this subtree, but it may not be
* under this particular device, it may be lower in the branch.
*/
- if (!ACPI_COMPARE_NAMESEG(device_node->name.ascii, "_SB_") ||
- device_node->parent != acpi_gbl_root_node) {
+ if (!acpi_ns_is_early_init_device(device_node)) {
ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
(ACPI_TYPE_METHOD, device_node,
METHOD_NAME__INI));
@@ -193,6 +193,27 @@ acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags)
ACPI_EXPORT_SYMBOL_INIT(acpi_enable_subsystem)
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_early_initialize_objects
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Call _INI on various root objects, this must be done before
+ * any other AML evaluations as those might depend on this.
+ * This is automatically called by acpi_initialize_objects(),
+ * unless ACPI_NO_EARLY_DEVICE_INIT is passed.
+ *
+ ******************************************************************************/
+acpi_status ACPI_INIT_FUNCTION acpi_early_initialize_objects(void)
+{
+ return acpi_ns_early_initialize_devices();
+}
+
+ACPI_EXPORT_SYMBOL_INIT(acpi_early_initialize_objects)
+
/*******************************************************************************
*
* FUNCTION: acpi_initialize_objects
@@ -410,6 +410,8 @@ 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_early_initialize_objects(void))
ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION
acpi_initialize_objects(u32 flags))
ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_INIT_FUNCTION
@@ -565,7 +565,8 @@ typedef u64 acpi_integer;
#define ACPI_NO_HANDLER_INIT 0x0010
#define ACPI_NO_OBJECT_INIT 0x0020
#define ACPI_NO_DEVICE_INIT 0x0040
-#define ACPI_NO_ADDRESS_SPACE_INIT 0x0080
+#define ACPI_NO_EARLY_DEVICE_INIT 0x0080
+#define ACPI_NO_ADDRESS_SPACE_INIT 0x0100
/*
* Initialization state
acpi_initialize_objects() calls \_INI and \_SB.INI before executing _REG OpRegion methods, because the _REG methods may rely on initialization done by these _INI methods. ACPI-2.0 says that the EC OpRegion handler must be available immediately (like the standard default OpRegion handlers). So the OS must probe the ECDT described EC early on and ends up calling the EC's _REG method before calling acpi_initialize_objects(). This may lead to the EC _REG method not working properly e.g. on a "Lenovo IdeaPad 5 15ITL05" \_SB.PC00.LPCB.EC0._REG gets evaluated before \_SB.PC00._INI and that _REG contains: If ((OSYS == 0x07DF)) { Local0 = 0x06 } If ((Acquire (LFCM, 0xA000) == Zero)) { OSTY = Local0 ... With OSTY being a SystemMemory OpRegion field, due to the _INI running too late, Local0 stays at 0. Causing OSTY to be set to 0 instead of 6, which causes the brightness up/down keys to not work, see: https://bugzilla.kernel.org/show_bug.cgi?id=214899 Factor out the early (root) object _INI calls into a new acpi_early_initialize_objects() helper. The OS code can then call this before executing the EC's _REG method so that it will work properly. Note that if the OS code uses acpi_early_initialize_objects(), then it must also pass the new ACPI_NO_EARLY_DEVICE_INIT flag to acpi_initialize_objects() to avoid the _INI methods getting evaluated twice. Without this flag acpi_initialize_objects() will still do the early object _INI calls as before. So by itself this patch does not cause any functional changes. Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- drivers/acpi/acpica/acnamesp.h | 2 + drivers/acpi/acpica/nsinit.c | 166 ++++++++++++++++++++------------- drivers/acpi/acpica/utxfinit.c | 21 +++++ include/acpi/acpixf.h | 2 + include/acpi/actypes.h | 3 +- 5 files changed, 127 insertions(+), 67 deletions(-)