From 60fa6ae6e6d09e377fce6f8d9b6f6a4d88769f63 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 15 May 2024 21:40:54 +0200 Subject: [PATCH 1/2] ACPI: EC: Install address space handler at the namespace root It is reported that _DSM evaluation fails in ucsi_acpi_dsm() on Lenovo IdeaPad Pro 5 due to a missing address space handler for the EC address space: ACPI Error: No handler for Region [ECSI] (000000007b8176ee) [EmbeddedControl] (20230628/evregion-130) This happens because if there is no ECDT, the EC driver only registers the EC address space handler for operation regions defined in the EC device scope of the ACPI namespace while the operation region being accessed by the _DSM in question is located beyond that scope. To address this, modify the ACPI EC driver to install the EC address space handler at the root of the ACPI namespace for the first EC that can be found regardless of whether or not an ECDT is present. Note that this change is consistent with some examples in the ACPI specification in which EC operation regions located outside the EC device scope are used (for example, see Section 9.17.15 in ACPI 6.5), so the current behavior of the EC driver is arguably questionable. Reported-by: webcaptcha Link: https://bugzilla.kernel.org/show_bug.cgi?id=218789 Link: https://uefi.org/specs/ACPI/6.5/09_ACPI_Defined_Devices_and_Device_Specific_Objects.html#example-asl-code Link: https://lore.kernel.org/linux-acpi/Zi+0whTvDbAdveHq@kuha.fi.intel.com Suggested-by: Heikki Krogerus Signed-off-by: Rafael J. Wysocki Reviewed-by: Hans de Goede Reviewed-by: Mario Limonciello Reviewed-by: Andy Shevchenko --- drivers/acpi/ec.c | 25 ++++++++++++++++--------- drivers/acpi/internal.h | 1 - 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 02255795b800d..e7793ee9e6498 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1482,13 +1482,14 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec) static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device, bool call_reg) { + acpi_handle scope_handle = ec == first_ec ? ACPI_ROOT_OBJECT : ec->handle; acpi_status status; acpi_ec_start(ec, false); if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { acpi_ec_enter_noirq(ec); - status = acpi_install_address_space_handler_no_reg(ec->handle, + status = acpi_install_address_space_handler_no_reg(scope_handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, NULL, ec); @@ -1497,11 +1498,10 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device, return -ENODEV; } set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); - ec->address_space_handler_holder = ec->handle; } if (call_reg && !test_bit(EC_FLAGS_EC_REG_CALLED, &ec->flags)) { - acpi_execute_reg_methods(ec->handle, ACPI_ADR_SPACE_EC); + acpi_execute_reg_methods(scope_handle, ACPI_ADR_SPACE_EC); set_bit(EC_FLAGS_EC_REG_CALLED, &ec->flags); } @@ -1553,10 +1553,13 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device, static void ec_remove_handlers(struct acpi_ec *ec) { + acpi_handle scope_handle = ec == first_ec ? ACPI_ROOT_OBJECT : ec->handle; + if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { if (ACPI_FAILURE(acpi_remove_address_space_handler( - ec->address_space_handler_holder, - ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) + scope_handle, + ACPI_ADR_SPACE_EC, + &acpi_ec_space_handler))) pr_err("failed to remove space handler\n"); clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); } @@ -1595,14 +1598,18 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca { int ret; - ret = ec_install_handlers(ec, device, call_reg); - if (ret) - return ret; - /* First EC capable of handling transactions */ if (!first_ec) first_ec = ec; + ret = ec_install_handlers(ec, device, call_reg); + if (ret) { + if (ec == first_ec) + first_ec = NULL; + + return ret; + } + pr_info("EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", ec->command_addr, ec->data_addr); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 60c483836756d..2a0e9fc7b74c1 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -186,7 +186,6 @@ enum acpi_ec_event_state { struct acpi_ec { acpi_handle handle; - acpi_handle address_space_handler_holder; int gpe; int irq; unsigned long command_addr; From 98a83da39b482c638954b111803906843a83a747 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 May 2024 18:32:52 +0200 Subject: [PATCH 2/2] platform/x86: wmi: Remove custom EC address space handler The custom EC address space handler in the WMI driver was only needed because the EC driver did not install its address space handler for EC operation regions beyond the EC device scope in the ACPI namespace. That has just changed, so the custom EC address handler is not needed any more and it can be removed. Signed-off-by: Rafael J. Wysocki Reviewed-by: Armin Wolf Reviewed-by: Heikki Krogerus Reviewed-by: Hans de Goede Reviewed-by: Mario Limonciello Reviewed-by: Andy Shevchenko --- drivers/platform/x86/wmi.c | 92 -------------------------------------- 1 file changed, 92 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 060e4236f932b..d21f3fa258239 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1153,77 +1153,6 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) return 0; } -static int ec_read_multiple(u8 address, u8 *buffer, size_t bytes) -{ - size_t i; - int ret; - - for (i = 0; i < bytes; i++) { - ret = ec_read(address + i, &buffer[i]); - if (ret < 0) - return ret; - } - - return 0; -} - -static int ec_write_multiple(u8 address, u8 *buffer, size_t bytes) -{ - size_t i; - int ret; - - for (i = 0; i < bytes; i++) { - ret = ec_write(address + i, buffer[i]); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - * WMI can have EmbeddedControl access regions. In which case, we just want to - * hand these off to the EC driver. - */ -static acpi_status -acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, - u32 bits, u64 *value, - void *handler_context, void *region_context) -{ - int bytes = bits / BITS_PER_BYTE; - int ret; - - if (!value) - return AE_NULL_ENTRY; - - if (!bytes || bytes > sizeof(*value)) - return AE_BAD_PARAMETER; - - if (address > U8_MAX || address + bytes - 1 > U8_MAX) - return AE_BAD_PARAMETER; - - if (function != ACPI_READ && function != ACPI_WRITE) - return AE_BAD_PARAMETER; - - if (function == ACPI_READ) - ret = ec_read_multiple(address, (u8 *)value, bytes); - else - ret = ec_write_multiple(address, (u8 *)value, bytes); - - switch (ret) { - case -EINVAL: - return AE_BAD_PARAMETER; - case -ENODEV: - return AE_NOT_FOUND; - case -ETIME: - return AE_TIME; - case 0: - return AE_OK; - default: - return AE_ERROR; - } -} - static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj) { struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -1338,14 +1267,6 @@ static void acpi_wmi_remove_notify_handler(void *data) acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); } -static void acpi_wmi_remove_address_space_handler(void *data) -{ - struct acpi_device *acpi_device = data; - - acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler); -} - static void acpi_wmi_remove_bus_device(void *data) { struct device *wmi_bus_dev = data; @@ -1377,19 +1298,6 @@ static int acpi_wmi_probe(struct platform_device *device) dev_set_drvdata(&device->dev, wmi_bus_dev); - status = acpi_install_address_space_handler(acpi_device->handle, - ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler, - NULL, NULL); - if (ACPI_FAILURE(status)) { - dev_err(&device->dev, "Error installing EC region handler\n"); - return -ENODEV; - } - error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_address_space_handler, - acpi_device); - if (error < 0) - return error; - status = acpi_install_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler, wmi_bus_dev); if (ACPI_FAILURE(status)) {