From 9880702d123f202369fb674ae62bae25be27475c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Nov 2023 17:06:27 +0100 Subject: [PATCH 01/56] ACPI: property: Support using strings in reference properties In order to allow referencing data nodes directly, which is not possible currently, add support for representing references in device properties as strings (relative or absolute name paths). For example, after this change, the "mipi-img-flash-leds" property in the ASL snippet below will be treated as a proper reference to the LED0 object under LEDD. Package () { "mipi-img-flash-leds", "\\_SB.PCI0.I2C2.LEDD.LED0", } Device (LEDD) { Name (_DSD, Package () // _DSD: Device-Specific Data { ToUUID ("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), /* Hierarchical Data Extension */, Package () { Package () { "mipi-img-flash-led-0", "LED0", } }, }) Name (LED0, Package () // _DSD: Device-Specific Data { ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties */, Package () { Package () { "mipi-img-max-current", 1000000, } } }) } Also remove the mechanism allowing data nodes to be referenced indirectly, with the help of an object reference pointing to the "ancestor" device and a path relative to it (this mechanism is not expected to be in use in any production platform firmware in the field). Note that this change allows also using strings for referencing device objects, in addition to object references that have been supported already. While at it, add pr_fmt() macro to prefix printouts and update copyright. Co-developed-by: Sakari Ailus Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki Tested-by: Sakari Ailus --- drivers/acpi/property.c | 102 ++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 20 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 6979a3f9f90a..07d76fb740b6 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -2,14 +2,17 @@ /* * ACPI device specific properties support. * - * Copyright (C) 2014, Intel Corporation + * Copyright (C) 2014 - 2023, Intel Corporation * All rights reserved. * * Authors: Mika Westerberg * Darren Hart * Rafael J. Wysocki + * Sakari Ailus */ +#define pr_fmt(fmt) "ACPI: " fmt + #include #include #include @@ -800,28 +803,16 @@ static int acpi_get_ref_args(struct fwnode_reference_args *args, { u32 nargs = 0, i; - /* - * Find the referred data extension node under the - * referred device node. - */ - for (; *element < end && (*element)->type == ACPI_TYPE_STRING; - (*element)++) { - const char *child_name = (*element)->string.pointer; - - ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name); - if (!ref_fwnode) - return -EINVAL; - } - /* * Assume the following integer elements are all args. Stop counting on - * the first reference or end of the package arguments. In case of - * neither reference, nor integer, return an error, we can't parse it. + * the first reference (possibly represented as a string) or end of the + * package arguments. In case of neither reference, nor integer, return + * an error, we can't parse it. */ for (i = 0; (*element) + i < end && i < num_args; i++) { acpi_object_type type = (*element)[i].type; - if (type == ACPI_TYPE_LOCAL_REFERENCE) + if (type == ACPI_TYPE_LOCAL_REFERENCE || type == ACPI_TYPE_STRING) break; if (type == ACPI_TYPE_INTEGER) @@ -845,6 +836,44 @@ static int acpi_get_ref_args(struct fwnode_reference_args *args, return 0; } +static struct fwnode_handle *acpi_parse_string_ref(const struct fwnode_handle *fwnode, + const char *refstring) +{ + acpi_handle scope, handle; + struct acpi_data_node *dn; + struct acpi_device *device; + acpi_status status; + + if (is_acpi_device_node(fwnode)) { + scope = to_acpi_device_node(fwnode)->handle; + } else if (is_acpi_data_node(fwnode)) { + scope = to_acpi_data_node(fwnode)->handle; + } else { + pr_debug("Bad node type for node %pfw\n", fwnode); + return NULL; + } + + status = acpi_get_handle(scope, refstring, &handle); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(scope, "Unable to get an ACPI handle for %s\n", + refstring); + return NULL; + } + + device = acpi_fetch_acpi_dev(handle); + if (device) + return acpi_fwnode_handle(device); + + status = acpi_get_data_full(handle, acpi_nondev_subnode_tag, + (void **)&dn, NULL); + if (ACPI_FAILURE(status) || !dn) { + acpi_handle_debug(handle, "Subnode not found\n"); + return NULL; + } + + return &dn->fwnode; +} + /** * __acpi_node_get_property_reference - returns handle to the referenced object * @fwnode: Firmware node to get the property from @@ -887,6 +916,7 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, const union acpi_object *element, *end; const union acpi_object *obj; const struct acpi_device_data *data; + struct fwnode_handle *ref_fwnode; struct acpi_device *device; int ret, idx = 0; @@ -910,16 +940,30 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, args->fwnode = acpi_fwnode_handle(device); args->nargs = 0; + + return 0; + case ACPI_TYPE_STRING: + if (index) + return -ENOENT; + + ref_fwnode = acpi_parse_string_ref(fwnode, obj->string.pointer); + if (!ref_fwnode) + return -EINVAL; + + args->fwnode = ref_fwnode; + args->nargs = 0; + return 0; case ACPI_TYPE_PACKAGE: /* * If it is not a single reference, then it is a package of - * references followed by number of ints as follows: + * references, followed by number of ints as follows: * * Package () { REF, INT, REF, INT, INT } * - * The index argument is then used to determine which reference - * the caller wants (along with the arguments). + * Here, REF may be either a local reference or a string. The + * index argument is then used to determine which reference the + * caller wants (along with the arguments). */ break; default: @@ -950,6 +994,24 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (idx == index) return 0; + break; + case ACPI_TYPE_STRING: + ref_fwnode = acpi_parse_string_ref(fwnode, + element->string.pointer); + if (!ref_fwnode) + return -EINVAL; + + element++; + + ret = acpi_get_ref_args(idx == index ? args : NULL, + ref_fwnode, &element, end, + num_args); + if (ret < 0) + return ret; + + if (idx == index) + return 0; + break; case ACPI_TYPE_INTEGER: if (idx == index) From bd721b934323e4dcde892013a97e0e5674f4c884 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Nov 2023 17:09:01 +0100 Subject: [PATCH 02/56] ACPI: scan: Extract CSI-2 connection graph from _CRS Find ACPI CSI-2 resource descriptors defined since ACPI 6.4 (for CSI-2 and camera configuration) in _CRS for all device objects in the given scope of the ACPI namespace that have them, identify the corresponding "remote endpoint" device objects for them and allocate memory for software nodes needed to create a DT-like data structure representing the CSI-2 connection graph for drivers. The code needed to populate these software nodes will be added by subsequent change sets. Link: https://uefi.org/specs/ACPI/6.5/06_Device_Configuration.html#camera-serial-interface-csi-2-connection-resource-descriptor Co-developed-by: Sakari Ailus Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki Tested-by: Sakari Ailus --- drivers/acpi/Makefile | 2 +- drivers/acpi/internal.h | 8 + drivers/acpi/mipi-disco-img.c | 292 ++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 48 ++++-- include/acpi/acpi_bus.h | 18 +++ 5 files changed, 358 insertions(+), 10 deletions(-) create mode 100644 drivers/acpi/mipi-disco-img.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index eaa09bf52f17..d367e649714f 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -37,7 +37,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o # ACPI Bus and Device Drivers # acpi-y += bus.o glue.o -acpi-y += scan.o +acpi-y += scan.o mipi-disco-img.o acpi-y += resource.o acpi-y += acpi_processor.o acpi-y += processor_core.o diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 866c7c4ed233..959a2bc61916 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -276,4 +276,12 @@ void acpi_init_lpit(void); static inline void acpi_init_lpit(void) { } #endif +/*-------------------------------------------------------------------------- + ACPI _CRS CSI-2 and MIPI DisCo for Imaging + -------------------------------------------------------------------------- */ + +void acpi_mipi_check_crs_csi2(acpi_handle handle); +void acpi_mipi_scan_crs_csi2(void); +void acpi_mipi_crs_csi2_cleanup(void); + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c new file mode 100644 index 000000000000..91281c8cb4f2 --- /dev/null +++ b/drivers/acpi/mipi-disco-img.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MIPI DisCo for Imaging support. + * + * Copyright (C) 2023 Intel Corporation + * + * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in + * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource + * Descriptor" of ACPI 6.5. + * + * The implementation looks for the information in the ACPI namespace (CSI-2 + * resource descriptors in _CRS) and constructs software nodes compatible with + * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2 + * connection graph. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +static LIST_HEAD(acpi_mipi_crs_csi2_list); + +static void acpi_mipi_data_tag(acpi_handle handle, void *context) +{ +} + +/* Connection data extracted from one _CRS CSI-2 resource descriptor. */ +struct crs_csi2_connection { + struct list_head entry; + struct acpi_resource_csi2_serialbus csi2_data; + acpi_handle remote_handle; + char remote_name[]; +}; + +/* Data extracted from _CRS CSI-2 resource descriptors for one device. */ +struct crs_csi2 { + struct list_head entry; + acpi_handle handle; + struct acpi_device_software_nodes *swnodes; + struct list_head connections; + u32 port_count; +}; + +struct csi2_resources_walk_data { + acpi_handle handle; + struct list_head connections; +}; + +static acpi_status parse_csi2_resource(struct acpi_resource *res, void *context) +{ + struct csi2_resources_walk_data *crwd = context; + struct acpi_resource_csi2_serialbus *csi2_res; + struct acpi_resource_source *csi2_res_src; + u16 csi2_res_src_length; + struct crs_csi2_connection *conn; + acpi_handle remote_handle; + + if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return AE_OK; + + csi2_res = &res->data.csi2_serial_bus; + + if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2) + return AE_OK; + + csi2_res_src = &csi2_res->resource_source; + if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr, + &remote_handle))) { + acpi_handle_debug(crwd->handle, + "unable to find resource source\n"); + return AE_OK; + } + csi2_res_src_length = csi2_res_src->string_length; + if (!csi2_res_src_length) { + acpi_handle_debug(crwd->handle, + "invalid resource source string length\n"); + return AE_OK; + } + + conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1), + GFP_KERNEL); + if (!conn) + return AE_OK; + + conn->csi2_data = *csi2_res; + strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length); + conn->csi2_data.resource_source.string_ptr = conn->remote_name; + conn->remote_handle = remote_handle; + + list_add(&conn->entry, &crwd->connections); + + return AE_OK; +} + +static struct crs_csi2 *acpi_mipi_add_crs_csi2(acpi_handle handle, + struct list_head *list) +{ + struct crs_csi2 *csi2; + + csi2 = kzalloc(sizeof(*csi2), GFP_KERNEL); + if (!csi2) + return NULL; + + csi2->handle = handle; + INIT_LIST_HEAD(&csi2->connections); + csi2->port_count = 1; + + if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2))) { + kfree(csi2); + return NULL; + } + + list_add(&csi2->entry, list); + + return csi2; +} + +static struct crs_csi2 *acpi_mipi_get_crs_csi2(acpi_handle handle) +{ + struct crs_csi2 *csi2; + + if (ACPI_FAILURE(acpi_get_data_full(handle, acpi_mipi_data_tag, + (void **)&csi2, NULL))) + return NULL; + + return csi2; +} + +static void csi_csr2_release_connections(struct list_head *list) +{ + struct crs_csi2_connection *conn, *conn_tmp; + + list_for_each_entry_safe(conn, conn_tmp, list, entry) { + list_del(&conn->entry); + kfree(conn); + } +} + +static void acpi_mipi_del_crs_csi2(struct crs_csi2 *csi2) +{ + list_del(&csi2->entry); + acpi_detach_data(csi2->handle, acpi_mipi_data_tag); + kfree(csi2->swnodes); + csi_csr2_release_connections(&csi2->connections); + kfree(csi2); +} + +/** + * acpi_mipi_check_crs_csi2 - Look for CSI-2 resources in _CRS + * @handle: Device object handle to evaluate _CRS for. + * + * Find all CSI-2 resource descriptors in the given device's _CRS + * and collect them into a list. + */ +void acpi_mipi_check_crs_csi2(acpi_handle handle) +{ + struct csi2_resources_walk_data crwd = { + .handle = handle, + .connections = LIST_HEAD_INIT(crwd.connections), + }; + struct crs_csi2 *csi2; + + /* + * Avoid allocating _CRS CSI-2 objects for devices without any CSI-2 + * resource descriptions in _CRS to reduce overhead. + */ + acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd); + if (list_empty(&crwd.connections)) + return; + + /* + * Create a _CRS CSI-2 entry to store the extracted connection + * information and add it to the global list. + */ + csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list); + if (!csi2) { + csi_csr2_release_connections(&crwd.connections); + return; /* Nothing really can be done about this. */ + } + + list_replace(&crwd.connections, &csi2->connections); +} + +#define NO_CSI2_PORT (UINT_MAX - 1) + +static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2) +{ + size_t port_count = csi2->port_count; + struct acpi_device_software_nodes *swnodes; + size_t alloc_size; + unsigned int i; + + /* + * Allocate memory for ports, node pointers (number of nodes + + * 1 (guardian), nodes (root + number of ports * 2 (because for + * every port there is an endpoint)). + */ + if (check_mul_overflow(sizeof(*swnodes->ports) + + sizeof(*swnodes->nodes) * 2 + + sizeof(*swnodes->nodeptrs) * 2, + port_count, &alloc_size) || + check_add_overflow(sizeof(*swnodes) + + sizeof(*swnodes->nodes) + + sizeof(*swnodes->nodeptrs) * 2, + alloc_size, &alloc_size)) { + acpi_handle_info(csi2->handle, + "too many _CRS CSI-2 resource handles (%zu)", + port_count); + return; + } + + swnodes = kmalloc(alloc_size, GFP_KERNEL); + if (!swnodes) + return; + + swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1); + swnodes->nodes = (struct software_node *)(swnodes->ports + port_count); + swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 + + 2 * port_count); + swnodes->num_ports = port_count; + + for (i = 0; i < 2 * port_count + 1; i++) + swnodes->nodeptrs[i] = &swnodes->nodes[i]; + + swnodes->nodeptrs[i] = NULL; + + for (i = 0; i < port_count; i++) + swnodes->ports[i].port_nr = NO_CSI2_PORT; + + csi2->swnodes = swnodes; +} + +/** + * acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes + * + * Note that this function must be called before any struct acpi_device objects + * are bound to any ACPI drivers or scan handlers, so it cannot assume the + * existence of struct acpi_device objects for every device present in the ACPI + * namespace. + * + * acpi_scan_lock in scan.c must be held when calling this function. + */ +void acpi_mipi_scan_crs_csi2(void) +{ + struct crs_csi2 *csi2; + LIST_HEAD(aux_list); + + /* Count references to each ACPI handle in the CSI-2 connection graph. */ + list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) { + struct crs_csi2_connection *conn; + + list_for_each_entry(conn, &csi2->connections, entry) { + struct crs_csi2 *remote_csi2; + + csi2->port_count++; + + remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle); + if (remote_csi2) { + remote_csi2->port_count++; + continue; + } + /* + * The remote endpoint has no _CRS CSI-2 list entry yet, + * so create one for it and add it to the list. + */ + acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list); + } + } + list_splice(&aux_list, &acpi_mipi_crs_csi2_list); + + /* Allocate software nodes for representing the CSI-2 information. */ + list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) + alloc_crs_csi2_swnodes(csi2); +} + +/** + * acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data + */ +void acpi_mipi_crs_csi2_cleanup(void) +{ + struct crs_csi2 *csi2, *csi2_tmp; + + list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) + acpi_mipi_del_crs_csi2(csi2); +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fa5dd71a80fa..8e12c558b8db 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1976,7 +1976,7 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) } } -static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) +static u32 acpi_scan_check_dep(acpi_handle handle) { struct acpi_handle_list dep_devices; acpi_status status; @@ -1989,8 +1989,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) * 2. ACPI nodes describing USB ports. * Still, checking for _HID catches more then just these cases ... */ - if (!check_dep || !acpi_has_method(handle, "_DEP") || - !acpi_has_method(handle, "_HID")) + if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID")) return 0; status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); @@ -2036,7 +2035,13 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) return count; } -static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, +static acpi_status acpi_scan_check_crs_csi2_cb(acpi_handle handle, u32 a, void *b, void **c) +{ + acpi_mipi_check_crs_csi2(handle); + return AE_OK; +} + +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass, struct acpi_device **adev_p) { struct acpi_device *device = acpi_fetch_acpi_dev(handle); @@ -2054,9 +2059,25 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, if (acpi_device_should_be_hidden(handle)) return AE_OK; - /* Bail out if there are dependencies. */ - if (acpi_scan_check_dep(handle, check_dep) > 0) - return AE_CTRL_DEPTH; + if (first_pass) { + acpi_mipi_check_crs_csi2(handle); + + /* Bail out if there are dependencies. */ + if (acpi_scan_check_dep(handle) > 0) { + /* + * The entire CSI-2 connection graph needs to be + * extracted before any drivers or scan handlers + * are bound to struct device objects, so scan + * _CRS CSI-2 resource descriptors for all + * devices below the current handle. + */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, + ACPI_UINT32_MAX, + acpi_scan_check_crs_csi2_cb, + NULL, NULL, NULL); + return AE_CTRL_DEPTH; + } + } fallthrough; case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ @@ -2079,10 +2100,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep, } /* - * If check_dep is true at this point, the device has no dependencies, + * If first_pass is true at this point, the device has no dependencies, * or the creation of the device object would have been postponed above. */ - acpi_add_single_object(&device, handle, type, !check_dep); + acpi_add_single_object(&device, handle, type, !first_pass); if (!device) return AE_CTRL_DEPTH; @@ -2494,12 +2515,21 @@ int acpi_bus_scan(acpi_handle handle) if (!device) return -ENODEV; + /* + * Allocate ACPI _CRS CSI-2 software nodes using information extracted + * from the _CRS CSI-2 resource descriptors during the ACPI namespace + * walk above. + */ + acpi_mipi_scan_crs_csi2(); + acpi_bus_attach(device, (void *)true); /* Pass 2: Enumerate all of the remaining devices. */ acpi_scan_postponed(); + acpi_mipi_crs_csi2_cleanup(); + return 0; } EXPORT_SYMBOL(acpi_bus_scan); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index afeed6e72049..f122fa1c10a8 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -366,6 +366,24 @@ struct acpi_device_data { struct acpi_gpio_mapping; +struct acpi_device_software_node_port { + unsigned int port_nr; +}; + +/** + * struct acpi_device_software_nodes - Software nodes for an ACPI device + * @nodes: Software nodes for root as well as ports and endpoints. + * @nodeprts: Array of software node pointers, for (un)registering them. + * @ports: Information related to each port and endpoint within a port. + * @num_ports: The number of ports. + */ +struct acpi_device_software_nodes { + struct software_node *nodes; + const struct software_node **nodeptrs; + struct acpi_device_software_node_port *ports; + unsigned int num_ports; +}; + /* Device */ struct acpi_device { u32 pld_crc; From 693c667b32ee1dd312000d4656b3383fffb3af2d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Nov 2023 17:16:26 +0100 Subject: [PATCH 03/56] ACPI: scan: Extract _CRS CSI-2 connection information into swnodes Use the connection information extracted from the _CRS CSI-2 resource descriptors for all devices that have them to populate port names and the "reg", "bus-type" and "remote-endpoint" properties in the software nodes representing the CSI-2 connection graph. Link: https://uefi.org/specs/ACPI/6.5/06_Device_Configuration.html#camera-serial-interface-csi-2-connection-resource-descriptor Co-developed-by: Sakari Ailus Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki Tested-by: Sakari Ailus --- drivers/acpi/mipi-disco-img.c | 153 +++++++++++++++++++++++++++++++++- include/acpi/acpi_bus.h | 53 ++++++++++++ 2 files changed, 205 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c index 91281c8cb4f2..5ff72d83fad2 100644 --- a/drivers/acpi/mipi-disco-img.c +++ b/drivers/acpi/mipi-disco-img.c @@ -23,6 +23,8 @@ #include #include +#include + #include "internal.h" static LIST_HEAD(acpi_mipi_crs_csi2_list); @@ -237,6 +239,142 @@ static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2) csi2->swnodes = swnodes; } +#define ACPI_CRS_CSI2_PHY_TYPE_C 0 +#define ACPI_CRS_CSI2_PHY_TYPE_D 1 + +static unsigned int next_csi2_port_index(struct acpi_device_software_nodes *swnodes, + unsigned int port_nr) +{ + unsigned int i; + + for (i = 0; i < swnodes->num_ports; i++) { + struct acpi_device_software_node_port *port = &swnodes->ports[i]; + + if (port->port_nr == port_nr) + return i; + + if (port->port_nr == NO_CSI2_PORT) { + port->port_nr = port_nr; + return i; + } + } + + return NO_CSI2_PORT; +} + +/* Print graph port name into a buffer, return non-zero on failure. */ +#define GRAPH_PORT_NAME(var, num) \ + (snprintf((var), sizeof(var), SWNODE_GRAPH_PORT_NAME_FMT, (num)) >= \ + sizeof(var)) + +static void extract_crs_csi2_conn_info(acpi_handle local_handle, + struct acpi_device_software_nodes *local_swnodes, + struct crs_csi2_connection *conn) +{ + struct crs_csi2 *remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle); + struct acpi_device_software_nodes *remote_swnodes; + struct acpi_device_software_node_port *local_port, *remote_port; + struct software_node *local_node, *remote_node; + unsigned int local_index, remote_index; + unsigned int bus_type; + + /* + * If the previous steps have failed to make room for a _CRS CSI-2 + * representation for the remote end of the given connection, skip it. + */ + if (!remote_csi2) + return; + + remote_swnodes = remote_csi2->swnodes; + if (!remote_swnodes) + return; + + switch (conn->csi2_data.phy_type) { + case ACPI_CRS_CSI2_PHY_TYPE_C: + bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; + break; + + case ACPI_CRS_CSI2_PHY_TYPE_D: + bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; + break; + + default: + acpi_handle_info(local_handle, "unknown CSI-2 PHY type %u\n", + conn->csi2_data.phy_type); + return; + } + + local_index = next_csi2_port_index(local_swnodes, + conn->csi2_data.local_port_instance); + if (WARN_ON_ONCE(local_index >= local_swnodes->num_ports)) + return; + + remote_index = next_csi2_port_index(remote_swnodes, + conn->csi2_data.resource_source.index); + if (WARN_ON_ONCE(remote_index >= remote_swnodes->num_ports)) + return; + + local_port = &local_swnodes->ports[local_index]; + local_node = &local_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(local_index)]; + local_port->crs_csi2_local = true; + + remote_port = &remote_swnodes->ports[remote_index]; + remote_node = &remote_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(remote_index)]; + + local_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(remote_node); + remote_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(local_node); + + local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] = + PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", + local_port->remote_ep); + + local_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] = + PROPERTY_ENTRY_U32("bus-type", bus_type); + + local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] = + PROPERTY_ENTRY_U32("reg", 0); + + local_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] = + PROPERTY_ENTRY_U32("reg", conn->csi2_data.local_port_instance); + + if (GRAPH_PORT_NAME(local_port->port_name, + conn->csi2_data.local_port_instance)) + acpi_handle_info(local_handle, "local port %u name too long", + conn->csi2_data.local_port_instance); + + remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] = + PROPERTY_ENTRY_REF_ARRAY("remote-endpoint", + remote_port->remote_ep); + + remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] = + PROPERTY_ENTRY_U32("bus-type", bus_type); + + remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] = + PROPERTY_ENTRY_U32("reg", 0); + + remote_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] = + PROPERTY_ENTRY_U32("reg", conn->csi2_data.resource_source.index); + + if (GRAPH_PORT_NAME(remote_port->port_name, + conn->csi2_data.resource_source.index)) + acpi_handle_info(local_handle, "remote port %u name too long", + conn->csi2_data.resource_source.index); +} + +static void prepare_crs_csi2_swnodes(struct crs_csi2 *csi2) +{ + struct acpi_device_software_nodes *local_swnodes = csi2->swnodes; + acpi_handle local_handle = csi2->handle; + struct crs_csi2_connection *conn; + + /* Bail out if the allocation of swnodes has failed. */ + if (!local_swnodes) + return; + + list_for_each_entry(conn, &csi2->connections, entry) + extract_crs_csi2_conn_info(local_handle, local_swnodes, conn); +} + /** * acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes * @@ -275,9 +413,22 @@ void acpi_mipi_scan_crs_csi2(void) } list_splice(&aux_list, &acpi_mipi_crs_csi2_list); - /* Allocate software nodes for representing the CSI-2 information. */ + /* + * Allocate software nodes for representing the CSI-2 information. + * + * This needs to be done for all of the list entries in one go, because + * they may point to each other without restrictions and the next step + * relies on the availability of swnodes memory for each list entry. + */ list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) alloc_crs_csi2_swnodes(csi2); + + /* + * Set up software node properties using data from _CRS CSI-2 resource + * descriptors. + */ + list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) + prepare_crs_csi2_swnodes(csi2); } /** diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f122fa1c10a8..a7fa24f1af46 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -366,8 +366,61 @@ struct acpi_device_data { struct acpi_gpio_mapping; +#define ACPI_DEVICE_CSI2_DATA_LANES 8 + +#define ACPI_DEVICE_SWNODE_PORT_NAME_LENGTH 8 + +enum acpi_device_swnode_port_props { + ACPI_DEVICE_SWNODE_PORT_REG, + ACPI_DEVICE_SWNODE_PORT_NUM_OF, + ACPI_DEVICE_SWNODE_PORT_NUM_ENTRIES +}; + +enum acpi_device_swnode_ep_props { + ACPI_DEVICE_SWNODE_EP_REMOTE_EP, + ACPI_DEVICE_SWNODE_EP_BUS_TYPE, + ACPI_DEVICE_SWNODE_EP_REG, + ACPI_DEVICE_SWNODE_EP_CLOCK_LANES, + ACPI_DEVICE_SWNODE_EP_DATA_LANES, + ACPI_DEVICE_SWNODE_EP_LANE_POLARITIES, + /* TX only */ + ACPI_DEVICE_SWNODE_EP_LINK_FREQUENCIES, + ACPI_DEVICE_SWNODE_EP_NUM_OF, + ACPI_DEVICE_SWNODE_EP_NUM_ENTRIES +}; + +/* + * Each device has a root software node plus two times as many nodes as the + * number of CSI-2 ports. + */ +#define ACPI_DEVICE_SWNODE_PORT(port) (2 * (port) + 1) +#define ACPI_DEVICE_SWNODE_EP(endpoint) \ + (ACPI_DEVICE_SWNODE_PORT(endpoint) + 1) + +/** + * struct acpi_device_software_node_port - MIPI DisCo for Imaging CSI-2 port + * @port_name: Port name. + * @data_lanes: "data-lanes" property values. + * @lane_polarities: "lane-polarities" property values. + * @link_frequencies: "link_frequencies" property values. + * @port_nr: Port number. + * @crs_crs2_local: _CRS CSI2 record present (i.e. this is a transmitter one). + * @port_props: Port properties. + * @ep_props: Endpoint properties. + * @remote_ep: Reference to the remote endpoint. + */ struct acpi_device_software_node_port { + char port_name[ACPI_DEVICE_SWNODE_PORT_NAME_LENGTH + 1]; + u32 data_lanes[ACPI_DEVICE_CSI2_DATA_LANES]; + u32 lane_polarities[ACPI_DEVICE_CSI2_DATA_LANES + 1 /* clock lane */]; + u64 link_frequencies[ACPI_DEVICE_CSI2_DATA_LANES]; unsigned int port_nr; + bool crs_csi2_local; + + struct property_entry port_props[ACPI_DEVICE_SWNODE_PORT_NUM_ENTRIES]; + struct property_entry ep_props[ACPI_DEVICE_SWNODE_EP_NUM_ENTRIES]; + + struct software_node_ref_args remote_ep[1]; }; /** From 48c9996f1dfe92bd7318472651c9ad538d6d53b5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 6 Nov 2023 17:16:37 +0100 Subject: [PATCH 04/56] device property: Add SOFTWARE_NODE() macro for defining software nodes Add SOFTWARE_NODE() macro in order to make defining software nodes look nicer. This is analogous to different PROPERTY_ENTRY_*() macros for defining properties. Signed-off-by: Sakari Ailus Reviewed-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Tested-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki --- include/linux/property.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/property.h b/include/linux/property.h index 9f2585d705a8..97f901c0914e 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -489,6 +489,13 @@ struct software_node { const struct property_entry *properties; }; +#define SOFTWARE_NODE(_name_, _properties_, _parent_) \ + (struct software_node) { \ + .name = _name_, \ + .properties = _properties_, \ + .parent = _parent_, \ + } + bool is_software_node(const struct fwnode_handle *fwnode); const struct software_node * to_software_node(const struct fwnode_handle *fwnode); From a6cb0a611273767683d50fb908173b6f88052ce5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Nov 2023 20:19:42 +0100 Subject: [PATCH 05/56] ACPI: scan: Extract MIPI DisCo for Imaging data into swnodes Add information extracted from the MIPI DisCo for Imaging device properties to software nodes created during the CSI-2 connection graph discovery. Link: https://www.mipi.org/specifications/mipi-disco-imaging Co-developed-by: Sakari Ailus Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki Tested-by: Sakari Ailus --- drivers/acpi/internal.h | 1 + drivers/acpi/mipi-disco-img.c | 252 +++++++++++++++++++++++++++++++++- drivers/acpi/scan.c | 12 +- include/acpi/acpi_bus.h | 17 +++ 4 files changed, 278 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 959a2bc61916..86b670637f2f 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -282,6 +282,7 @@ static inline void acpi_init_lpit(void) { } void acpi_mipi_check_crs_csi2(acpi_handle handle); void acpi_mipi_scan_crs_csi2(void); +void acpi_mipi_init_crs_csi2_swnodes(void); void acpi_mipi_crs_csi2_cleanup(void); #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c index 5ff72d83fad2..dcbaba91d4fe 100644 --- a/drivers/acpi/mipi-disco-img.c +++ b/drivers/acpi/mipi-disco-img.c @@ -6,12 +6,16 @@ * * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource - * Descriptor" of ACPI 6.5. + * Descriptor" of ACPI 6.5 and using device properties defined by the MIPI DisCo + * for Imaging specification. * * The implementation looks for the information in the ACPI namespace (CSI-2 * resource descriptors in _CRS) and constructs software nodes compatible with * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2 - * connection graph. + * connection graph. The software nodes are then populated with the data + * extracted from the _CRS CSI-2 resource descriptors and the MIPI DisCo + * for Imaging device properties present in _DSD for the ACPI device objects + * with CSI-2 connections. */ #include @@ -431,6 +435,250 @@ void acpi_mipi_scan_crs_csi2(void) prepare_crs_csi2_swnodes(csi2); } +/* + * Get the index of the next property in the property array, with a given + * maximum value. + */ +#define NEXT_PROPERTY(index, max) \ + (WARN_ON((index) > ACPI_DEVICE_SWNODE_##max) ? \ + ACPI_DEVICE_SWNODE_##max : (index)++) + +static void init_csi2_port_local(struct acpi_device *adev, + struct acpi_device_software_node_port *port, + struct fwnode_handle *port_fwnode, + unsigned int index) +{ + acpi_handle handle = acpi_device_handle(adev); + unsigned int num_link_freqs; + int ret; + + ret = fwnode_property_count_u64(port_fwnode, "mipi-img-link-frequencies"); + if (ret <= 0) + return; + + num_link_freqs = ret; + if (num_link_freqs > ACPI_DEVICE_CSI2_DATA_LANES) { + acpi_handle_info(handle, "Too many link frequencies: %u\n", + num_link_freqs); + num_link_freqs = ACPI_DEVICE_CSI2_DATA_LANES; + } + + ret = fwnode_property_read_u64_array(port_fwnode, + "mipi-img-link-frequencies", + port->link_frequencies, + num_link_freqs); + if (ret) { + acpi_handle_info(handle, "Unable to get link frequencies (%d)\n", + ret); + return; + } + + port->ep_props[NEXT_PROPERTY(index, EP_LINK_FREQUENCIES)] = + PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies", + port->link_frequencies, + num_link_freqs); +} + +static void init_csi2_port(struct acpi_device *adev, + struct acpi_device_software_nodes *swnodes, + struct acpi_device_software_node_port *port, + struct fwnode_handle *port_fwnode, + unsigned int port_index) +{ + unsigned int ep_prop_index = ACPI_DEVICE_SWNODE_EP_CLOCK_LANES; + acpi_handle handle = acpi_device_handle(adev); + u8 val[ACPI_DEVICE_CSI2_DATA_LANES]; + int num_lanes = 0; + int ret; + + if (GRAPH_PORT_NAME(port->port_name, port->port_nr)) + return; + + swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)] = + SOFTWARE_NODE(port->port_name, port->port_props, + &swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT]); + + ret = fwnode_property_read_u8(port_fwnode, "mipi-img-clock-lane", val); + if (!ret) + port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_CLOCK_LANES)] = + PROPERTY_ENTRY_U32("clock-lanes", val[0]); + + ret = fwnode_property_count_u8(port_fwnode, "mipi-img-data-lanes"); + if (ret > 0) { + num_lanes = ret; + + if (num_lanes > ACPI_DEVICE_CSI2_DATA_LANES) { + acpi_handle_info(handle, "Too many data lanes: %u\n", + num_lanes); + num_lanes = ACPI_DEVICE_CSI2_DATA_LANES; + } + + ret = fwnode_property_read_u8_array(port_fwnode, + "mipi-img-data-lanes", + val, num_lanes); + if (!ret) { + unsigned int i; + + for (i = 0; i < num_lanes; i++) + port->data_lanes[i] = val[i]; + + port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_DATA_LANES)] = + PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes", + port->data_lanes, + num_lanes); + } + } + + ret = fwnode_property_count_u8(port_fwnode, "mipi-img-lane-polarities"); + if (ret < 0) { + acpi_handle_debug(handle, "Lane polarity bytes missing\n"); + } else if (ret * BITS_PER_TYPE(u8) < num_lanes + 1) { + acpi_handle_info(handle, "Too few lane polarity bytes (%lu vs. %d)\n", + ret * BITS_PER_TYPE(u8), num_lanes + 1); + } else { + unsigned long mask = 0; + int byte_count = ret; + unsigned int i; + + /* + * The total number of lanes is ACPI_DEVICE_CSI2_DATA_LANES + 1 + * (data lanes + clock lane). It is not expected to ever be + * greater than the number of bits in an unsigned long + * variable, but ensure that this is the case. + */ + BUILD_BUG_ON(BITS_PER_TYPE(unsigned long) <= ACPI_DEVICE_CSI2_DATA_LANES); + + if (byte_count > sizeof(mask)) { + acpi_handle_info(handle, "Too many lane polarities: %d\n", + byte_count); + byte_count = sizeof(mask); + } + fwnode_property_read_u8_array(port_fwnode, "mipi-img-lane-polarities", + val, byte_count); + + for (i = 0; i < byte_count; i++) + mask |= (unsigned long)val[i] << BITS_PER_TYPE(u8) * i; + + for (i = 0; i <= num_lanes; i++) + port->lane_polarities[i] = test_bit(i, &mask); + + port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_LANE_POLARITIES)] = + PROPERTY_ENTRY_U32_ARRAY_LEN("lane-polarities", + port->lane_polarities, + num_lanes + 1); + } + + swnodes->nodes[ACPI_DEVICE_SWNODE_EP(port_index)] = + SOFTWARE_NODE("endpoint@0", swnodes->ports[port_index].ep_props, + &swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)]); + + if (port->crs_csi2_local) + init_csi2_port_local(adev, port, port_fwnode, ep_prop_index); +} + +#define MIPI_IMG_PORT_PREFIX "mipi-img-port-" + +static struct fwnode_handle *get_mipi_port_handle(struct fwnode_handle *adev_fwnode, + unsigned int port_nr) +{ + char port_name[sizeof(MIPI_IMG_PORT_PREFIX) + 2]; + + if (snprintf(port_name, sizeof(port_name), "%s%u", + MIPI_IMG_PORT_PREFIX, port_nr) >= sizeof(port_name)) + return NULL; + + return fwnode_get_named_child_node(adev_fwnode, port_name); +} + +static void init_crs_csi2_swnodes(struct crs_csi2 *csi2) +{ + struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER }; + struct acpi_device_software_nodes *swnodes = csi2->swnodes; + acpi_handle handle = csi2->handle; + struct fwnode_handle *adev_fwnode; + struct acpi_device *adev; + acpi_status status; + unsigned int i; + int ret; + + /* + * Bail out if the swnodes are not available (either they have not been + * allocated or they have been assigned to the device already). + */ + if (!swnodes) + return; + + adev = acpi_fetch_acpi_dev(handle); + if (!adev) + return; + + adev_fwnode = acpi_fwnode_handle(adev); + + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_info(handle, "Unable to get the path name\n"); + return; + } + + swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT] = + SOFTWARE_NODE(buffer.pointer, swnodes->dev_props, NULL); + + for (i = 0; i < swnodes->num_ports; i++) { + struct acpi_device_software_node_port *port = &swnodes->ports[i]; + struct fwnode_handle *port_fwnode; + + /* + * The MIPI DisCo for Imaging specification defines _DSD device + * properties for providing CSI-2 port parameters that can be + * accessed through the generic device properties framework. To + * access them, it is first necessary to find the data node + * representing the port under the given ACPI device object. + */ + port_fwnode = get_mipi_port_handle(adev_fwnode, port->port_nr); + if (!port_fwnode) { + acpi_handle_info(handle, + "MIPI port name too long for port %u\n", + port->port_nr); + continue; + } + + init_csi2_port(adev, swnodes, port, port_fwnode, i); + + fwnode_handle_put(port_fwnode); + } + + ret = software_node_register_node_group(swnodes->nodeptrs); + if (ret < 0) { + acpi_handle_info(handle, + "Unable to register software nodes (%d)\n", ret); + return; + } + + adev->swnodes = swnodes; + adev_fwnode->secondary = software_node_fwnode(swnodes->nodes); + + /* + * Prevents the swnodes from this csi2 entry from being assigned again + * or freed prematurely. + */ + csi2->swnodes = NULL; +} + +/** + * acpi_mipi_init_crs_csi2_swnodes - Initialize _CRS CSI-2 software nodes + * + * Use MIPI DisCo for Imaging device properties to finalize the initialization + * of CSI-2 software nodes for all ACPI device objects that have been already + * enumerated. + */ +void acpi_mipi_init_crs_csi2_swnodes(void) +{ + struct crs_csi2 *csi2, *csi2_tmp; + + list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) + init_crs_csi2_swnodes(csi2); +} + /** * acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8e12c558b8db..b8df04779904 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2447,6 +2447,13 @@ static void acpi_scan_postponed_branch(acpi_handle handle) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add_2, NULL, NULL, (void **)&adev); + + /* + * Populate the ACPI _CRS CSI-2 software nodes for the ACPI devices that + * have been added above. + */ + acpi_mipi_init_crs_csi2_swnodes(); + acpi_bus_attach(adev, NULL); } @@ -2516,11 +2523,12 @@ int acpi_bus_scan(acpi_handle handle) return -ENODEV; /* - * Allocate ACPI _CRS CSI-2 software nodes using information extracted + * Set up ACPI _CRS CSI-2 software nodes using information extracted * from the _CRS CSI-2 resource descriptors during the ACPI namespace - * walk above. + * walk above and MIPI DisCo for Imaging device properties. */ acpi_mipi_scan_crs_csi2(); + acpi_mipi_init_crs_csi2_swnodes(); acpi_bus_attach(device, (void *)true); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a7fa24f1af46..c299fb974e49 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -366,10 +366,24 @@ struct acpi_device_data { struct acpi_gpio_mapping; +#define ACPI_DEVICE_SWNODE_ROOT 0 + +/* + * The maximum expected number of CSI-2 data lanes. + * + * This number is not expected to ever have to be equal to or greater than the + * number of bits in an unsigned long variable, but if it needs to be increased + * above that limit, code will need to be adjusted accordingly. + */ #define ACPI_DEVICE_CSI2_DATA_LANES 8 #define ACPI_DEVICE_SWNODE_PORT_NAME_LENGTH 8 +enum acpi_device_swnode_dev_props { + ACPI_DEVICE_SWNODE_DEV_NUM_OF, + ACPI_DEVICE_SWNODE_DEV_NUM_ENTRIES +}; + enum acpi_device_swnode_port_props { ACPI_DEVICE_SWNODE_PORT_REG, ACPI_DEVICE_SWNODE_PORT_NUM_OF, @@ -425,12 +439,14 @@ struct acpi_device_software_node_port { /** * struct acpi_device_software_nodes - Software nodes for an ACPI device + * @dev_props: Device properties. * @nodes: Software nodes for root as well as ports and endpoints. * @nodeprts: Array of software node pointers, for (un)registering them. * @ports: Information related to each port and endpoint within a port. * @num_ports: The number of ports. */ struct acpi_device_software_nodes { + struct property_entry dev_props[ACPI_DEVICE_SWNODE_DEV_NUM_ENTRIES]; struct software_node *nodes; const struct software_node **nodeptrs; struct acpi_device_software_node_port *ports; @@ -455,6 +471,7 @@ struct acpi_device { struct acpi_device_data data; struct acpi_scan_handler *handler; struct acpi_hotplug_context *hp; + struct acpi_device_software_nodes *swnodes; const struct acpi_gpio_mapping *driver_gpios; void *driver_data; struct device dev; From f533e43a2a3117cc59886cbcd66ca32e42cf1ea9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 6 Nov 2023 17:28:40 +0100 Subject: [PATCH 06/56] ACPI: property: Dig "rotation" property for devices with CSI2 _CRS Find the "rotation" property value for devices with _CRS CSI-2 resource descriptors and use it to add the "rotation" property to the software nodes representing the CSI-2 connection graph. That value typically comes from the _PLD (Physical Location of Device) object if it is present for the given device. This way, camera sensor drivers that know the "rotation" property do not need to care about _PLD on systems using ACPI. Signed-off-by: Sakari Ailus [ rjw: Changelog edits, file rename ] Signed-off-by: Rafael J. Wysocki Tested-by: Sakari Ailus --- drivers/acpi/mipi-disco-img.c | 17 +++++++++++++++++ include/acpi/acpi_bus.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c index dcbaba91d4fe..b2c7a4922804 100644 --- a/drivers/acpi/mipi-disco-img.c +++ b/drivers/acpi/mipi-disco-img.c @@ -595,6 +595,7 @@ static void init_crs_csi2_swnodes(struct crs_csi2 *csi2) struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER }; struct acpi_device_software_nodes *swnodes = csi2->swnodes; acpi_handle handle = csi2->handle; + unsigned int prop_index = 0; struct fwnode_handle *adev_fwnode; struct acpi_device *adev; acpi_status status; @@ -614,6 +615,22 @@ static void init_crs_csi2_swnodes(struct crs_csi2 *csi2) adev_fwnode = acpi_fwnode_handle(adev); + /* + * If the "rotation" property is not present, but _PLD is there, + * evaluate it to get the "rotation" value. + */ + if (!fwnode_property_present(adev_fwnode, "rotation")) { + struct acpi_pld_info *pld; + + status = acpi_get_physical_device_location(handle, &pld); + if (ACPI_SUCCESS(status)) { + swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_ROTATION)] = + PROPERTY_ENTRY_U32("rotation", + pld->rotation * 45U); + kfree(pld); + } + } + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); if (ACPI_FAILURE(status)) { acpi_handle_info(handle, "Unable to get the path name\n"); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c299fb974e49..1e5d2e2c3444 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -380,6 +380,7 @@ struct acpi_gpio_mapping; #define ACPI_DEVICE_SWNODE_PORT_NAME_LENGTH 8 enum acpi_device_swnode_dev_props { + ACPI_DEVICE_SWNODE_DEV_ROTATION, ACPI_DEVICE_SWNODE_DEV_NUM_OF, ACPI_DEVICE_SWNODE_DEV_NUM_ENTRIES }; From 4cd57d6d527c3570827a6eb8bd790ae216a78ed9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 6 Nov 2023 17:31:18 +0100 Subject: [PATCH 07/56] ACPI: property: Replicate DT-aligned u32 properties from DisCo for Imaging MIPI DisCo for Imaging defines properties for camera sensors that functionally align with DT equivalents. Replicate these properties in the ACPI device swnodes so the code using the corresponding DT properties already does not need to be updated to deal with their MIPI counterparts directly. The replicated properties are: "mipi-img-clock-frequency" -> "clock-frequency" "mipi-img-led-max-current" -> "led-max-microamp" "mipi-img-flash-max-current" -> "flash-max-microamp" "mipi-img-flash-max-timeout" -> "flash-max-timeout-us" Signed-off-by: Sakari Ailus [ rjw: Changelog edits, removal of redundant braces ] Signed-off-by: Rafael J. Wysocki Tested-by: Sakari Ailus --- drivers/acpi/mipi-disco-img.c | 17 +++++++++++++++++ include/acpi/acpi_bus.h | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c index b2c7a4922804..cad72d1fc127 100644 --- a/drivers/acpi/mipi-disco-img.c +++ b/drivers/acpi/mipi-disco-img.c @@ -600,6 +600,7 @@ static void init_crs_csi2_swnodes(struct crs_csi2 *csi2) struct acpi_device *adev; acpi_status status; unsigned int i; + u32 val; int ret; /* @@ -631,6 +632,22 @@ static void init_crs_csi2_swnodes(struct crs_csi2 *csi2) } } + if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-clock-frequency", &val)) + swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_CLOCK_FREQUENCY)] = + PROPERTY_ENTRY_U32("clock-frequency", val); + + if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-led-max-current", &val)) + swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_LED_MAX_MICROAMP)] = + PROPERTY_ENTRY_U32("led-max-microamp", val); + + if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-current", &val)) + swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_MICROAMP)] = + PROPERTY_ENTRY_U32("flash-max-microamp", val); + + if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-timeout-us", &val)) + swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_TIMEOUT_US)] = + PROPERTY_ENTRY_U32("flash-max-timeout-us", val); + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); if (ACPI_FAILURE(status)) { acpi_handle_info(handle, "Unable to get the path name\n"); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 1e5d2e2c3444..989ea623b1c2 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -381,6 +381,10 @@ struct acpi_gpio_mapping; enum acpi_device_swnode_dev_props { ACPI_DEVICE_SWNODE_DEV_ROTATION, + ACPI_DEVICE_SWNODE_DEV_CLOCK_FREQUENCY, + ACPI_DEVICE_SWNODE_DEV_LED_MAX_MICROAMP, + ACPI_DEVICE_SWNODE_DEV_FLASH_MAX_MICROAMP, + ACPI_DEVICE_SWNODE_DEV_FLASH_MAX_TIMEOUT_US, ACPI_DEVICE_SWNODE_DEV_NUM_OF, ACPI_DEVICE_SWNODE_DEV_NUM_ENTRIES }; From 52304886ea49ee662589aff05925ef226c17a6a6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 26 Oct 2023 15:53:03 +0200 Subject: [PATCH 08/56] ACPI: video: Add comment about acpi_video_backlight_use_native() usage Add a comment explaining that acpi_video_backlight_use_native() MUST only be used by GPU drivers and that it must NOT be used on other places. Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- include/acpi/video.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/acpi/video.h b/include/acpi/video.h index 4230392b5b0b..3d538d4178ab 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -75,6 +75,15 @@ static inline enum acpi_backlight_type acpi_video_get_backlight_type(void) return __acpi_video_get_backlight_type(false, NULL); } +/* + * This function MUST only be called by GPU drivers to check if the driver + * should register a backlight class device. This function not only checks + * if a GPU native backlight device should be registered it *also* tells + * the ACPI video-detect code that native GPU backlight control is available. + * Therefor calling this from any place other then the GPU driver is wrong! + * To check if GPU native backlight control is used in other places instead use: + * if (acpi_video_get_backlight_type() == acpi_backlight_native) { ... } + */ static inline bool acpi_video_backlight_use_native(void) { return __acpi_video_get_backlight_type(true, NULL) == acpi_backlight_native; From c7add369b4cc599db336ca67578a052c5b0f0891 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 15 Nov 2023 18:48:11 +0100 Subject: [PATCH 09/56] ACPI: video: Drop should_check_lcd_flag() Since commit 3dbc80a3e4c5 ("ACPI: video: Make backlight class device registration a separate step (v2)") acpi_video# backlights are no longer automatically registered. Instead they now only get registered when the GPU/KMS driver calls acpi_video_register_backlight() which it only does when it has detected an internal LCD panel. This fixes the issue of sometimes a non-working acpi_video# backlight showing up on Desktops / HDMI-sticks without an internal LCD display in a more complete and robust manner then the LCD flag check which gets enabled by the should_check_lcd_flag() helper does. Therefor the should_check_lcd_flag() helper is no longer necessary. The lcd_only flag itself is still necessary to only register a single backlight device (for the right output) on the ESPRIMO Mobile M9410 which has 2 ACPI video connector nodes with a _BCM control method, which is the issue for which the flag was originally introduced in commit e50b9be14ab0 ("ACPI / video: only register backlight for LCD device"). Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_video.c | 56 +-------------------------------------- 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index d321ca7160d9..5eded14f8853 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -67,7 +67,7 @@ MODULE_PARM_DESC(hw_changes_brightness, static bool device_id_scheme = false; module_param(device_id_scheme, bool, 0444); -static int only_lcd = -1; +static int only_lcd; module_param(only_lcd, int, 0444); static bool may_report_brightness_keys; @@ -2141,57 +2141,6 @@ static int __init intel_opregion_present(void) return opregion; } -/* Check if the chassis-type indicates there is no builtin LCD panel */ -static bool dmi_is_desktop(void) -{ - const char *chassis_type; - unsigned long type; - - chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); - if (!chassis_type) - return false; - - if (kstrtoul(chassis_type, 10, &type) != 0) - return false; - - switch (type) { - case 0x03: /* Desktop */ - case 0x04: /* Low Profile Desktop */ - case 0x05: /* Pizza Box */ - case 0x06: /* Mini Tower */ - case 0x07: /* Tower */ - case 0x10: /* Lunch Box */ - case 0x11: /* Main Server Chassis */ - return true; - } - - return false; -} - -/* - * We're seeing a lot of bogus backlight interfaces on newer machines - * without a LCD such as desktops, servers and HDMI sticks. Checking the - * lcd flag fixes this, enable this by default on any machines which are: - * 1. Win8 ready (where we also prefer the native backlight driver, so - * normally the acpi_video code should not register there anyways); *and* - * 2.1 Report a desktop/server DMI chassis-type, or - * 2.2 Are an ACPI-reduced-hardware platform (and thus won't use the EC for - backlight control) - */ -static bool should_check_lcd_flag(void) -{ - if (!acpi_osi_is_win8()) - return false; - - if (dmi_is_desktop()) - return true; - - if (acpi_reduced_hardware()) - return true; - - return false; -} - int acpi_video_register(void) { int ret = 0; @@ -2205,9 +2154,6 @@ int acpi_video_register(void) goto leave; } - if (only_lcd == -1) - only_lcd = should_check_lcd_flag(); - dmi_check_system(video_dmi_table); ret = acpi_bus_register_driver(&acpi_video_bus); From 6d392d8daa7514a431678521c2af8be10fc31bc1 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 14 Nov 2023 14:06:11 +0200 Subject: [PATCH 10/56] ACPI: Run USB4 _OSC() first with query bit set The platform can deny certain tunneling from the OS and it does that by clearing the control bits it does not want the OS to get and returning with OSC_CAPABILITIES_MASK_ERROR bit set. Currently we do not handle this properly so if this happens, for example when the platform denies PCIe tunneling, we just fail the whole negotiation and revert back to what the Thunderbolt driver is doing to figure out whether the controller is running firmware connection manager or not. However, we should honor what the platform returns. For this reason run the USB4 _OSC() first with query bit set, and then use the returned control double word (that may contain some of the bits cleared by the platform) and run it second time with query bit clear. While there, remove an extra space from the assignment of the control double word. Reported-by: NaamaX Shachar Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 72e64c0718c9..569bd15f211b 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -408,7 +408,7 @@ static void acpi_bus_decode_usb_osc(const char *msg, u32 bits) static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; static void acpi_bus_osc_negotiate_usb_control(void) { - u32 capbuf[3]; + u32 capbuf[3], *capbuf_ret; struct acpi_osc_context context = { .uuid_str = sb_usb_uuid_str, .rev = 1, @@ -428,7 +428,12 @@ static void acpi_bus_osc_negotiate_usb_control(void) control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING | OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN; - capbuf[OSC_QUERY_DWORD] = 0; + /* + * Run _OSC first with query bit set, trying to get control over + * all tunneling. The platform can then clear out bits in the + * control dword that it does not want to grant to the OS. + */ + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; capbuf[OSC_SUPPORT_DWORD] = 0; capbuf[OSC_CONTROL_DWORD] = control; @@ -441,8 +446,29 @@ static void acpi_bus_osc_negotiate_usb_control(void) goto out_free; } + /* + * Run _OSC again now with query bit clear and the control dword + * matching what the platform granted (which may not have all + * the control bits set). + */ + capbuf_ret = context.ret.pointer; + + capbuf[OSC_QUERY_DWORD] = 0; + capbuf[OSC_CONTROL_DWORD] = capbuf_ret[OSC_CONTROL_DWORD]; + + kfree(context.ret.pointer); + + status = acpi_run_osc(handle, &context); + if (ACPI_FAILURE(status)) + return; + + if (context.ret.length != sizeof(capbuf)) { + pr_info("USB4 _OSC: returned invalid length buffer\n"); + goto out_free; + } + osc_sb_native_usb4_control = - control & acpi_osc_ctx_get_pci_control(&context); + control & acpi_osc_ctx_get_pci_control(&context); acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control); acpi_bus_decode_usb_osc("USB4 _OSC: OS controls", From f47507988145185aef5d0e7a0e28dbf6e7776f29 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Oct 2023 22:05:23 +0200 Subject: [PATCH 11/56] thermal: ACPI: Move the ACPI thermal library to drivers/acpi/ The ACPI thermal library contains functions that can be used to retrieve trip point temperature values through the platform firmware for various types of trip points. Each of these functions basically evaluates a specific ACPI object, checks if the value produced by it is reasonable and returns it (or THERMAL_TEMP_INVALID if anything fails). It made sense to hold it in drivers/thermal/ so long as it was only used by the code in that directory, but since it is also going to be used by the ACPI thermal driver located in drivers/acpi/, move it to the latter in order to keep the code related to evaluating ACPI objects defined in the specification proper together. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 5 +++++ drivers/acpi/Makefile | 1 + drivers/{thermal/thermal_acpi.c => acpi/thermal_lib.c} | 0 drivers/thermal/Kconfig | 4 ---- drivers/thermal/Makefile | 1 - drivers/thermal/intel/Kconfig | 2 +- drivers/thermal/intel/int340x_thermal/Kconfig | 2 +- include/linux/acpi.h | 7 +++++++ include/linux/thermal.h | 7 ------- 9 files changed, 15 insertions(+), 14 deletions(-) rename drivers/{thermal/thermal_acpi.c => acpi/thermal_lib.c} (100%) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index f819e760ff19..6f2bfcf7645c 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -61,6 +61,10 @@ config ACPI_CCA_REQUIRED config ACPI_TABLE_LIB bool +config ACPI_THERMAL_LIB + depends on THERMAL + bool + config ACPI_DEBUGGER bool "AML debugger interface" select ACPI_DEBUG @@ -327,6 +331,7 @@ config ACPI_THERMAL tristate "Thermal Zone" depends on ACPI_PROCESSOR select THERMAL + select ACPI_THERMAL_LIB default y help This driver supports ACPI thermal zones. Most mobile and diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index eaa09bf52f17..2a52083704e6 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_ACPI_TAD) += acpi_tad.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI) += container.o +obj-$(CONFIG_ACPI_THERMAL_LIB) += thermal_lib.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-$(CONFIG_ACPI_PLATFORM_PROFILE) += platform_profile.o obj-$(CONFIG_ACPI_NFIT) += nfit/ diff --git a/drivers/thermal/thermal_acpi.c b/drivers/acpi/thermal_lib.c similarity index 100% rename from drivers/thermal/thermal_acpi.c rename to drivers/acpi/thermal_lib.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index c81a00fbca7d..59883502eff4 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -76,10 +76,6 @@ config THERMAL_OF Say 'Y' here if you need to build thermal infrastructure based on device tree. -config THERMAL_ACPI - depends on ACPI - bool - config THERMAL_WRITABLE_TRIPS bool "Enable writable trip points" help diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index c934cab309ae..a8318d671036 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -13,7 +13,6 @@ thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o # interface to/from other layers providing sensors thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o -thermal_sys-$(CONFIG_THERMAL_ACPI) += thermal_acpi.o # governors CFLAGS_gov_power_allocator.o := -I$(src) diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig index ecd7e07eece0..b43953b5539f 100644 --- a/drivers/thermal/intel/Kconfig +++ b/drivers/thermal/intel/Kconfig @@ -85,7 +85,7 @@ config INTEL_BXT_PMIC_THERMAL config INTEL_PCH_THERMAL tristate "Intel PCH Thermal Reporting Driver" depends on X86 && PCI - select THERMAL_ACPI if ACPI + select ACPI_THERMAL_LIB if ACPI help Enable this to support thermal reporting on certain intel PCHs. Thermal reporting device will provide temperature reading, diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig index 300ea53e9b33..e76b13e44d03 100644 --- a/drivers/thermal/intel/int340x_thermal/Kconfig +++ b/drivers/thermal/intel/int340x_thermal/Kconfig @@ -9,7 +9,7 @@ config INT340X_THERMAL select THERMAL_GOV_USER_SPACE select ACPI_THERMAL_REL select ACPI_FAN - select THERMAL_ACPI + select ACPI_THERMAL_LIB select INTEL_SOC_DTS_IOSF_CORE select INTEL_TCC select PROC_THERMAL_MMIO_RAPL if POWERCAP diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 54189e0e5f41..b63d7811c728 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -424,6 +424,13 @@ extern int acpi_blacklisted(void); extern void acpi_osi_setup(char *str); extern bool acpi_osi_is_win8(void); +#ifdef CONFIG_ACPI_THERMAL_LIB +int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp); +int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp); +int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp); +int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp); +#endif + #ifdef CONFIG_ACPI_NUMA int acpi_map_pxm_to_node(int pxm); int acpi_get_node(acpi_handle handle); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index cee814d5d1ac..35f620059456 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -294,13 +294,6 @@ int thermal_zone_get_num_trips(struct thermal_zone_device *tz); int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp); -#ifdef CONFIG_THERMAL_ACPI -int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp); -int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp); -int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp); -int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp); -#endif - #ifdef CONFIG_THERMAL struct thermal_zone_device *thermal_zone_device_register_with_trips( const char *type, From 6908097aa5a7bd0c66c0b7ae9dd994b6ef62be8c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Oct 2023 22:06:52 +0200 Subject: [PATCH 12/56] ACPI: thermal_lib: Add functions returning temperature in deci-Kelvin Because the ACPI thermal driver generally operates temperature values in deci-Kelvin, it needs the library functions returning temperature for various trip point types to use deci-Kelvin too. To address that, arrange the ACPI thermal library code in three levels of functions where the high-level ones will return temperature in milli-Celsius, as needed by the thermal core and the majority of thermal drivers, the mid-level ones will return temperature in deci-Kelvin and will be called internally by the corresponding high- level functions, and all of the mid-level functions will call the same low-level one, acpi_trip_temp(), to actually evaluate ACPI objects to retrieve themperature values from the platform firmware. Going forward, this will allow the ACPI thermal driver to use the mid-level functions to provide temperature values needed by it, so as to reduce code duplication related to evaluating trip temperature ACPI control methods. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal_lib.c | 75 ++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/thermal_lib.c b/drivers/acpi/thermal_lib.c index 43eaf0f2ff49..02bf570141af 100644 --- a/drivers/acpi/thermal_lib.c +++ b/drivers/acpi/thermal_lib.c @@ -3,8 +3,8 @@ * Copyright 2023 Linaro Limited * Copyright 2023 Intel Corporation * - * Library routines for populating a generic thermal trip point structure - * with data obtained by evaluating a specific object in the ACPI Namespace. + * Library routines for retrieving trip point temperature values from the + * platform firmware via ACPI. */ #include #include @@ -17,11 +17,11 @@ * firmware. Any values out of these boundaries may be considered * bogus and we can assume the firmware has no data to provide. */ -#define TEMP_MIN_DECIK 2180 -#define TEMP_MAX_DECIK 4480 +#define TEMP_MIN_DECIK 2180ULL +#define TEMP_MAX_DECIK 4480ULL -static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name, - int *ret_temp) +static int acpi_trip_temp(struct acpi_device *adev, char *obj_name, + int *ret_temp) { unsigned long long temp; acpi_status status; @@ -33,7 +33,7 @@ static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name, } if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) { - *ret_temp = deci_kelvin_to_millicelsius(temp); + *ret_temp = temp; } else { acpi_handle_debug(adev->handle, "%s result %llu out of range\n", obj_name, temp); @@ -43,6 +43,44 @@ static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name, return 0; } +int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp) +{ + char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'}; + + if (id < 0 || id > 9) + return -EINVAL; + + return acpi_trip_temp(adev, obj_name, ret_temp); +} + +int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp) +{ + return acpi_trip_temp(adev, "_PSV", ret_temp); +} + +int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp) +{ + return acpi_trip_temp(adev, "_HOT", ret_temp); +} + +int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp) +{ + return acpi_trip_temp(adev, "_CRT", ret_temp); +} + +static int thermal_temp(int error, int temp_decik, int *ret_temp) +{ + if (error) + return error; + + if (temp_decik == THERMAL_TEMP_INVALID) + *ret_temp = THERMAL_TEMP_INVALID; + else + *ret_temp = deci_kelvin_to_millicelsius(temp_decik); + + return 0; +} + /** * thermal_acpi_active_trip_temp - Retrieve active trip point temperature * @adev: Target thermal zone ACPI device object. @@ -57,12 +95,10 @@ static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name, */ int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp) { - char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'}; + int temp_decik; + int ret = acpi_active_trip_temp(adev, id, &temp_decik); - if (id < 0 || id > 9) - return -EINVAL; - - return thermal_acpi_trip_temp(adev, obj_name, ret_temp); + return thermal_temp(ret, temp_decik, ret_temp); } EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp); @@ -78,7 +114,10 @@ EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp); */ int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp) { - return thermal_acpi_trip_temp(adev, "_PSV", ret_temp); + int temp_decik; + int ret = acpi_passive_trip_temp(adev, &temp_decik); + + return thermal_temp(ret, temp_decik, ret_temp); } EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp); @@ -95,7 +134,10 @@ EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp); */ int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp) { - return thermal_acpi_trip_temp(adev, "_HOT", ret_temp); + int temp_decik; + int ret = acpi_hot_trip_temp(adev, &temp_decik); + + return thermal_temp(ret, temp_decik, ret_temp); } EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp); @@ -111,6 +153,9 @@ EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp); */ int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp) { - return thermal_acpi_trip_temp(adev, "_CRT", ret_temp); + int temp_decik; + int ret = acpi_critical_trip_temp(adev, &temp_decik); + + return thermal_temp(ret, temp_decik, ret_temp); } EXPORT_SYMBOL_GPL(thermal_acpi_critical_trip_temp); From 9c8647224e9fabb765019193aa43c054a638f808 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Oct 2023 22:12:33 +0200 Subject: [PATCH 13/56] ACPI: thermal: Use library functions to obtain trip point temperature values Modify the ACPI thermal driver to use functions from the ACPI thermal library to obtain trip point temperature values instead of duplicating them locally. Among other things, this requires the functions in question to be exported to it, because it can be built as a module. It effectively changes the behavior of the driver to treat temperature values out of the reasonable range (-55 centigrade to 175 centigrade) as invalid, but there is no other expected functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 5 ++++ drivers/acpi/thermal.c | 57 +++++++++++++++++--------------------- drivers/acpi/thermal_lib.c | 4 +++ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 866c7c4ed233..a3728f70a795 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -85,6 +85,11 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context); void acpi_scan_table_notify(void); +int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp); +int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp); +int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp); +int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp); + /* -------------------------------------------------------------------------- Device Node Initialization / Removal -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index f74d81abdbfc..6c29a266dbd0 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -31,6 +31,8 @@ #include #include +#include "internal.h" + #define ACPI_THERMAL_CLASS "thermal_zone" #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" #define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80 @@ -188,24 +190,19 @@ static int active_trip_index(struct acpi_thermal *tz, static long get_passive_temp(struct acpi_thermal *tz) { - unsigned long long tmp; - acpi_status status; + int temp; - status = acpi_evaluate_integer(tz->device->handle, "_PSV", NULL, &tmp); - if (ACPI_FAILURE(status)) + if (acpi_passive_trip_temp(tz->device, &temp)) return THERMAL_TEMP_INVALID; - return tmp; + return temp; } static long get_active_temp(struct acpi_thermal *tz, int index) { - char method[] = { '_', 'A', 'C', '0' + index, '\0' }; - unsigned long long tmp; - acpi_status status; + int temp; - status = acpi_evaluate_integer(tz->device->handle, method, NULL, &tmp); - if (ACPI_FAILURE(status)) + if (acpi_active_trip_temp(tz->device, index, &temp)) return THERMAL_TEMP_INVALID; /* @@ -215,10 +212,10 @@ static long get_active_temp(struct acpi_thermal *tz, int index) if (act > 0) { unsigned long long override = celsius_to_deci_kelvin(act); - if (tmp > override) - tmp = override; + if (temp > override) + return override; } - return tmp; + return temp; } static void acpi_thermal_update_trip(struct acpi_thermal *tz, @@ -339,13 +336,12 @@ static void acpi_thermal_trips_update(struct acpi_thermal *tz, u32 event) dev_name(&adev->dev), event, 0); } -static long acpi_thermal_get_critical_trip(struct acpi_thermal *tz) +static int acpi_thermal_get_critical_trip(struct acpi_thermal *tz) { - unsigned long long tmp; - acpi_status status; + int temp; if (crt > 0) { - tmp = celsius_to_deci_kelvin(crt); + temp = celsius_to_deci_kelvin(crt); goto set; } if (crt == -1) { @@ -353,38 +349,34 @@ static long acpi_thermal_get_critical_trip(struct acpi_thermal *tz) return THERMAL_TEMP_INVALID; } - status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, &tmp); - if (ACPI_FAILURE(status)) { - acpi_handle_debug(tz->device->handle, "No critical threshold\n"); + if (acpi_critical_trip_temp(tz->device, &temp)) return THERMAL_TEMP_INVALID; - } - if (tmp <= 2732) { + + if (temp <= 2732) { /* * Below zero (Celsius) values clearly aren't right for sure, * so discard them as invalid. */ - pr_info(FW_BUG "Invalid critical threshold (%llu)\n", tmp); + pr_info(FW_BUG "Invalid critical threshold (%d)\n", temp); return THERMAL_TEMP_INVALID; } set: - acpi_handle_debug(tz->device->handle, "Critical threshold [%llu]\n", tmp); - return tmp; + acpi_handle_debug(tz->device->handle, "Critical threshold [%d]\n", temp); + return temp; } -static long acpi_thermal_get_hot_trip(struct acpi_thermal *tz) +static int acpi_thermal_get_hot_trip(struct acpi_thermal *tz) { - unsigned long long tmp; - acpi_status status; + int temp; - status = acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, &tmp); - if (ACPI_FAILURE(status)) { + if (acpi_hot_trip_temp(tz->device, &temp) || temp == THERMAL_TEMP_INVALID) { acpi_handle_debug(tz->device->handle, "No hot threshold\n"); return THERMAL_TEMP_INVALID; } - acpi_handle_debug(tz->device->handle, "Hot threshold [%llu]\n", tmp); - return tmp; + acpi_handle_debug(tz->device->handle, "Hot threshold [%d]\n", temp); + return temp; } static bool passive_trip_params_init(struct acpi_thermal *tz) @@ -1142,6 +1134,7 @@ static void __exit acpi_thermal_exit(void) module_init(acpi_thermal_init); module_exit(acpi_thermal_exit); +MODULE_IMPORT_NS(ACPI_THERMAL); MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/thermal_lib.c b/drivers/acpi/thermal_lib.c index 02bf570141af..646ff6bda6dd 100644 --- a/drivers/acpi/thermal_lib.c +++ b/drivers/acpi/thermal_lib.c @@ -52,21 +52,25 @@ int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp) return acpi_trip_temp(adev, obj_name, ret_temp); } +EXPORT_SYMBOL_NS_GPL(acpi_active_trip_temp, ACPI_THERMAL); int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp) { return acpi_trip_temp(adev, "_PSV", ret_temp); } +EXPORT_SYMBOL_NS_GPL(acpi_passive_trip_temp, ACPI_THERMAL); int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp) { return acpi_trip_temp(adev, "_HOT", ret_temp); } +EXPORT_SYMBOL_NS_GPL(acpi_hot_trip_temp, ACPI_THERMAL); int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp) { return acpi_trip_temp(adev, "_CRT", ret_temp); } +EXPORT_SYMBOL_NS_GPL(acpi_critical_trip_temp, ACPI_THERMAL); static int thermal_temp(int error, int temp_decik, int *ret_temp) { From cb21746b179c7c64faeb777a8d1d901f7d680a28 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 21 Nov 2023 09:08:26 +0200 Subject: [PATCH 14/56] ACPI: scan: Fix an error message in DisCo for Imaging support The recently merged DisCo for Imaging support used a wrong printk specifier in printing a message. Fix it by using %zu instead of %lu. Also use "bits" instead of "bytes" as these are indeed bytes. Fixes: a6cb0a611273 ("ACPI: scan: Extract MIPI DisCo for Imaging data into swnodes") Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki --- drivers/acpi/mipi-disco-img.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c index cad72d1fc127..7286cf4579bc 100644 --- a/drivers/acpi/mipi-disco-img.c +++ b/drivers/acpi/mipi-disco-img.c @@ -533,7 +533,7 @@ static void init_csi2_port(struct acpi_device *adev, if (ret < 0) { acpi_handle_debug(handle, "Lane polarity bytes missing\n"); } else if (ret * BITS_PER_TYPE(u8) < num_lanes + 1) { - acpi_handle_info(handle, "Too few lane polarity bytes (%lu vs. %d)\n", + acpi_handle_info(handle, "Too few lane polarity bits (%zu vs. %d)\n", ret * BITS_PER_TYPE(u8), num_lanes + 1); } else { unsigned long mask = 0; From ccd45faf4973746c4f30ea41eec864e5cf191099 Mon Sep 17 00:00:00 2001 From: Nikita Kiryushin Date: Thu, 9 Nov 2023 16:49:25 +0300 Subject: [PATCH 15/56] ACPI: video: check for error while searching for backlight device parent If acpi_get_parent() called in acpi_video_dev_register_backlight() fails, for example, because acpi_ut_acquire_mutex() fails inside acpi_get_parent), this can lead to incorrect (uninitialized) acpi_parent handle being passed to acpi_get_pci_dev() for detecting the parent pci device. Check acpi_get_parent() result and set parent device only in case of success. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 9661e92c10a9 ("acpi: tie ACPI backlight devices to PCI devices if possible") Signed-off-by: Nikita Kiryushin Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_video.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 5eded14f8853..f71287072719 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1717,12 +1717,12 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) return; count++; - acpi_get_parent(device->dev->handle, &acpi_parent); - - pdev = acpi_get_pci_dev(acpi_parent); - if (pdev) { - parent = &pdev->dev; - pci_dev_put(pdev); + if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) { + pdev = acpi_get_pci_dev(acpi_parent); + if (pdev) { + parent = &pdev->dev; + pci_dev_put(pdev); + } } memset(&props, 0, sizeof(struct backlight_properties)); From 709f3cbd652e50e96a9d9c62a300313b636e3f6f Mon Sep 17 00:00:00 2001 From: Avadhut Naik Date: Thu, 16 Nov 2023 16:47:22 -0600 Subject: [PATCH 16/56] ACPI: APEI: EINJ: Refactor available_error_type_show() OSPM can discover the error injection capabilities of the platform by executing GET_ERROR_TYPE error injection action.[1] The action returns a DWORD representing a bitmap of platform supported error injections.[2] The available_error_type_show() function determines the bits set within this DWORD and provides a verbose output, from einj_error_type_string array, through /sys/kernel/debug/apei/einj/available_error_type file. The function however, assumes one to one correspondence between an error's position in the bitmap and its array entry offset. Consequently, some errors like Vendor Defined Error Type fail this assumption and will incorrectly be shown as not supported, even if their corresponding bit is set in the bitmap and they have an entry in the array. Navigate around the issue by converting einj_error_type_string into an array of structures with a predetermined mask for all error types corresponding to their bit position in the DWORD returned by GET_ERROR_TYPE action. The same breaks the aforementioned assumption resulting in all supported error types by a platform being outputted through the above available_error_type file. [1] ACPI specification 6.5, Table 18.25 [2] ACPI specification 6.5, Table 18.30 Suggested-by: Alexey Kardashevskiy Signed-off-by: Avadhut Naik Reviewed-by: Borislav Petkov (AMD) Reviewed-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj.c | 47 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 013eb621dc92..506fe319379f 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -577,38 +577,39 @@ static u64 error_param2; static u64 error_param3; static u64 error_param4; static struct dentry *einj_debug_dir; -static const char * const einj_error_type_string[] = { - "0x00000001\tProcessor Correctable\n", - "0x00000002\tProcessor Uncorrectable non-fatal\n", - "0x00000004\tProcessor Uncorrectable fatal\n", - "0x00000008\tMemory Correctable\n", - "0x00000010\tMemory Uncorrectable non-fatal\n", - "0x00000020\tMemory Uncorrectable fatal\n", - "0x00000040\tPCI Express Correctable\n", - "0x00000080\tPCI Express Uncorrectable non-fatal\n", - "0x00000100\tPCI Express Uncorrectable fatal\n", - "0x00000200\tPlatform Correctable\n", - "0x00000400\tPlatform Uncorrectable non-fatal\n", - "0x00000800\tPlatform Uncorrectable fatal\n", - "0x00001000\tCXL.cache Protocol Correctable\n", - "0x00002000\tCXL.cache Protocol Uncorrectable non-fatal\n", - "0x00004000\tCXL.cache Protocol Uncorrectable fatal\n", - "0x00008000\tCXL.mem Protocol Correctable\n", - "0x00010000\tCXL.mem Protocol Uncorrectable non-fatal\n", - "0x00020000\tCXL.mem Protocol Uncorrectable fatal\n", +static struct { u32 mask; const char *str; } const einj_error_type_string[] = { + { BIT(0), "Processor Correctable" }, + { BIT(1), "Processor Uncorrectable non-fatal" }, + { BIT(2), "Processor Uncorrectable fatal" }, + { BIT(3), "Memory Correctable" }, + { BIT(4), "Memory Uncorrectable non-fatal" }, + { BIT(5), "Memory Uncorrectable fatal" }, + { BIT(6), "PCI Express Correctable" }, + { BIT(7), "PCI Express Uncorrectable non-fatal" }, + { BIT(8), "PCI Express Uncorrectable fatal" }, + { BIT(9), "Platform Correctable" }, + { BIT(10), "Platform Uncorrectable non-fatal" }, + { BIT(11), "Platform Uncorrectable fatal"}, + { BIT(12), "CXL.cache Protocol Correctable" }, + { BIT(13), "CXL.cache Protocol Uncorrectable non-fatal" }, + { BIT(14), "CXL.cache Protocol Uncorrectable fatal" }, + { BIT(15), "CXL.mem Protocol Correctable" }, + { BIT(16), "CXL.mem Protocol Uncorrectable non-fatal" }, + { BIT(17), "CXL.mem Protocol Uncorrectable fatal" }, }; static int available_error_type_show(struct seq_file *m, void *v) { int rc; - u32 available_error_type = 0; + u32 error_type = 0; - rc = einj_get_available_error_type(&available_error_type); + rc = einj_get_available_error_type(&error_type); if (rc) return rc; for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++) - if (available_error_type & BIT(pos)) - seq_puts(m, einj_error_type_string[pos]); + if (error_type & einj_error_type_string[pos].mask) + seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask, + einj_error_type_string[pos].str); return 0; } From 71cd3c636404ceb08226b5095ca36a04eb578ca1 Mon Sep 17 00:00:00 2001 From: Avadhut Naik Date: Thu, 16 Nov 2023 16:47:23 -0600 Subject: [PATCH 17/56] fs: debugfs: Add write functionality to debugfs blobs Currently, debugfs_create_blob() creates read-only debugfs binary blob files. In some cases, however, userspace tools need to write variable length data structures into predetermined memory addresses. An example is when injecting Vendor-defined error types through the einj module. In such cases, the functionality to write to these blob files in debugfs would be desired since the mapping aspect can be handled within the modules with userspace tools only needing to write into the blob files. Implement a write callback to enable writing to these blob files, created in debugfs, by owners only. Signed-off-by: Avadhut Naik Reviewed-by: Alexey Kardashevskiy Reviewed-by: Greg Kroah-Hartman Reviewed-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- fs/debugfs/file.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index c45e8c2d62e1..00b834269aad 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -1008,17 +1008,35 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf, return r; } +static ssize_t write_file_blob(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debugfs_blob_wrapper *blob = file->private_data; + struct dentry *dentry = F_DENTRY(file); + ssize_t r; + + r = debugfs_file_get(dentry); + if (unlikely(r)) + return r; + r = simple_write_to_buffer(blob->data, blob->size, ppos, user_buf, + count); + + debugfs_file_put(dentry); + return r; +} + static const struct file_operations fops_blob = { .read = read_file_blob, + .write = write_file_blob, .open = simple_open, .llseek = default_llseek, }; /** - * debugfs_create_blob - create a debugfs file that is used to read a binary blob + * debugfs_create_blob - create a debugfs file that is used to read and write + * a binary blob * @name: a pointer to a string containing the name of the file to create. - * @mode: the read permission that the file should have (other permissions are - * masked out) + * @mode: the permission that the file should have * @parent: a pointer to the parent dentry for this file. This should be a * directory dentry if set. If this parameter is %NULL, then the * file will be created in the root of the debugfs filesystem. @@ -1027,7 +1045,7 @@ static const struct file_operations fops_blob = { * * This function creates a file in debugfs with the given name that exports * @blob->data as a binary blob. If the @mode variable is so set it can be - * read from. Writing is not supported. + * read from and written to. * * This function will return a pointer to a dentry if it succeeds. This * pointer must be passed to the debugfs_remove() function when the file is @@ -1042,7 +1060,7 @@ struct dentry *debugfs_create_blob(const char *name, umode_t mode, struct dentry *parent, struct debugfs_blob_wrapper *blob) { - return debugfs_create_file_unsafe(name, mode & 0444, parent, blob, &fops_blob); + return debugfs_create_file_unsafe(name, mode & 0644, parent, blob, &fops_blob); } EXPORT_SYMBOL_GPL(debugfs_create_blob); From 0706526ec7704dcd046239078ac175d11a88a95e Mon Sep 17 00:00:00 2001 From: Avadhut Naik Date: Thu, 16 Nov 2023 16:47:24 -0600 Subject: [PATCH 18/56] platform/chrome: cros_ec_debugfs: Fix permissions for panicinfo The debugfs_create_blob() function has been used to create read-only binary blobs in debugfs. The function filters out permissions, other than S_IRUSR, S_IRGRP and S_IROTH, provided while creating the blobs. The very behavior though is being changed through previous patch in the series (fs: debugfs: Add write functionality to debugfs blobs) which makes the binary blobs writable by owners. Thus, all permissions provided while creating the blobs, except S_IRUSR,S_IWUSR, S_IRGRP, S_IROTH, will be filtered by debugfs_create_blob(). As such, rectify the permissions of panicinfo file since the S_IFREG flag was anyways being filtered out by debugfs_create_blob(). Moreover, the very flag will always be set be set for the panicinfo file through __debugfs_create_file(). Signed-off-by: Avadhut Naik Reviewed-by: Greg Kroah-Hartman Reviewed-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- drivers/platform/chrome/cros_ec_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 091fdc154d79..6bf6f0e7b597 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -454,7 +454,7 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) debug_info->panicinfo_blob.data = data; debug_info->panicinfo_blob.size = ret; - debugfs_create_blob("panicinfo", S_IFREG | 0444, debug_info->dir, + debugfs_create_blob("panicinfo", 0444, debug_info->dir, &debug_info->panicinfo_blob); return 0; From 22fca621bd1bbc5366e9cd941eb1c07c0963d984 Mon Sep 17 00:00:00 2001 From: Avadhut Naik Date: Thu, 16 Nov 2023 16:47:25 -0600 Subject: [PATCH 19/56] ACPI: APEI: EINJ: Add support for vendor defined error types Vendor-Defined Error types are supported by the platform apart from standard error types if bit 31 is set in the output of GET_ERROR_TYPE Error Injection Action.[1] While the errors themselves and the length of their associated "OEM Defined data structure" might vary between vendors, the physical address of this structure can be computed through vendor_extension and length fields of "SET_ERROR_TYPE_WITH_ADDRESS" and "Vendor Error Type Extension" Structures respectively.[2][3] Currently, however, the einj module only computes the physical address of Vendor Error Type Extension Structure. Neither does it compute the physical address of OEM Defined structure nor does it establish the memory mapping required for injecting Vendor-defined errors. Consequently, userspace tools have to establish the very mapping through /dev/mem, nopat kernel parameter and system calls like mmap/munmap initially before injecting Vendor-defined errors. Circumvent the issue by computing the physical address of OEM Defined data structure and establishing the required mapping with the structure. Create a new file "oem_error", if the system supports Vendor-defined errors, to export this mapping, through debugfs_create_blob(). Userspace tools can then populate their respective OEM Defined structure instances and just write to the file as part of injecting Vendor-defined Errors. Similarly, the tools can also read from the file if the system firmware provides some information through the OEM defined structure after error injection. [1] ACPI specification 6.5, section 18.6.4 [2] ACPI specification 6.5, Table 18.31 [3] ACPI specification 6.5, Table 18.32 Suggested-by: Yazen Ghannam Signed-off-by: Avadhut Naik Reviewed-by: Tony Luck Reviewed-by: Borislav Petkov (AMD) Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 506fe319379f..89fb9331c611 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -73,6 +73,7 @@ static u32 notrigger; static u32 vendor_flags; static struct debugfs_blob_wrapper vendor_blob; +static struct debugfs_blob_wrapper vendor_errors; static char vendor_dev[64]; /* @@ -182,6 +183,21 @@ static int einj_timedout(u64 *t) return 0; } +static void get_oem_vendor_struct(u64 paddr, int offset, + struct vendor_error_type_extension *v) +{ + unsigned long vendor_size; + u64 target_pa = paddr + offset + sizeof(struct vendor_error_type_extension); + + vendor_size = v->length - sizeof(struct vendor_error_type_extension); + + if (vendor_size) + vendor_errors.data = acpi_os_map_memory(target_pa, vendor_size); + + if (vendor_errors.data) + vendor_errors.size = vendor_size; +} + static void check_vendor_extension(u64 paddr, struct set_error_type_with_address *v5param) { @@ -194,6 +210,7 @@ static void check_vendor_extension(u64 paddr, v = acpi_os_map_iomem(paddr + offset, sizeof(*v)); if (!v) return; + get_oem_vendor_struct(paddr, offset, v); sbdf = v->pcie_sbdf; sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", sbdf >> 24, (sbdf >> 16) & 0xff, @@ -596,6 +613,7 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = { { BIT(15), "CXL.mem Protocol Correctable" }, { BIT(16), "CXL.mem Protocol Uncorrectable non-fatal" }, { BIT(17), "CXL.mem Protocol Uncorrectable fatal" }, + { BIT(31), "Vendor Defined Error Types" }, }; static int available_error_type_show(struct seq_file *m, void *v) @@ -768,6 +786,10 @@ static int __init einj_init(void) einj_debug_dir, &vendor_flags); } + if (vendor_errors.size) + debugfs_create_blob("oem_error", 0600, einj_debug_dir, + &vendor_errors); + pr_info("Error INJection is initialized.\n"); return 0; @@ -793,6 +815,8 @@ static void __exit einj_exit(void) sizeof(struct einj_parameter); acpi_os_unmap_iomem(einj_param, size); + if (vendor_errors.size) + acpi_os_unmap_memory(vendor_errors.data, vendor_errors.size); } einj_exec_ctx_init(&ctx); apei_exec_post_unmap_gars(&ctx); From a2ee7581afd59015b8f9ae01fad131aed9f26f01 Mon Sep 17 00:00:00 2001 From: Jeff Brasen Date: Fri, 10 Nov 2023 00:03:21 +0530 Subject: [PATCH 20/56] ACPI: thermal: Add Thermal fast Sampling Period (_TFP) support Add support of "Thermal fast Sampling Period (_TFP)" for passive cooling. As per the ACPI specification (ACPI 6.5, Section 11.4.17 "_TFP (Thermal fast Sampling Period)", _TFP overrides _TSP ("Thermal Sampling Period" if both are present in a Thermal zone. Signed-off-by: Jeff Brasen Co-developed-by: Sumit Gupta Signed-off-by: Sumit Gupta [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 6c29a266dbd0..ee28ca93d983 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -92,7 +92,7 @@ struct acpi_thermal_passive { struct acpi_thermal_trip trip; unsigned long tc1; unsigned long tc2; - unsigned long tsp; + unsigned long delay; }; struct acpi_thermal_active { @@ -396,11 +396,17 @@ static bool passive_trip_params_init(struct acpi_thermal *tz) tz->trips.passive.tc2 = tmp; + status = acpi_evaluate_integer(tz->device->handle, "_TFP", NULL, &tmp); + if (ACPI_SUCCESS(status)) { + tz->trips.passive.delay = tmp; + return true; + } + status = acpi_evaluate_integer(tz->device->handle, "_TSP", NULL, &tmp); if (ACPI_FAILURE(status)) return false; - tz->trips.passive.tsp = tmp; + tz->trips.passive.delay = tmp * 100; return true; } @@ -896,7 +902,7 @@ static int acpi_thermal_add(struct acpi_device *device) acpi_trip = &tz->trips.passive.trip; if (acpi_thermal_trip_valid(acpi_trip)) { - passive_delay = tz->trips.passive.tsp * 100; + passive_delay = tz->trips.passive.delay; trip->type = THERMAL_TRIP_PASSIVE; trip->temperature = acpi_thermal_temp(tz, acpi_trip->temp_dk); From 4c2ba6a0ed1944c17b957157bd1e686be4ea968a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Sep 2023 14:58:02 +0200 Subject: [PATCH 21/56] ACPI: processor: Provide empty stub of acpi_proc_quirk_mwait_check() Commit 0a0e2ea642f6 ("ACPI: processor: Move MWAIT quirk out of acpi_processor.c") added acpi_proc_quirk_mwait_check() that is only defined for x86 and is unlikely to be defined for any other architectures, so put it under #ifdef CONFIG_X86 and provide an empty stub implementation of it for the other cases. This is kind of orthogonal to [1], because if any architectures other than x86 decide to use the processor _OSC, they will see the reported build error. Link: https://lore.kernel.org/lkml/c7a05a44-c0be-46c2-a21d-b242524d482b@roeck-us.net Link: https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/commit/?h=remove-ia64&id=a0334bf78b95532cec54f56b53e8ae1bfe7e1ca1 # [1] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 866c7c4ed233..db666f13c2ef 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -148,8 +148,11 @@ int acpi_wakeup_device_init(void); #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC void acpi_early_processor_control_setup(void); void acpi_early_processor_set_pdc(void); - +#ifdef CONFIG_X86 void acpi_proc_quirk_mwait_check(void); +#else +static inline void acpi_proc_quirk_mwait_check(void) {} +#endif bool processor_physically_present(acpi_handle handle); #else static inline void acpi_early_processor_control_setup(void) {} From 56d2eeda87995245300836ee4dbd13b002311782 Mon Sep 17 00:00:00 2001 From: Nikita Kiryushin Date: Thu, 9 Nov 2023 21:08:59 +0300 Subject: [PATCH 22/56] ACPI: LPIT: Avoid u32 multiplication overflow In lpit_update_residency() there is a possibility of overflow in multiplication, if tsc_khz is large enough (> UINT_MAX/1000). Change multiplication to mul_u32_u32(). Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: eeb2d80d502a ("ACPI / LPIT: Add Low Power Idle Table (LPIT) support") Signed-off-by: Nikita Kiryushin Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_lpit.c b/drivers/acpi/acpi_lpit.c index c5598b6d5db8..794962c5c88e 100644 --- a/drivers/acpi/acpi_lpit.c +++ b/drivers/acpi/acpi_lpit.c @@ -105,7 +105,7 @@ static void lpit_update_residency(struct lpit_residency_info *info, return; info->frequency = lpit_native->counter_frequency ? - lpit_native->counter_frequency : tsc_khz * 1000; + lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U); if (!info->frequency) info->frequency = 1; From b14b2d56168c1bcf00fccb5a2fe746e64ed970cc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Nov 2023 07:59:57 +0100 Subject: [PATCH 23/56] ACPI: thermal_lib: include "internal.h" for function prototypes The newly added functions are declared in a header that is not included before the definition: drivers/acpi/thermal_lib.c:46:5: error: no previous prototype for 'acpi_active_trip_temp' [-Werror=missing-prototypes] 46 | int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp) | ^~~~~~~~~~~~~~~~~~~~~ drivers/acpi/thermal_lib.c:57:5: error: no previous prototype for 'acpi_passive_trip_temp' [-Werror=missing-prototypes] 57 | int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp) | ^~~~~~~~~~~~~~~~~~~~~~ drivers/acpi/thermal_lib.c:63:5: error: no previous prototype for 'acpi_hot_trip_temp' [-Werror=missing-prototypes] 63 | int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp) | ^~~~~~~~~~~~~~~~~~ drivers/acpi/thermal_lib.c:69:5: error: no previous prototype for 'acpi_critical_trip_temp' [-Werror=missing-prototypes] 69 | int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp) | ^~~~~~~~~~~~~~~~~~~~~~~ Fixes: 6908097aa5a7 ("ACPI: thermal_lib: Add functions returning temperature in deci-Kelvin") Signed-off-by: Arnd Bergmann Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal_lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/thermal_lib.c b/drivers/acpi/thermal_lib.c index 646ff6bda6dd..4e0519ca9739 100644 --- a/drivers/acpi/thermal_lib.c +++ b/drivers/acpi/thermal_lib.c @@ -9,6 +9,7 @@ #include #include #include +#include "internal.h" /* * Minimum temperature for full military grade is 218°K (-55°C) and From 7a36b901a6eb0e9945341db71ed3c45c7721cfa9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 27 Nov 2023 20:57:43 +0100 Subject: [PATCH 24/56] ACPI: OSL: Use a threaded interrupt handler for SCI In the current arrangement, all of the acpi_ev_sci_xrupt_handler() code is run as an interrupt handler for the SCI, in interrupt context. Among other things, this causes it to run with local interrupts off which can be problematic if many GPEs are enabled and they are located in the I/O address space, for example (because in that case local interrupts will be off for the duration of all of the GPE hardware accesses carried out while handling an SCI combined and that may be quite a bit of time in extreme scenarios). However, there is no particular reason why the code in question really needs to run in interrupt context and in particular, it has no specific reason to run with local interrupts off. The only real requirement is to prevent multiple instences of it from running in parallel with each other, but that can be achieved regardless. For this reason, use request_threaded_irq() instead of request_irq() for the ACPI SCI and pass IRQF_ONESHOT to it in flags to indicate that the interrupt needs to be masked while its handling thread is running so as to prevent it from re-triggering while it is being handled (and in particular until the final handled/not handled outcome is determined). While at it, drop a redundant local variable from acpi_irq(). Signed-off-by: Rafael J. Wysocki Tested-by: Mario Limonciello Tested-by: Mika Westerberg --- drivers/acpi/osl.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c09cc3c68633..d56dda795118 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -544,11 +544,7 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val, static irqreturn_t acpi_irq(int irq, void *dev_id) { - u32 handled; - - handled = (*acpi_irq_handler) (acpi_irq_context); - - if (handled) { + if ((*acpi_irq_handler)(acpi_irq_context)) { acpi_irq_handled++; return IRQ_HANDLED; } else { @@ -582,7 +578,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_handler = handler; acpi_irq_context = context; - if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { + if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED | IRQF_ONESHOT, + "acpi", acpi_irq)) { pr_err("SCI (IRQ%d) allocation failed\n", irq); acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; From 57b8543ceee82ea72be1745a6dc3a9111d55a151 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Thu, 23 Nov 2023 15:36:13 +0530 Subject: [PATCH 25/56] ACPI: bus: update acpi_dev_uid_match() to support multiple types According to the ACPI specification, a _UID object can evaluate to either a numeric value or a string. Update acpi_dev_uid_match() to support _UID matching for both integer and string types. Suggested-by: Mika Westerberg Signed-off-by: Raag Jadav [ rjw: Rename auxiliary macros, relocate kerneldoc comment ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/utils.c | 19 ------------------- include/acpi/acpi_bus.h | 41 ++++++++++++++++++++++++++++++++++++++++- include/linux/acpi.h | 8 +++----- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 28c75242fca9..fe7e850c6479 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -824,25 +824,6 @@ bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs) } EXPORT_SYMBOL(acpi_check_dsm); -/** - * acpi_dev_uid_match - Match device by supplied UID - * @adev: ACPI device to match. - * @uid2: Unique ID of the device. - * - * Matches UID in @adev with given @uid2. - * - * Returns: - * - %true if matches. - * - %false otherwise. - */ -bool acpi_dev_uid_match(struct acpi_device *adev, const char *uid2) -{ - const char *uid1 = acpi_device_uid(adev); - - return uid1 && uid2 && !strcmp(uid1, uid2); -} -EXPORT_SYMBOL_GPL(acpi_dev_uid_match); - /** * acpi_dev_hid_uid_match - Match device by supplied HID and UID * @adev: ACPI device to match. diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 1216d72c650f..4bde0b417476 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -764,10 +764,49 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev) adev->power.states[ACPI_STATE_D3_HOT].flags.explicit_set); } -bool acpi_dev_uid_match(struct acpi_device *adev, const char *uid2); bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2); int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer); +static inline bool acpi_str_uid_match(struct acpi_device *adev, const char *uid2) +{ + const char *uid1 = acpi_device_uid(adev); + + return uid1 && uid2 && !strcmp(uid1, uid2); +} + +static inline bool acpi_int_uid_match(struct acpi_device *adev, u64 uid2) +{ + u64 uid1; + + return !acpi_dev_uid_to_integer(adev, &uid1) && uid1 == uid2; +} + +#define TYPE_ENTRY(type, x) \ + const type: x, \ + type: x + +#define ACPI_STR_TYPES(match) \ + TYPE_ENTRY(unsigned char *, match), \ + TYPE_ENTRY(signed char *, match), \ + TYPE_ENTRY(char *, match), \ + TYPE_ENTRY(void *, match) + +/** + * acpi_dev_uid_match - Match device by supplied UID + * @adev: ACPI device to match. + * @uid2: Unique ID of the device. + * + * Matches UID in @adev with given @uid2. + * + * Returns: %true if matches, %false otherwise. + */ +#define acpi_dev_uid_match(adev, uid2) \ + _Generic(uid2, \ + /* Treat @uid2 as a string for acpi string types */ \ + ACPI_STR_TYPES(acpi_str_uid_match), \ + /* Treat as an integer otherwise */ \ + default: acpi_int_uid_match)(adev, uid2) + void acpi_dev_clear_dependencies(struct acpi_device *supplier); bool acpi_dev_ready_for_enumeration(const struct acpi_device *device); struct acpi_device *acpi_dev_get_next_consumer_dev(struct acpi_device *supplier, diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 4db54e928b36..2abe81f074de 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -756,6 +756,9 @@ const char *acpi_get_subsystem_id(acpi_handle handle); #define ACPI_HANDLE(dev) (NULL) #define ACPI_HANDLE_FWNODE(fwnode) (NULL) +/* Get rid of the -Wunused-variable for adev */ +#define acpi_dev_uid_match(adev, uid2) (adev && false) + #include struct fwnode_handle; @@ -772,11 +775,6 @@ static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv) struct acpi_device; -static inline bool acpi_dev_uid_match(struct acpi_device *adev, const char *uid2) -{ - return false; -} - static inline bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2) { From b2b32a1738815155d4a0039bb7a6092d40f23e81 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Thu, 23 Nov 2023 15:36:14 +0530 Subject: [PATCH 26/56] ACPI: bus: update acpi_dev_hid_uid_match() to support multiple types Now that we have _UID matching support for both integer and string types, we can support them into acpi_dev_hid_uid_match() helper as well. Signed-off-by: Raag Jadav Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/utils.c | 29 ----------------------------- include/acpi/acpi_bus.h | 24 +++++++++++++++++++++++- include/linux/acpi.h | 7 +------ 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index fe7e850c6479..03f6de9a0807 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -824,35 +824,6 @@ bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs) } EXPORT_SYMBOL(acpi_check_dsm); -/** - * acpi_dev_hid_uid_match - Match device by supplied HID and UID - * @adev: ACPI device to match. - * @hid2: Hardware ID of the device. - * @uid2: Unique ID of the device, pass NULL to not check _UID. - * - * Matches HID and UID in @adev with given @hid2 and @uid2. Absence of @uid2 - * will be treated as a match. If user wants to validate @uid2, it should be - * done before calling this function. - * - * Returns: - * - %true if matches or @uid2 is NULL. - * - %false otherwise. - */ -bool acpi_dev_hid_uid_match(struct acpi_device *adev, - const char *hid2, const char *uid2) -{ - const char *hid1 = acpi_device_hid(adev); - - if (strcmp(hid1, hid2)) - return false; - - if (!uid2) - return true; - - return acpi_dev_uid_match(adev, uid2); -} -EXPORT_SYMBOL(acpi_dev_hid_uid_match); - /** * acpi_dev_uid_to_integer - treat ACPI device _UID as integer * @adev: ACPI device to get _UID from diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 4bde0b417476..aae31552c574 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -764,9 +764,15 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev) adev->power.states[ACPI_STATE_D3_HOT].flags.explicit_set); } -bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2); int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer); +static inline bool acpi_dev_hid_match(struct acpi_device *adev, const char *hid2) +{ + const char *hid1 = acpi_device_hid(adev); + + return hid1 && hid2 && !strcmp(hid1, hid2); +} + static inline bool acpi_str_uid_match(struct acpi_device *adev, const char *uid2) { const char *uid1 = acpi_device_uid(adev); @@ -807,6 +813,22 @@ static inline bool acpi_int_uid_match(struct acpi_device *adev, u64 uid2) /* Treat as an integer otherwise */ \ default: acpi_int_uid_match)(adev, uid2) +/** + * acpi_dev_hid_uid_match - Match device by supplied HID and UID + * @adev: ACPI device to match. + * @hid2: Hardware ID of the device. + * @uid2: Unique ID of the device, pass 0 or NULL to not check _UID. + * + * Matches HID and UID in @adev with given @hid2 and @uid2. Absence of @uid2 + * will be treated as a match. If user wants to validate @uid2, it should be + * done before calling this function. + * + * Returns: %true if matches or @uid2 is 0 or NULL, %false otherwise. + */ +#define acpi_dev_hid_uid_match(adev, hid2, uid2) \ + (acpi_dev_hid_match(adev, hid2) && \ + (!(uid2) || acpi_dev_uid_match(adev, uid2))) + void acpi_dev_clear_dependencies(struct acpi_device *supplier); bool acpi_dev_ready_for_enumeration(const struct acpi_device *device); struct acpi_device *acpi_dev_get_next_consumer_dev(struct acpi_device *supplier, diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 2abe81f074de..75274585656c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -758,6 +758,7 @@ const char *acpi_get_subsystem_id(acpi_handle handle); /* Get rid of the -Wunused-variable for adev */ #define acpi_dev_uid_match(adev, uid2) (adev && false) +#define acpi_dev_hid_uid_match(adev, hid2, uid2) (adev && false) #include @@ -775,12 +776,6 @@ static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv) struct acpi_device; -static inline bool -acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2) -{ - return false; -} - static inline int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer) { return -ENODEV; From 5ecdb287be126172ce7f4d61af5c6402b0fc9e61 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Thu, 23 Nov 2023 15:36:15 +0530 Subject: [PATCH 27/56] ACPI: LPSS: use acpi_dev_uid_match() for matching _UID Now that we have _UID matching support for integer types, we can use acpi_dev_uid_match() for it. Signed-off-by: Raag Jadav Acked-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpss.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 875de44961bf..79f4fc7d6871 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -167,13 +167,9 @@ static struct pwm_lookup byt_pwm_lookup[] = { static void byt_pwm_setup(struct lpss_private_data *pdata) { - u64 uid; - /* Only call pwm_add_table for the first PWM controller */ - if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 1) - return; - - pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); + if (acpi_dev_uid_match(pdata->adev, 1)) + pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); } #define LPSS_I2C_ENABLE 0x6c @@ -218,13 +214,9 @@ static struct pwm_lookup bsw_pwm_lookup[] = { static void bsw_pwm_setup(struct lpss_private_data *pdata) { - u64 uid; - /* Only call pwm_add_table for the first PWM controller */ - if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 1) - return; - - pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); + if (acpi_dev_uid_match(pdata->adev, 1)) + pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); } static const struct property_entry lpt_spi_properties[] = { From 9e93507da2cf57068ec5d3496185fe0236844ae9 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Thu, 23 Nov 2023 15:36:16 +0530 Subject: [PATCH 28/56] efi: dev-path-parser: use acpi_dev_uid_match() for matching _UID Now that we have _UID matching support for integer types, we can use acpi_dev_uid_match() for it. Signed-off-by: Raag Jadav Reviewed-by: Ard Biesheuvel Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/dev-path-parser.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/efi/dev-path-parser.c b/drivers/firmware/efi/dev-path-parser.c index f80d87c199c3..937be269fee8 100644 --- a/drivers/firmware/efi/dev-path-parser.c +++ b/drivers/firmware/efi/dev-path-parser.c @@ -18,8 +18,6 @@ static long __init parse_acpi_path(const struct efi_dev_path *node, struct acpi_device *adev; struct device *phys_dev; char hid[ACPI_ID_LEN]; - u64 uid; - int ret; if (node->header.length != 12) return -EINVAL; @@ -31,10 +29,9 @@ static long __init parse_acpi_path(const struct efi_dev_path *node, node->acpi.hid >> 16); for_each_acpi_dev_match(adev, hid, NULL, -1) { - ret = acpi_dev_uid_to_integer(adev, &uid); - if (ret == 0 && node->acpi.uid == uid) + if (acpi_dev_uid_match(adev, node->acpi.uid)) break; - if (ret == -ENODATA && node->acpi.uid == 0) + if (!acpi_device_uid(adev) && node->acpi.uid == 0) break; } if (!adev) From 38dd7b72ef8046a3009ef384b711d4509de3d427 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Thu, 23 Nov 2023 15:36:17 +0530 Subject: [PATCH 29/56] perf: arm_cspmu: drop redundant acpi_dev_uid_to_integer() Now that we have _UID matching support for integer types, we can use acpi_dev_hid_uid_match() for it. Signed-off-by: Raag Jadav Acked-by: Will Deacon Signed-off-by: Rafael J. Wysocki --- drivers/perf/arm_cspmu/arm_cspmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/perf/arm_cspmu/arm_cspmu.c b/drivers/perf/arm_cspmu/arm_cspmu.c index 2cc35dded007..50b89b989ce7 100644 --- a/drivers/perf/arm_cspmu/arm_cspmu.c +++ b/drivers/perf/arm_cspmu/arm_cspmu.c @@ -1108,7 +1108,6 @@ static int arm_cspmu_request_irq(struct arm_cspmu *cspmu) static inline int arm_cspmu_find_cpu_container(int cpu, u32 container_uid) { - u64 acpi_uid; struct device *cpu_dev; struct acpi_device *acpi_dev; @@ -1118,8 +1117,7 @@ static inline int arm_cspmu_find_cpu_container(int cpu, u32 container_uid) acpi_dev = ACPI_COMPANION(cpu_dev); while (acpi_dev) { - if (acpi_dev_hid_uid_match(acpi_dev, ACPI_PROCESSOR_CONTAINER_HID, NULL) && - !acpi_dev_uid_to_integer(acpi_dev, &acpi_uid) && acpi_uid == container_uid) + if (acpi_dev_hid_uid_match(acpi_dev, ACPI_PROCESSOR_CONTAINER_HID, container_uid)) return 0; acpi_dev = acpi_dev_parent(acpi_dev); From 4b3805daaacb2168665c6222f261e68accb120dc Mon Sep 17 00:00:00 2001 From: Yuntao Wang Date: Mon, 20 Nov 2023 19:41:43 +0800 Subject: [PATCH 30/56] ACPI: tables: Correct and clean up the logic of acpi_parse_entries_array() The original intention of acpi_parse_entries_array() is to return the number of all matching entries on success. This number may be greater than the value of the max_entries parameter. When this happens, the function will output a warning message, indicating that `count - max_entries` matching entries remain unprocessed and have been ignored. However, commit 4ceacd02f5a1 ("ACPI / table: Always count matched and successfully parsed entries") changed this logic to return the number of entries successfully processed by the handler. In this case, when the max_entries parameter is not zero, the number of entries successfully processed can never be greater than the value of max_entries. In other words, the expression `count > max_entries` will always evaluate to false. This means that the logic in the final if statement will never be executed. Commit 99b0efd7c886 ("ACPI / tables: do not report the number of entries ignored by acpi_parse_entries()") mentioned this issue, but it tried to fix it by removing part of the warning message. This is meaningless because the pr_warn statement will never be executed in the first place. Commit 8726d4f44150 ("ACPI / tables: fix acpi_parse_entries_array() so it traverses all subtables") introduced an errs variable, which is intended to make acpi_parse_entries_array() always traverse all of the subtables, calling as many of the callbacks as possible. However, it seems that the commit does not achieve this goal. For example, when a handler returns an error, none of the handlers will be called again in the subsequent iterations. This result appears to be no different from before the change. This patch corrects and cleans up the logic of acpi_parse_entries_array(), making it return the number of all matching entries, rather than the number of entries successfully processed by handlers. Additionally, if an error occurs when executing a handler, the function will return -EINVAL immediately. This patch should not affect existing users of acpi_parse_entries_array(). Signed-off-by: Yuntao Wang Reviewed-by: Dave Jiang Signed-off-by: Rafael J. Wysocki --- lib/fw_table.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/lib/fw_table.c b/lib/fw_table.c index 294df54e33b6..c49a09ee3853 100644 --- a/lib/fw_table.c +++ b/lib/fw_table.c @@ -85,11 +85,6 @@ acpi_get_subtable_type(char *id) return ACPI_SUBTABLE_COMMON; } -static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc) -{ - return proc->handler || proc->handler_arg; -} - static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc, union acpi_subtable_headers *hdr, unsigned long end) @@ -133,7 +128,6 @@ acpi_parse_entries_array(char *id, unsigned long table_size, unsigned long table_end, subtable_len, entry_len; struct acpi_subtable_entry entry; int count = 0; - int errs = 0; int i; table_end = (unsigned long)table_header + table_header->length; @@ -145,25 +139,19 @@ acpi_parse_entries_array(char *id, unsigned long table_size, ((unsigned long)table_header + table_size); subtable_len = acpi_get_subtable_header_length(&entry); - while (((unsigned long)entry.hdr) + subtable_len < table_end) { - if (max_entries && count >= max_entries) - break; - + while (((unsigned long)entry.hdr) + subtable_len < table_end) { for (i = 0; i < proc_num; i++) { if (acpi_get_entry_type(&entry) != proc[i].id) continue; - if (!has_handler(&proc[i]) || - (!errs && - call_handler(&proc[i], entry.hdr, table_end))) { - errs++; - continue; - } + + if (!max_entries || count < max_entries) + if (call_handler(&proc[i], entry.hdr, table_end)) + return -EINVAL; proc[i].count++; + count++; break; } - if (i != proc_num) - count++; /* * If entry->length is 0, break from this loop to avoid @@ -180,9 +168,9 @@ acpi_parse_entries_array(char *id, unsigned long table_size, } if (max_entries && count > max_entries) { - pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n", - id, proc->id, count); + pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", + id, proc->id, count - max_entries, count); } - return errs ? -EINVAL : count; + return count; } From 310293a2b94197f3d75e65ab22672287a7938a00 Mon Sep 17 00:00:00 2001 From: Srikar Srimath Tirumala Date: Thu, 23 Nov 2023 17:44:33 +0530 Subject: [PATCH 31/56] ACPI: processor: reduce CPUFREQ thermal reduction pctg for Tegra241 Current implementation of processor_thermal performs software throttling in fixed steps of "20%" which can be too coarse for some platforms. We observed some performance gain after reducing the throttle percentage. Change the CPUFREQ thermal reduction percentage and maximum thermal steps to be configurable. Also, update the default values of both for Nvidia Tegra241 (Grace) SoC. The thermal reduction percentage is reduced to "5%" and accordingly the maximum number of thermal steps are increased as they are derived from the reduction percentage. Signed-off-by: Srikar Srimath Tirumala Co-developed-by: Sumit Gupta Signed-off-by: Sumit Gupta Acked-by: Sudeep Holla Acked-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki --- drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/thermal_cpufreq.c | 20 ++++++++++++ drivers/acpi/internal.h | 9 +++++ drivers/acpi/processor_thermal.c | 49 +++++++++++++++++++++++----- 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 drivers/acpi/arm64/thermal_cpufreq.c diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 143debc1ba4a..726944648c9b 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ACPI_GTDT) += gtdt.o obj-$(CONFIG_ACPI_APMT) += apmt.o obj-$(CONFIG_ARM_AMBA) += amba.o obj-y += dma.o init.o +obj-y += thermal_cpufreq.o diff --git a/drivers/acpi/arm64/thermal_cpufreq.c b/drivers/acpi/arm64/thermal_cpufreq.c new file mode 100644 index 000000000000..d524f2cd6044 --- /dev/null +++ b/drivers/acpi/arm64/thermal_cpufreq.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include + +#include "../internal.h" + +#define SMCCC_SOC_ID_T241 0x036b0241 + +int acpi_arch_thermal_cpufreq_pctg(void) +{ + s32 soc_id = arm_smccc_get_soc_id_version(); + + /* + * Check JEP106 code for NVIDIA Tegra241 chip (036b:0241) and + * reduce the CPUFREQ Thermal reduction percentage to 5%. + */ + if (soc_id == SMCCC_SOC_ID_T241) + return 5; + + return 0; +} diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index db666f13c2ef..14b2df0b54b8 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -85,6 +85,15 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context); void acpi_scan_table_notify(void); +#ifdef CONFIG_ARM64 +int acpi_arch_thermal_cpufreq_pctg(void); +#else +static inline int acpi_arch_thermal_cpufreq_pctg(void) +{ + return 0; +} +#endif + /* -------------------------------------------------------------------------- Device Node Initialization / Removal -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index b7c6287eccca..1219adb11ab9 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -17,6 +17,8 @@ #include #include +#include "internal.h" + #ifdef CONFIG_CPU_FREQ /* If a passive cooling situation is detected, primarily CPUfreq is used, as it @@ -26,12 +28,21 @@ */ #define CPUFREQ_THERMAL_MIN_STEP 0 -#define CPUFREQ_THERMAL_MAX_STEP 3 -static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg); +static int cpufreq_thermal_max_step __read_mostly = 3; -#define reduction_pctg(cpu) \ - per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu)) +/* + * Minimum throttle percentage for processor_thermal cooling device. + * The processor_thermal driver uses it to calculate the percentage amount by + * which cpu frequency must be reduced for each cooling state. This is also used + * to calculate the maximum number of throttling steps or cooling states. + */ +static int cpufreq_thermal_reduction_pctg __read_mostly = 20; + +static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_step); + +#define reduction_step(cpu) \ + per_cpu(cpufreq_thermal_reduction_step, phys_package_first_cpu(cpu)) /* * Emulate "per package data" using per cpu data (which should really be @@ -71,7 +82,7 @@ static int cpufreq_get_max_state(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return 0; - return CPUFREQ_THERMAL_MAX_STEP; + return cpufreq_thermal_max_step; } static int cpufreq_get_cur_state(unsigned int cpu) @@ -79,7 +90,7 @@ static int cpufreq_get_cur_state(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return 0; - return reduction_pctg(cpu); + return reduction_step(cpu); } static int cpufreq_set_cur_state(unsigned int cpu, int state) @@ -92,7 +103,7 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state) if (!cpu_has_cpufreq(cpu)) return 0; - reduction_pctg(cpu) = state; + reduction_step(cpu) = state; /* * Update all the CPUs in the same package because they all @@ -113,7 +124,8 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state) if (!policy) return -EINVAL; - max_freq = (policy->cpuinfo.max_freq * (100 - reduction_pctg(i) * 20)) / 100; + max_freq = (policy->cpuinfo.max_freq * + (100 - reduction_step(i) * cpufreq_thermal_reduction_pctg)) / 100; cpufreq_cpu_put(policy); @@ -126,10 +138,29 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state) return 0; } +static void acpi_thermal_cpufreq_config(void) +{ + int cpufreq_pctg = acpi_arch_thermal_cpufreq_pctg(); + + if (!cpufreq_pctg) + return; + + cpufreq_thermal_reduction_pctg = cpufreq_pctg; + + /* + * Derive the MAX_STEP from minimum throttle percentage so that the reduction + * percentage doesn't end up becoming negative. Also, cap the MAX_STEP so that + * the CPU performance doesn't become 0. + */ + cpufreq_thermal_max_step = (100 / cpufreq_pctg) - 2; +} + void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy) { unsigned int cpu; + acpi_thermal_cpufreq_config(); + for_each_cpu(cpu, policy->related_cpus) { struct acpi_processor *pr = per_cpu(processors, cpu); int ret; @@ -190,7 +221,7 @@ static int acpi_processor_max_state(struct acpi_processor *pr) /* * There exists four states according to - * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3 + * cpufreq_thermal_reduction_step. 0, 1, 2, 3 */ max_state += cpufreq_get_max_state(pr->id); if (pr->flags.throttling) From 143176a46bdd3bfbe9ba2462bf94458e80d65ebf Mon Sep 17 00:00:00 2001 From: Yuluo Qiu Date: Sun, 26 Nov 2023 21:59:13 +0800 Subject: [PATCH 32/56] ACPI: video: Add quirk for the Colorful X15 AT 23 Laptop The Colorful X15 AT 23 ACPI video-bus device report spurious ACPI_VIDEO_NOTIFY_CYCLE events resulting in spurious KEY_SWITCHVIDEOMODE events being reported to userspace (and causing trouble there) when an external screen plugged in. Add a quirk setting the report_key_events mask to REPORT_BRIGHTNESS_KEY_EVENTS so that the ACPI_VIDEO_NOTIFY_CYCLE events will be ignored, while still reporting brightness up/down hotkey-presses to userspace normally. Signed-off-by: Yuluo Qiu Co-developed-by: Celeste Liu Signed-off-by: Celeste Liu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_video.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 14663ba4c736..4afdda9db019 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -500,6 +500,15 @@ static const struct dmi_system_id video_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), }, }, + { + .callback = video_set_report_key_events, + .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS), + .ident = "COLORFUL X15 AT 23", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "COLORFUL"), + DMI_MATCH(DMI_PRODUCT_NAME, "X15 AT 23"), + }, + }, /* * Some machines change the brightness themselves when a brightness * hotkey gets pressed, despite us telling them not to. In this case From ba3f5058db437d919f8468db50483dd9028ff688 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 28 Nov 2023 05:52:10 +0300 Subject: [PATCH 33/56] PNP: ACPI: fix fortify warning When compiling with gcc version 14.0.0 20231126 (experimental) and CONFIG_FORTIFY_SOURCE=y, I've noticed the following: In file included from ./include/linux/string.h:295, from ./include/linux/bitmap.h:12, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/paravirt.h:17, from ./arch/x86/include/asm/cpuid.h:62, from ./arch/x86/include/asm/processor.h:19, from ./arch/x86/include/asm/cpufeature.h:5, from ./arch/x86/include/asm/thread_info.h:53, from ./include/linux/thread_info.h:60, from ./arch/x86/include/asm/preempt.h:9, from ./include/linux/preempt.h:79, from ./include/linux/spinlock.h:56, from ./include/linux/mmzone.h:8, from ./include/linux/gfp.h:7, from ./include/linux/slab.h:16, from ./include/linux/resource_ext.h:11, from ./include/linux/acpi.h:13, from drivers/pnp/pnpacpi/rsparser.c:11: In function 'fortify_memcpy_chk', inlined from 'pnpacpi_parse_allocated_vendor' at drivers/pnp/pnpacpi/rsparser.c:158:3, inlined from 'pnpacpi_allocated_resource' at drivers/pnp/pnpacpi/rsparser.c:249:3: ./include/linux/fortify-string.h:588:25: warning: call to '__read_overflow2_field' declared with attribute warning: detected read beyond size of field (2nd parameter); maybe use struct_group()? [-Wattribute-warning] 588 | __read_overflow2_field(q_size_field, size); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ According to the comments in include/linux/fortify-string.h, 'memcpy()', 'memmove()' and 'memset()' must not be used beyond individual struct members to ensure that the compiler can enforce protection against buffer overflows, and, IIUC, this also applies to partial copies from the particular member ('vendor->byte_data' in this case). So it should be better (and safer) to do both copies at once (and 'byte_data' of 'struct acpi_resource_vendor_typed' seems to be a good candidate for '__counted_by(byte_length)' as well). Signed-off-by: Dmitry Antipov Reviewed-by: Kees Cook Signed-off-by: Rafael J. Wysocki --- drivers/pnp/pnpacpi/rsparser.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 4f05f610391b..c02ce0834c2c 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -151,13 +151,13 @@ static int vendor_resource_matches(struct pnp_dev *dev, static void pnpacpi_parse_allocated_vendor(struct pnp_dev *dev, struct acpi_resource_vendor_typed *vendor) { - if (vendor_resource_matches(dev, vendor, &hp_ccsr_uuid, 16)) { - u64 start, length; + struct { u64 start, length; } range; - memcpy(&start, vendor->byte_data, sizeof(start)); - memcpy(&length, vendor->byte_data + 8, sizeof(length)); - - pnp_add_mem_resource(dev, start, start + length - 1, 0); + if (vendor_resource_matches(dev, vendor, &hp_ccsr_uuid, + sizeof(range))) { + memcpy(&range, vendor->byte_data, sizeof(range)); + pnp_add_mem_resource(dev, range.start, range.start + + range.length - 1, 0); } } From 392829ede37f36efa2e0f034631594786a9c8139 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 29 Nov 2023 14:46:51 +0100 Subject: [PATCH 34/56] ACPI: OSL: Rework error handling in acpi_os_execute() Reduce the number of checks and goto labels related to error handling in acpi_os_execute() and drop the status local variable, which turns out to be redundant, from it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- drivers/acpi/osl.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index d56dda795118..603057f6c63e 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1060,7 +1060,6 @@ int __init acpi_debugger_init(void) acpi_status acpi_os_execute(acpi_execute_type type, acpi_osd_exec_callback function, void *context) { - acpi_status status = AE_OK; struct acpi_os_dpc *dpc; struct workqueue_struct *queue; int ret; @@ -1073,9 +1072,9 @@ acpi_status acpi_os_execute(acpi_execute_type type, ret = acpi_debugger_create_thread(function, context); if (ret) { pr_err("Kernel thread creation failed\n"); - status = AE_ERROR; + return AE_ERROR; } - goto out_thread; + return AE_OK; } /* @@ -1107,12 +1106,9 @@ acpi_status acpi_os_execute(acpi_execute_type type, INIT_WORK(&dpc->work, acpi_os_execute_deferred); } else { pr_err("Unsupported os_execute type %d.\n", type); - status = AE_ERROR; + goto err; } - if (ACPI_FAILURE(status)) - goto err_workqueue; - /* * On some machines, a software-initiated SMI causes corruption unless * the SMI runs on CPU 0. An SMI can be initiated by any AML, but @@ -1123,13 +1119,14 @@ acpi_status acpi_os_execute(acpi_execute_type type, ret = queue_work_on(0, queue, &dpc->work); if (!ret) { pr_err("Unable to queue work\n"); - status = AE_ERROR; + goto err; } -err_workqueue: - if (ACPI_FAILURE(status)) - kfree(dpc); -out_thread: - return status; + + return AE_OK; + +err: + kfree(dpc); + return AE_ERROR; } EXPORT_SYMBOL(acpi_os_execute); From 3f3a2599374ede5ac47ca89981ff8dd8f304d915 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 29 Nov 2023 14:48:22 +0100 Subject: [PATCH 35/56] ACPI: OSL: Rearrange workqueue selection in acpi_os_execute() Replace the 3-branch if () statement used for selecting the target workqueue in acpi_os_execute() with a switch () one that is more suitable for this purpose and carry out the work item initialization before it to avoid code duplication. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- drivers/acpi/osl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 603057f6c63e..5eacf807d552 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1092,19 +1092,21 @@ acpi_status acpi_os_execute(acpi_execute_type type, dpc->function = function; dpc->context = context; + INIT_WORK(&dpc->work, acpi_os_execute_deferred); /* * To prevent lockdep from complaining unnecessarily, make sure that * there is a different static lockdep key for each workqueue by using * INIT_WORK() for each of them separately. */ - if (type == OSL_NOTIFY_HANDLER) { + switch (type) { + case OSL_NOTIFY_HANDLER: queue = kacpi_notify_wq; - INIT_WORK(&dpc->work, acpi_os_execute_deferred); - } else if (type == OSL_GPE_HANDLER) { + break; + case OSL_GPE_HANDLER: queue = kacpid_wq; - INIT_WORK(&dpc->work, acpi_os_execute_deferred); - } else { + break; + default: pr_err("Unsupported os_execute type %d.\n", type); goto err; } From e2ffcda1629012a2c1a3706432bc45fdc899a584 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 29 Nov 2023 14:50:54 +0100 Subject: [PATCH 36/56] ACPI: OSL: Allow Notify () handlers to run on all CPUs Notify () handlers, like GPE handlers, are only allowed to run on CPU0 now out of the concern that they might trigger an SMM trap leading to memory corruption. Namely, in some cases, SMM code might corrupt memory if not run on CPU0. However, Notify () handlers are registered by kernel code and they are not likely to evaluate AML that would trigger an SMM trap. In fact, many of them don't even evaluate any AML at all and even if they do, that AML may as well be evaluated in other code paths. In other words, they are not special from the AML evaluation perspective, so there is no real reason to treat them in any special way. Accordingly, allow Notify () handlers, unlike GPE handlers, to be executed by all CPUs in the system. Also adjust the allocation of the "notify" workqueue to allow multiple handlers to be executed at the same time, because they need not be serialized. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- drivers/acpi/osl.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5eacf807d552..a55cb578741a 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1061,7 +1061,6 @@ acpi_status acpi_os_execute(acpi_execute_type type, acpi_osd_exec_callback function, void *context) { struct acpi_os_dpc *dpc; - struct workqueue_struct *queue; int ret; ACPI_DEBUG_PRINT((ACPI_DB_EXEC, @@ -1101,24 +1100,22 @@ acpi_status acpi_os_execute(acpi_execute_type type, */ switch (type) { case OSL_NOTIFY_HANDLER: - queue = kacpi_notify_wq; + ret = queue_work(kacpi_notify_wq, &dpc->work); break; case OSL_GPE_HANDLER: - queue = kacpid_wq; + /* + * On some machines, a software-initiated SMI causes corruption + * unless the SMI runs on CPU 0. An SMI can be initiated by + * any AML, but typically it's done in GPE-related methods that + * are run via workqueues, so we can avoid the known corruption + * cases by always queueing on CPU 0. + */ + ret = queue_work_on(0, kacpid_wq, &dpc->work); break; default: pr_err("Unsupported os_execute type %d.\n", type); goto err; } - - /* - * On some machines, a software-initiated SMI causes corruption unless - * the SMI runs on CPU 0. An SMI can be initiated by any AML, but - * typically it's done in GPE-related methods that are run via - * workqueues, so we can avoid the known corruption cases by always - * queueing on CPU 0. - */ - ret = queue_work_on(0, queue, &dpc->work); if (!ret) { pr_err("Unable to queue work\n"); goto err; @@ -1668,7 +1665,7 @@ acpi_status __init acpi_os_initialize(void) acpi_status __init acpi_os_initialize1(void) { kacpid_wq = alloc_workqueue("kacpid", 0, 1); - kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1); + kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 0); kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0); BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); From 72d9b9747e78979510e9aafdd32eb99c7aa30dd1 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Mon, 4 Dec 2023 13:00:37 -0500 Subject: [PATCH 37/56] ACPI: extlog: fix NULL pointer dereference check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gcc plugin -fanalyzer [1] tries to detect various patterns of incorrect behaviour. The tool reports: drivers/acpi/acpi_extlog.c: In function ‘extlog_exit’: drivers/acpi/acpi_extlog.c:307:12: warning: check of ‘extlog_l1_addr’ for NULL after already dereferencing it [-Wanalyzer-deref-before-check] | | 306 | ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~ | | | | | (1) pointer ‘extlog_l1_addr’ is dereferenced here | 307 | if (extlog_l1_addr) | | ~ | | | | | (2) pointer ‘extlog_l1_addr’ is checked for NULL here but it was already dereferenced at (1) | Fix the NULL pointer dereference check in extlog_exit(). Link: https://gcc.gnu.org/onlinedocs/gcc-10.1.0/gcc/Static-Analyzer-Options.html # [1] Signed-off-by: Prarit Bhargava Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_extlog.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c index e120a96e1eae..193147769146 100644 --- a/drivers/acpi/acpi_extlog.c +++ b/drivers/acpi/acpi_extlog.c @@ -303,9 +303,10 @@ err: static void __exit extlog_exit(void) { mce_unregister_decode_chain(&extlog_mce_dec); - ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; - if (extlog_l1_addr) + if (extlog_l1_addr) { + ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; acpi_os_unmap_iomem(extlog_l1_addr, l1_size); + } if (elog_addr) acpi_os_unmap_iomem(elog_addr, elog_size); release_mem_region(elog_base, elog_size); From 8a3134a02538096e35dddc3c80676505c2edf57c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 5 Dec 2023 14:50:45 -0800 Subject: [PATCH 38/56] ACPI: watchdog: fix kernel-doc warnings Fix kernel-doc warnings found when using "W=1". acpi_watchdog.c:85: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst acpi_watchdog.c:85: warning: missing initial short description on line: * Returns true if this system should prefer ACPI based watchdog instead of Signed-off-by: Randy Dunlap Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_watchdog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c index ca28183f4d13..8e9e001da38f 100644 --- a/drivers/acpi/acpi_watchdog.c +++ b/drivers/acpi/acpi_watchdog.c @@ -81,7 +81,7 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void) return wdat; } -/** +/* * Returns true if this system should prefer ACPI based watchdog instead of * the native one (which are typically the same hardware). */ From 9ecc3b38abebd18998af40f42029ba84f3f4311c Mon Sep 17 00:00:00 2001 From: Yuntao Wang Date: Wed, 6 Dec 2023 18:43:16 +0800 Subject: [PATCH 39/56] ACPI: NUMA: Remove unnecessary check in acpi_parse_gi_affinity() The acpi_map_pxm_to_node() function will never return a node value that is greater than or equal to MAX_NUMNODES. Remove the unnecessary `node >= MAX_NUMNODES` check to keep the code consistent with other users of the acpi_map_pxm_to_node() function. Signed-off-by: Yuntao Wang Signed-off-by: Rafael J. Wysocki --- drivers/acpi/numa/srat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index 12f330b0eac0..9d2d0deb256e 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -430,7 +430,7 @@ acpi_parse_gi_affinity(union acpi_subtable_headers *header, return -EINVAL; node = acpi_map_pxm_to_node(gi_affinity->proximity_domain); - if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { + if (node == NUMA_NO_NODE) { pr_err("SRAT: Too many proximity domains.\n"); return -EINVAL; } From ec0f96260737ae23dcad57463fdc32eba025b11d Mon Sep 17 00:00:00 2001 From: Yuntao Wang Date: Wed, 6 Dec 2023 18:43:17 +0800 Subject: [PATCH 40/56] ACPI: NUMA: Optimize the check for the availability of node values The first_unset_node() function returns the first unused node in nodes_found_map. If all nodes are in use, the function returns MAX_NUMNODES. Use this return value to determine whether there are any available node values in nodes_found_map, eliminating the need to use nodes_weight() for this purpose. Signed-off-by: Yuntao Wang [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/numa/srat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index 9d2d0deb256e..d58e5ef424f2 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -67,9 +67,9 @@ int acpi_map_pxm_to_node(int pxm) node = pxm_to_node_map[pxm]; if (node == NUMA_NO_NODE) { - if (nodes_weight(nodes_found_map) >= MAX_NUMNODES) - return NUMA_NO_NODE; node = first_unset_node(nodes_found_map); + if (node >= MAX_NUMNODES) + return NUMA_NO_NODE; __acpi_map_pxm_to_node(pxm, node); node_set(node, nodes_found_map); } From e3f577830ce216b0ca21d4750cbbd64cfc21efff Mon Sep 17 00:00:00 2001 From: Yuntao Wang Date: Wed, 6 Dec 2023 18:43:18 +0800 Subject: [PATCH 41/56] ACPI: NUMA: Fix the logic of getting the fake_pxm value The for loop does not iterate over the last element of the node_to_pxm_map array. This could lead to a conflict between the final fake_pxm value and the existing pxm values. That is, the final fake_pxm value can not be guaranteed to be an unused pxm value. While at it, fix up white space in slit_valid(). Signed-off-by: Yuntao Wang [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/numa/srat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index d58e5ef424f2..0214518fc582 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -183,7 +183,7 @@ static int __init slit_valid(struct acpi_table_slit *slit) int i, j; int d = slit->locality_count; for (i = 0; i < d; i++) { - for (j = 0; j < d; j++) { + for (j = 0; j < d; j++) { u8 val = slit->entry[d*i + j]; if (i == j) { if (val != LOCAL_DISTANCE) @@ -532,7 +532,7 @@ int __init acpi_numa_init(void) */ /* fake_pxm is the next unused PXM value after SRAT parsing */ - for (i = 0, fake_pxm = -1; i < MAX_NUMNODES - 1; i++) { + for (i = 0, fake_pxm = -1; i < MAX_NUMNODES; i++) { if (node_to_pxm_map[i] > fake_pxm) fake_pxm = node_to_pxm_map[i]; } From 3ebccf1d1ca74bbb78e6f8c38d1d172e468d91f8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 11 Dec 2023 13:14:29 +0200 Subject: [PATCH 42/56] ACPI: LPSS: Fix the fractional clock divider flags The conversion to CLK_FRAC_DIVIDER_POWER_OF_TWO_PS uses wrong flags in the parameters and hence miscalculates the values in the clock divider. Fix this by applying the flag to the proper parameter. Fixes: 82f53f9ee577 ("clk: fractional-divider: Introduce POWER_OF_TWO_PS flag") Reported-by: Alex Vinarskis Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 875de44961bf..d48407472dfb 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -461,8 +461,9 @@ static int register_device_clock(struct acpi_device *adev, if (!clk_name) return -ENOMEM; clk = clk_register_fractional_divider(NULL, clk_name, parent, + 0, prv_base, 1, 15, 16, 15, CLK_FRAC_DIVIDER_POWER_OF_TWO_PS, - prv_base, 1, 15, 16, 15, 0, NULL); + NULL); parent = clk_name; clk_name = kasprintf(GFP_KERNEL, "%s-update", devname); From 38c872a9e96f72f2947affc0526cc05659367d3d Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 12 Dec 2023 13:22:39 -0800 Subject: [PATCH 43/56] ACPI: extlog: Clear Extended Error Log status when RAS_CEC handled the error When both CONFIG_RAS_CEC and CONFIG_ACPI_EXTLOG are enabled, Linux does not clear the status word of the BIOS supplied error record for corrected errors. This may prevent logging of subsequent uncorrected errors. Fix by clearing the status. Fixes: 23ba710a0864 ("x86/mce: Fix all mce notifiers to update the mce->kflags bitmask") Reported-by: Erwin Tsaur Signed-off-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_extlog.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c index 193147769146..ca87a0939135 100644 --- a/drivers/acpi/acpi_extlog.c +++ b/drivers/acpi/acpi_extlog.c @@ -145,9 +145,14 @@ static int extlog_print(struct notifier_block *nb, unsigned long val, static u32 err_seq; estatus = extlog_elog_entry_check(cpu, bank); - if (estatus == NULL || (mce->kflags & MCE_HANDLED_CEC)) + if (!estatus) return NOTIFY_DONE; + if (mce->kflags & MCE_HANDLED_CEC) { + estatus->block_status = 0; + return NOTIFY_DONE; + } + memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN); /* clear record status to enable BIOS to update it again */ estatus->block_status = 0; From ccb45b34d44016b91fa75646741d317d6d6fdeea Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 12 Dec 2023 22:48:38 +0100 Subject: [PATCH 44/56] ACPI: arm64: export acpi_arch_thermal_cpufreq_pctg() The cpufreq code can be in a loadable module, so the architecture support for it has to be exported: ERROR: modpost: "acpi_arch_thermal_cpufreq_pctg" [drivers/acpi/processor.ko] undefined! Fixes: 310293a2b941 ("ACPI: processor: reduce CPUFREQ thermal reduction pctg for Tegra241") Signed-off-by: Arnd Bergmann Signed-off-by: Rafael J. Wysocki --- drivers/acpi/arm64/thermal_cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/arm64/thermal_cpufreq.c b/drivers/acpi/arm64/thermal_cpufreq.c index d524f2cd6044..582854914c5c 100644 --- a/drivers/acpi/arm64/thermal_cpufreq.c +++ b/drivers/acpi/arm64/thermal_cpufreq.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include "../internal.h" @@ -18,3 +19,4 @@ int acpi_arch_thermal_cpufreq_pctg(void) return 0; } +EXPORT_SYMBOL_GPL(acpi_arch_thermal_cpufreq_pctg); From 87824da27b0aee399600d313667c1d812c2749d8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Dec 2023 21:05:19 +0100 Subject: [PATCH 45/56] ACPI: utils: Rearrange in acpi_evaluate_reference() The code in acpi_evaluate_reference() can be improved in some ways without changing its observable behavior. Among other things: * None of the local variables in that function except for buffer needs to be initialized. * The element local variable is only used in the for () loop block, so it can be defined there. * Multiple checks can be combined. * Code duplication related to error handling can be eliminated. * Redundant inner parens can be dropped. Modify the function as per the above. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/utils.c | 60 ++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 3dbc5eea0e17..5a7766c3fbbd 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -335,12 +335,10 @@ acpi_evaluate_reference(acpi_handle handle, struct acpi_object_list *arguments, struct acpi_handle_list *list) { - acpi_status status = AE_OK; - union acpi_object *package = NULL; - union acpi_object *element = NULL; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - u32 i = 0; - + union acpi_object *package; + acpi_status status; + u32 i; if (!list) return AE_BAD_PARAMETER; @@ -353,45 +351,32 @@ acpi_evaluate_reference(acpi_handle handle, package = buffer.pointer; - if ((buffer.length == 0) || !package) { + if (buffer.length == 0 || !package || + package->type != ACPI_TYPE_PACKAGE || !package->package.count) { status = AE_BAD_DATA; - acpi_util_eval_error(handle, pathname, status); - goto end; - } - if (package->type != ACPI_TYPE_PACKAGE) { - status = AE_BAD_DATA; - acpi_util_eval_error(handle, pathname, status); - goto end; - } - if (!package->package.count) { - status = AE_BAD_DATA; - acpi_util_eval_error(handle, pathname, status); - goto end; + goto err; } - list->handles = kcalloc(package->package.count, sizeof(*list->handles), GFP_KERNEL); - if (!list->handles) { - kfree(package); - return AE_NO_MEMORY; - } list->count = package->package.count; + list->handles = kcalloc(list->count, sizeof(*list->handles), GFP_KERNEL); + if (!list->handles) { + status = AE_NO_MEMORY; + goto err_clear; + } /* Extract package data. */ for (i = 0; i < list->count; i++) { - - element = &(package->package.elements[i]); + union acpi_object *element = &(package->package.elements[i]); if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { status = AE_BAD_DATA; - acpi_util_eval_error(handle, pathname, status); - break; + goto err_free; } if (!element->reference.handle) { status = AE_NULL_ENTRY; - acpi_util_eval_error(handle, pathname, status); - break; + goto err_free; } /* Get the acpi_handle. */ @@ -399,16 +384,21 @@ acpi_evaluate_reference(acpi_handle handle, acpi_handle_debug(list->handles[i], "Found in reference list\n"); } - if (ACPI_FAILURE(status)) { - list->count = 0; - kfree(list->handles); - list->handles = NULL; - } - end: kfree(buffer.pointer); return status; + +err_free: + kfree(list->handles); + list->handles = NULL; + +err_clear: + list->count = 0; + +err: + acpi_util_eval_error(handle, pathname, status); + goto end; } EXPORT_SYMBOL(acpi_evaluate_reference); From 6909e0f322b0527fee9fdc54685e6cad69008713 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Dec 2023 21:06:04 +0100 Subject: [PATCH 46/56] ACPI: utils: Return bool from acpi_evaluate_reference() There are only 4 users of acpi_evaluate_reference() and none of them actually cares about the reason why it fails. All of them are only interested in whether or not it is successful, so it can return a bool value indicating that. Modify acpi_evaluate_reference() as per the observation above and update its callers accordingly so as to get rid of useless code and local variables. The observable behavior of the kernel is not expected to change after this modification of the code. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_lpss.c | 5 +-- drivers/acpi/scan.c | 5 ++- drivers/acpi/thermal.c | 4 +-- drivers/acpi/utils.c | 32 +++++++------------ .../platform/surface/surface_acpi_notify.c | 4 +-- include/acpi/acpi_bus.h | 8 ++--- 6 files changed, 20 insertions(+), 38 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 79f4fc7d6871..1623af8d62bc 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -565,16 +565,13 @@ static struct device *acpi_lpss_find_device(const char *hid, const char *uid) static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle) { struct acpi_handle_list dep_devices; - acpi_status status; bool ret = false; int i; if (!acpi_has_method(adev->handle, "_DEP")) return false; - status = acpi_evaluate_reference(adev->handle, "_DEP", NULL, - &dep_devices); - if (ACPI_FAILURE(status)) { + if (!acpi_evaluate_reference(adev->handle, "_DEP", NULL, &dep_devices)) { dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n"); return false; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 02bb2cce423f..7b731958af5e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1984,7 +1984,6 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) { struct acpi_handle_list dep_devices; - acpi_status status; u32 count; int i; @@ -1998,8 +1997,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) !acpi_has_method(handle, "_HID")) return 0; - status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); - if (ACPI_FAILURE(status)) { + if (!acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices)) { acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); return 0; } @@ -2008,6 +2006,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) struct acpi_device_info *info; struct acpi_dep_data *dep; bool skip, honor_dep; + acpi_status status; status = acpi_get_object_info(dep_devices.handles[i], &info); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index f74d81abdbfc..15f09c71a5ec 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -247,7 +247,6 @@ static bool update_trip_devices(struct acpi_thermal *tz, { struct acpi_handle_list devices = { 0 }; char method[] = "_PSL"; - acpi_status status; if (index != ACPI_THERMAL_TRIP_PASSIVE) { method[1] = 'A'; @@ -255,8 +254,7 @@ static bool update_trip_devices(struct acpi_thermal *tz, method[3] = '0' + index; } - status = acpi_evaluate_reference(tz->device->handle, method, NULL, &devices); - if (ACPI_FAILURE(status)) { + if (!acpi_evaluate_reference(tz->device->handle, method, NULL, &devices)) { acpi_handle_info(tz->device->handle, "%s evaluation failure\n", method); return false; } diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 5a7766c3fbbd..958dc651d467 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -329,19 +329,18 @@ const char *acpi_get_subsystem_id(acpi_handle handle) } EXPORT_SYMBOL_GPL(acpi_get_subsystem_id); -acpi_status -acpi_evaluate_reference(acpi_handle handle, - acpi_string pathname, - struct acpi_object_list *arguments, - struct acpi_handle_list *list) +bool acpi_evaluate_reference(acpi_handle handle, acpi_string pathname, + struct acpi_object_list *arguments, + struct acpi_handle_list *list) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *package; acpi_status status; + bool ret = false; u32 i; if (!list) - return AE_BAD_PARAMETER; + return false; /* Evaluate object. */ @@ -352,42 +351,35 @@ acpi_evaluate_reference(acpi_handle handle, package = buffer.pointer; if (buffer.length == 0 || !package || - package->type != ACPI_TYPE_PACKAGE || !package->package.count) { - status = AE_BAD_DATA; + package->type != ACPI_TYPE_PACKAGE || !package->package.count) goto err; - } list->count = package->package.count; list->handles = kcalloc(list->count, sizeof(*list->handles), GFP_KERNEL); - if (!list->handles) { - status = AE_NO_MEMORY; + if (!list->handles) goto err_clear; - } /* Extract package data. */ for (i = 0; i < list->count; i++) { union acpi_object *element = &(package->package.elements[i]); - if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { - status = AE_BAD_DATA; + if (element->type != ACPI_TYPE_LOCAL_REFERENCE || + !element->reference.handle) goto err_free; - } - if (!element->reference.handle) { - status = AE_NULL_ENTRY; - goto err_free; - } /* Get the acpi_handle. */ list->handles[i] = element->reference.handle; acpi_handle_debug(list->handles[i], "Found in reference list\n"); } + ret = true; + end: kfree(buffer.pointer); - return status; + return ret; err_free: kfree(list->handles); diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c index e4dee920da18..96ec052d0940 100644 --- a/drivers/platform/surface/surface_acpi_notify.c +++ b/drivers/platform/surface/surface_acpi_notify.c @@ -740,15 +740,13 @@ static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle) { struct acpi_handle_list dep_devices; acpi_handle supplier = ACPI_HANDLE(&pdev->dev); - acpi_status status; bool ret = false; int i; if (!acpi_has_method(handle, "_DEP")) return false; - status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); - if (ACPI_FAILURE(status)) { + if (!acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices)) { san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n"); return false; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index aae31552c574..fbe60af56b34 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -25,11 +25,9 @@ acpi_status acpi_evaluate_integer(acpi_handle handle, acpi_string pathname, struct acpi_object_list *arguments, unsigned long long *data); -acpi_status -acpi_evaluate_reference(acpi_handle handle, - acpi_string pathname, - struct acpi_object_list *arguments, - struct acpi_handle_list *list); +bool acpi_evaluate_reference(acpi_handle handle, acpi_string pathname, + struct acpi_object_list *arguments, + struct acpi_handle_list *list); bool acpi_handle_list_equal(struct acpi_handle_list *list1, struct acpi_handle_list *list2); void acpi_handle_list_replace(struct acpi_handle_list *dst, From 1feb042d4e9b30b3ec3363e557d2ba884485f835 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Dec 2023 21:06:45 +0100 Subject: [PATCH 47/56] ACPI: utils: Refine acpi_handle_list_equal() slightly It is somewhat better to use the size of the first array element for computing the size of the entire array than to rely on the array element data type definition knowledge and the former is also consistent with the array allocation in acpi_evaluate_reference(), so modify the code accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 958dc651d467..57663065dbf6 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -408,7 +408,7 @@ bool acpi_handle_list_equal(struct acpi_handle_list *list1, { return list1->count == list2->count && !memcmp(list1->handles, list2->handles, - list1->count * sizeof(acpi_handle)); + list1->count * sizeof(*list1->handles)); } EXPORT_SYMBOL_GPL(acpi_handle_list_equal); From 4c660ffef34b7d645ae3144369bc50257f295212 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 Dec 2023 21:07:41 +0100 Subject: [PATCH 48/56] ACPI: utils: Fix white space in struct acpi_handle_list definition Fix inadvertently introduced white space damage in the struct acpi_handle_list definition. No functional impact. Fixes: 2e57d10a6591 ("ACPI: utils: Dynamically determine acpi_handle_list size") Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index fbe60af56b34..3dcf07b41428 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -14,7 +14,7 @@ struct acpi_handle_list { u32 count; - acpi_handle* handles; + acpi_handle *handles; }; /* acpi_utils.h */ From d70d141bb15f328528f94557ddf754abeb027365 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 14 Dec 2023 12:07:55 +0100 Subject: [PATCH 49/56] ACPI: utils: Introduce helper for _DEP list lookup The ACPI LPSS driver and the Surface platform driver code use almost the same code pattern for checking if one ACPI device is present in the list returned by _DEP for another ACPI device. To reduce the resulting code duplication, introduce a helper for that called acpi_device_dep() and invoke it from both places. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg --- drivers/acpi/acpi_lpss.c | 29 ++-------------- drivers/acpi/utils.c | 34 +++++++++++++++++++ .../platform/surface/surface_acpi_notify.c | 28 +-------------- include/acpi/acpi_bus.h | 1 + 4 files changed, 38 insertions(+), 54 deletions(-) diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 1623af8d62bc..920402dfe1ad 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -562,31 +562,6 @@ static struct device *acpi_lpss_find_device(const char *hid, const char *uid) return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid); } -static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle) -{ - struct acpi_handle_list dep_devices; - bool ret = false; - int i; - - if (!acpi_has_method(adev->handle, "_DEP")) - return false; - - if (!acpi_evaluate_reference(adev->handle, "_DEP", NULL, &dep_devices)) { - dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n"); - return false; - } - - for (i = 0; i < dep_devices.count; i++) { - if (dep_devices.handles[i] == handle) { - ret = true; - break; - } - } - - acpi_handle_list_free(&dep_devices); - return ret; -} - static void acpi_lpss_link_consumer(struct device *dev1, const struct lpss_device_links *link) { @@ -597,7 +572,7 @@ static void acpi_lpss_link_consumer(struct device *dev1, return; if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) - || acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1))) + || acpi_device_dep(ACPI_HANDLE(dev2), ACPI_HANDLE(dev1))) device_link_add(dev2, dev1, link->flags); put_device(dev2); @@ -613,7 +588,7 @@ static void acpi_lpss_link_supplier(struct device *dev1, return; if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) - || acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2))) + || acpi_device_dep(ACPI_HANDLE(dev1), ACPI_HANDLE(dev2))) device_link_add(dev1, dev2, link->flags); put_device(dev2); diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 57663065dbf6..abac5cc25477 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -450,6 +450,40 @@ void acpi_handle_list_free(struct acpi_handle_list *list) } EXPORT_SYMBOL_GPL(acpi_handle_list_free); +/** + * acpi_device_dep - Check ACPI device dependency + * @target: ACPI handle of the target ACPI device. + * @match: ACPI handle to look up in the target's _DEP list. + * + * Return true if @match is present in the list returned by _DEP for + * @target or false otherwise. + */ +bool acpi_device_dep(acpi_handle target, acpi_handle match) +{ + struct acpi_handle_list dep_devices; + bool ret = false; + int i; + + if (!acpi_has_method(target, "_DEP")) + return false; + + if (!acpi_evaluate_reference(target, "_DEP", NULL, &dep_devices)) { + acpi_handle_debug(target, "Failed to evaluate _DEP.\n"); + return false; + } + + for (i = 0; i < dep_devices.count; i++) { + if (dep_devices.handles[i] == match) { + ret = true; + break; + } + } + + acpi_handle_list_free(&dep_devices); + return ret; +} +EXPORT_SYMBOL_GPL(acpi_device_dep); + acpi_status acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld) { diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c index 96ec052d0940..20f3870915d2 100644 --- a/drivers/platform/surface/surface_acpi_notify.c +++ b/drivers/platform/surface/surface_acpi_notify.c @@ -736,32 +736,6 @@ do { \ #define san_consumer_warn(dev, handle, fmt, ...) \ san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__) -static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle) -{ - struct acpi_handle_list dep_devices; - acpi_handle supplier = ACPI_HANDLE(&pdev->dev); - bool ret = false; - int i; - - if (!acpi_has_method(handle, "_DEP")) - return false; - - if (!acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices)) { - san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n"); - return false; - } - - for (i = 0; i < dep_devices.count; i++) { - if (dep_devices.handles[i] == supplier) { - ret = true; - break; - } - } - - acpi_handle_list_free(&dep_devices); - return ret; -} - static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl, void *context, void **rv) { @@ -770,7 +744,7 @@ static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl, struct acpi_device *adev; struct device_link *link; - if (!is_san_consumer(pdev, handle)) + if (!acpi_device_dep(handle, ACPI_HANDLE(&pdev->dev))) return AE_OK; /* Ignore ACPI devices that are not present. */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 3dcf07b41428..b5c082e34539 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -33,6 +33,7 @@ bool acpi_handle_list_equal(struct acpi_handle_list *list1, void acpi_handle_list_replace(struct acpi_handle_list *dst, struct acpi_handle_list *src); void acpi_handle_list_free(struct acpi_handle_list *list); +bool acpi_device_dep(acpi_handle target, acpi_handle match); acpi_status acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, struct acpi_buffer *status_buf); From a70297d2213253853e95f5b49651f924990c6d3b Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Mon, 18 Dec 2023 14:45:18 +0800 Subject: [PATCH 50/56] ACPI: APEI: set memory failure flags as MF_ACTION_REQUIRED on synchronous events There are two major types of uncorrected recoverable (UCR) errors : - Synchronous error: The error is detected and raised at the point of the consumption in the execution flow, e.g. when a CPU tries to access a poisoned cache line. The CPU will take a synchronous error exception such as Synchronous External Abort (SEA) on Arm64 and Machine Check Exception (MCE) on X86. OS requires to take action (for example, offline failure page/kill failure thread) to recover this uncorrectable error. - Asynchronous error: The error is detected out of processor execution context, e.g. when an error is detected by a background scrubber. Some data in the memory are corrupted. But the data have not been consumed. OS is optional to take action to recover this uncorrectable error. When APEI firmware first is enabled, a platform may describe one error source for the handling of synchronous errors (e.g. MCE or SEA notification ), or for handling asynchronous errors (e.g. SCI or External Interrupt notification). In other words, we can distinguish synchronous errors by APEI notification. For synchronous errors, kernel will kill the current process which accessing the poisoned page by sending SIGBUS with BUS_MCEERR_AR. In addition, for asynchronous errors, kernel will notify the process who owns the poisoned page by sending SIGBUS with BUS_MCEERR_AO in early kill mode. However, the GHES driver always sets mf_flags to 0 so that all synchronous errors are handled as asynchronous errors in memory failure. To this end, set memory failure flags as MF_ACTION_REQUIRED on synchronous events. Signed-off-by: Shuai Xue Tested-by: Ma Wupeng Reviewed-by: Kefeng Wang Reviewed-by: Xiaofei Tan Reviewed-by: Baolin Wang Reviewed-by: James Morse Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 63ad0541db38..ab2a82cb1b0b 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -101,6 +101,20 @@ static inline bool is_hest_type_generic_v2(struct ghes *ghes) return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2; } +/* + * A platform may describe one error source for the handling of synchronous + * errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI + * or External Interrupt). On x86, the HEST notifications are always + * asynchronous, so only SEA on ARM is delivered as a synchronous + * notification. + */ +static inline bool is_hest_sync_notify(struct ghes *ghes) +{ + u8 notify_type = ghes->generic->notify.type; + + return notify_type == ACPI_HEST_NOTIFY_SEA; +} + /* * This driver isn't really modular, however for the time being, * continuing to use module_param is the easiest way to remain @@ -489,7 +503,7 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags) } static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, - int sev) + int sev, bool sync) { int flags = -1; int sec_sev = ghes_severity(gdata->error_severity); @@ -503,7 +517,7 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) flags = MF_SOFT_OFFLINE; if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) - flags = 0; + flags = sync ? MF_ACTION_REQUIRED : 0; if (flags != -1) return ghes_do_memory_failure(mem_err->physical_addr, flags); @@ -511,9 +525,11 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, return false; } -static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int sev) +static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, + int sev, bool sync) { struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata); + int flags = sync ? MF_ACTION_REQUIRED : 0; bool queued = false; int sec_sev, i; char *p; @@ -538,7 +554,7 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int s * and don't filter out 'corrected' error here. */ if (is_cache && has_pa) { - queued = ghes_do_memory_failure(err_info->physical_fault_addr, 0); + queued = ghes_do_memory_failure(err_info->physical_fault_addr, flags); p += err_info->length; continue; } @@ -666,6 +682,7 @@ static bool ghes_do_proc(struct ghes *ghes, const guid_t *fru_id = &guid_null; char *fru_text = ""; bool queued = false; + bool sync = is_hest_sync_notify(ghes); sev = ghes_severity(estatus->error_severity); apei_estatus_for_each_section(estatus, gdata) { @@ -683,13 +700,13 @@ static bool ghes_do_proc(struct ghes *ghes, atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err); arch_apei_report_mem_error(sev, mem_err); - queued = ghes_handle_memory_failure(gdata, sev); + queued = ghes_handle_memory_failure(gdata, sev, sync); } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { ghes_handle_aer(gdata); } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { - queued = ghes_handle_arm_hw_error(gdata, sev); + queued = ghes_handle_arm_hw_error(gdata, sev, sync); } else { void *err = acpi_hest_get_payload(gdata); From 8e57de43076477c5cce113f2579bef02ce3e8b27 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Dec 2023 12:25:15 +0100 Subject: [PATCH 51/56] ACPI: OSL: Use spin locks without disabling interrupts After commit 7a36b901a6eb ("ACPI: OSL: Use a threaded interrupt handler for SCI") any ACPICA code never runs in a hardirq handler, so it need not dissable interrupts on the local CPU when acquiring a spin lock. Make it use spin locks without disabling interrupts. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index a55cb578741a..70af3fbbebe5 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1515,20 +1515,18 @@ void acpi_os_delete_lock(acpi_spinlock handle) acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp) __acquires(lockp) { - acpi_cpu_flags flags; - - spin_lock_irqsave(lockp, flags); - return flags; + spin_lock(lockp); + return 0; } /* * Release a spinlock. See above. */ -void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags) +void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags not_used) __releases(lockp) { - spin_unlock_irqrestore(lockp, flags); + spin_unlock(lockp); } #ifndef ACPI_USE_LOCAL_CACHE From 655a6e7c0d83d47c36218525708c9fcfdd7f4b43 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Dec 2023 12:26:33 +0100 Subject: [PATCH 52/56] ACPI: EC: Use a threaded handler for dedicated IRQ After commit 7a36b901a6eb ("ACPI: OSL: Use a threaded interrupt handler for SCI") all of the EC code runs in thread context on all systems where EC events are signaled through a GPE. It may as well run in thread context on systems using a dedicated IRQ for EC events signaling, so make it use a threaded handler for that IRQ. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a59c11df7375..02255795b800 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1458,8 +1458,8 @@ static bool install_gpe_event_handler(struct acpi_ec *ec) static bool install_gpio_irq_event_handler(struct acpi_ec *ec) { - return request_irq(ec->irq, acpi_ec_irq_handler, IRQF_SHARED, - "ACPI EC", ec) >= 0; + return request_threaded_irq(ec->irq, NULL, acpi_ec_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, "ACPI EC", ec) >= 0; } /** From eb9299beadbdd7be8de1e97f1059e89bcb64b05d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Dec 2023 12:27:30 +0100 Subject: [PATCH 53/56] ACPI: EC: Use a spin lock without disabing interrupts Since all of the ACPI EC driver code runs in thread context after recent changes, it does not need to disable interrupts on the local CPU when acquiring a spin lock. Make it use the spin lock without disabling interrupts. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 112 +++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 66 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 02255795b800..dbdee2924594 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -525,12 +525,10 @@ static void acpi_ec_clear(struct acpi_ec *ec) static void acpi_ec_enable_event(struct acpi_ec *ec) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); if (acpi_ec_started(ec)) __acpi_ec_enable_event(ec); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); /* Drain additional events if hardware requires that */ if (EC_FLAGS_CLEAR_ON_RESUME) @@ -546,11 +544,9 @@ static void __acpi_ec_flush_work(void) static void acpi_ec_disable_event(struct acpi_ec *ec) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); __acpi_ec_disable_event(ec); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); /* * When ec_freeze_events is true, we need to flush events in @@ -571,10 +567,9 @@ void acpi_ec_flush_work(void) static bool acpi_ec_guard_event(struct acpi_ec *ec) { - unsigned long flags; bool guarded; - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); /* * If firmware SCI_EVT clearing timing is "event", we actually * don't know when the SCI_EVT will be cleared by firmware after @@ -590,31 +585,29 @@ static bool acpi_ec_guard_event(struct acpi_ec *ec) guarded = ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && ec->event_state != EC_EVENT_READY && (!ec->curr || ec->curr->command != ACPI_EC_COMMAND_QUERY); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); return guarded; } static int ec_transaction_polled(struct acpi_ec *ec) { - unsigned long flags; int ret = 0; - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL)) ret = 1; - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); return ret; } static int ec_transaction_completed(struct acpi_ec *ec) { - unsigned long flags; int ret = 0; - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) ret = 1; - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); return ret; } @@ -756,7 +749,6 @@ static int ec_guard(struct acpi_ec *ec) static int ec_poll(struct acpi_ec *ec) { - unsigned long flags; int repeat = 5; /* number of command restarts */ while (repeat--) { @@ -765,14 +757,14 @@ static int ec_poll(struct acpi_ec *ec) do { if (!ec_guard(ec)) return 0; - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); advance_transaction(ec, false); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } while (time_before(jiffies, delay)); pr_debug("controller reset, restart transaction\n"); - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); start_transaction(ec); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } return -ETIME; } @@ -780,11 +772,10 @@ static int ec_poll(struct acpi_ec *ec) static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, struct transaction *t) { - unsigned long tmp; int ret = 0; /* start transaction */ - spin_lock_irqsave(&ec->lock, tmp); + spin_lock(&ec->lock); /* Enable GPE for command processing (IBF=0/OBF=1) */ if (!acpi_ec_submit_flushable_request(ec)) { ret = -EINVAL; @@ -795,11 +786,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ec->curr = t; ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command)); start_transaction(ec); - spin_unlock_irqrestore(&ec->lock, tmp); + spin_unlock(&ec->lock); ret = ec_poll(ec); - spin_lock_irqsave(&ec->lock, tmp); + spin_lock(&ec->lock); if (t->irq_count == ec_storm_threshold) acpi_ec_unmask_events(ec); ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command)); @@ -808,7 +799,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, acpi_ec_complete_request(ec); ec_dbg_ref(ec, "Decrease command"); unlock: - spin_unlock_irqrestore(&ec->lock, tmp); + spin_unlock(&ec->lock); return ret; } @@ -936,9 +927,7 @@ EXPORT_SYMBOL(ec_get_handle); static void acpi_ec_start(struct acpi_ec *ec, bool resuming) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { ec_dbg_drv("Starting EC"); /* Enable GPE for event processing (SCI_EVT=1) */ @@ -948,31 +937,28 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming) } ec_log_drv("EC started"); } - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } static bool acpi_ec_stopped(struct acpi_ec *ec) { - unsigned long flags; bool flushed; - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); flushed = acpi_ec_flushed(ec); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); return flushed; } static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); if (acpi_ec_started(ec)) { ec_dbg_drv("Stopping EC"); set_bit(EC_FLAGS_STOPPED, &ec->flags); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); wait_event(ec->wait, acpi_ec_stopped(ec)); - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); /* Disable GPE for event processing (SCI_EVT=1) */ if (!suspending) { acpi_ec_complete_request(ec); @@ -983,29 +969,25 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) clear_bit(EC_FLAGS_STOPPED, &ec->flags); ec_log_drv("EC stopped"); } - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } static void acpi_ec_enter_noirq(struct acpi_ec *ec) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); ec->busy_polling = true; ec->polling_guard = 0; ec_log_drv("interrupt blocked"); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } static void acpi_ec_leave_noirq(struct acpi_ec *ec) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); ec->busy_polling = ec_busy_polling; ec->polling_guard = ec_polling_guard; ec_log_drv("interrupt unblocked"); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } void acpi_ec_block_transactions(void) @@ -1137,9 +1119,9 @@ static void acpi_ec_event_processor(struct work_struct *work) ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit); - spin_lock_irq(&ec->lock); + spin_lock(&ec->lock); ec->queries_in_progress--; - spin_unlock_irq(&ec->lock); + spin_unlock(&ec->lock); acpi_ec_put_query_handler(handler); kfree(q); @@ -1202,12 +1184,12 @@ static int acpi_ec_submit_query(struct acpi_ec *ec) */ ec_dbg_evt("Query(0x%02x) scheduled", value); - spin_lock_irq(&ec->lock); + spin_lock(&ec->lock); ec->queries_in_progress++; queue_work(ec_query_wq, &q->work); - spin_unlock_irq(&ec->lock); + spin_unlock(&ec->lock); return 0; @@ -1223,14 +1205,14 @@ static void acpi_ec_event_handler(struct work_struct *work) ec_dbg_evt("Event started"); - spin_lock_irq(&ec->lock); + spin_lock(&ec->lock); while (ec->events_to_process) { - spin_unlock_irq(&ec->lock); + spin_unlock(&ec->lock); acpi_ec_submit_query(ec); - spin_lock_irq(&ec->lock); + spin_lock(&ec->lock); ec->events_to_process--; } @@ -1247,11 +1229,11 @@ static void acpi_ec_event_handler(struct work_struct *work) ec_dbg_evt("Event stopped"); - spin_unlock_irq(&ec->lock); + spin_unlock(&ec->lock); guard_timeout = !!ec_guard(ec); - spin_lock_irq(&ec->lock); + spin_lock(&ec->lock); /* Take care of SCI_EVT unless someone else is doing that. */ if (guard_timeout && !ec->curr) @@ -1264,7 +1246,7 @@ static void acpi_ec_event_handler(struct work_struct *work) ec->events_in_progress--; - spin_unlock_irq(&ec->lock); + spin_unlock(&ec->lock); } static void clear_gpe_and_advance_transaction(struct acpi_ec *ec, bool interrupt) @@ -1289,13 +1271,11 @@ static void clear_gpe_and_advance_transaction(struct acpi_ec *ec, bool interrupt static void acpi_ec_handle_interrupt(struct acpi_ec *ec) { - unsigned long flags; - - spin_lock_irqsave(&ec->lock, flags); + spin_lock(&ec->lock); clear_gpe_and_advance_transaction(ec, true); - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock(&ec->lock); } static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, @@ -2105,7 +2085,7 @@ bool acpi_ec_dispatch_gpe(void) * Dispatch the EC GPE in-band, but do not report wakeup in any case * to allow the caller to process events properly after that. */ - spin_lock_irq(&first_ec->lock); + spin_lock(&first_ec->lock); if (acpi_ec_gpe_status_set(first_ec)) { pm_pr_dbg("ACPI EC GPE status set\n"); @@ -2114,7 +2094,7 @@ bool acpi_ec_dispatch_gpe(void) work_in_progress = acpi_ec_work_in_progress(first_ec); } - spin_unlock_irq(&first_ec->lock); + spin_unlock(&first_ec->lock); if (!work_in_progress) return false; @@ -2127,11 +2107,11 @@ bool acpi_ec_dispatch_gpe(void) pm_pr_dbg("ACPI EC work flushed\n"); - spin_lock_irq(&first_ec->lock); + spin_lock(&first_ec->lock); work_in_progress = acpi_ec_work_in_progress(first_ec); - spin_unlock_irq(&first_ec->lock); + spin_unlock(&first_ec->lock); } while (work_in_progress && !pm_wakeup_pending()); return false; From df0cced74159c79e36ce7971f0bf250673296d93 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 23 Dec 2023 15:57:06 +0100 Subject: [PATCH 54/56] ACPI: resource: Add another DMI match for the TongFang GMxXGxx The TongFang GMxXGxx, which needs IRQ overriding for the keyboard to work, is also sold as the Eluktronics RP-15 which does not use the standard TongFang GMxXGxx DMI board_name. Add an entry for this laptop to the irq1_edge_low_force_override[] DMI table to make the internal keyboard functional. Reported-by: Luis Acuna Signed-off-by: Hans de Goede Cc: All applicable Signed-off-by: Rafael J. Wysocki --- drivers/acpi/resource.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 9bd9f79cd409..c3536c236be9 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -510,6 +510,13 @@ static const struct dmi_system_id irq1_edge_low_force_override[] = { DMI_MATCH(DMI_BOARD_NAME, "GMxXGxx"), }, }, + { + /* TongFang GMxXGxx sold as Eluktronics Inc. RP-15 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Eluktronics Inc."), + DMI_MATCH(DMI_BOARD_NAME, "RP-15"), + }, + }, { /* TongFang GM6XGxX/TUXEDO Stellaris 16 Gen5 AMD */ .matches = { From 16f70feaabe9fde0af703f2991d44a7589f0b6e3 Mon Sep 17 00:00:00 2001 From: Ken Xue Date: Mon, 25 Dec 2023 13:58:35 +0800 Subject: [PATCH 55/56] ACPI: button: trigger wakeup key events Andorid can wakeup from various wakeup sources, but only several wakeup sources can wake up screen with right events(POWER, WAKEUP) from input device. Regarding pressing acpi power button, it can resume system and ACPI_BITMASK_WAKE_STATUS and ACPI_BITMASK_POWER_BUTTON_STATUS are set in pm1a_sts, but kernel does not report any key event to user space during resuming by default. So, send wakeup key event to user space during resume from power button. Signed-off-by: Ken Xue Reviewed-by: Andy Shevchenko [ rjw: Subject edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 1e76a64cce0a..cc61020756be 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -480,6 +480,7 @@ static int acpi_button_suspend(struct device *dev) static int acpi_button_resume(struct device *dev) { + struct input_dev *input; struct acpi_device *device = to_acpi_device(dev); struct acpi_button *button = acpi_driver_data(device); @@ -489,6 +490,14 @@ static int acpi_button_resume(struct device *dev) button->last_time = ktime_get(); acpi_lid_initialize_state(device); } + + if (button->type == ACPI_BUTTON_TYPE_POWER) { + input = button->input; + input_report_key(input, KEY_WAKEUP, 1); + input_sync(input); + input_report_key(input, KEY_WAKEUP, 0); + input_sync(input); + } return 0; } #endif @@ -579,6 +588,7 @@ static int acpi_button_add(struct acpi_device *device) switch (button->type) { case ACPI_BUTTON_TYPE_POWER: input_set_capability(input, EV_KEY, KEY_POWER); + input_set_capability(input, EV_KEY, KEY_WAKEUP); break; case ACPI_BUTTON_TYPE_SLEEP: From e2605d4039a42a03000856b3229932455717b48b Mon Sep 17 00:00:00 2001 From: David McFarland Date: Wed, 3 Jan 2024 12:55:18 -0400 Subject: [PATCH 56/56] ACPI: resource: Add Infinity laptops to irq1_edge_low_force_override A user reported a keyboard problem similar to ones reported with other Zen laptops, on an Infinity E15-5A165-BM. Add board name matches for this model and one (untested) close relative to irq1_edge_low_force_override. Link: https://lemmy.ml/post/9864736 Link: https://www.infinitygaming.com.au/bios/ Link: https://lore.kernel.org/linux-acpi/20231006123304.32686-1-hdegoede@redhat.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/resource.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index c3536c236be9..0e2c397b1399 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -555,6 +555,18 @@ static const struct dmi_system_id irq1_edge_low_force_override[] = { DMI_MATCH(DMI_BOARD_NAME, "GM6BG0Q"), }, }, + { + /* Infinity E15-5A165-BM */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GM5RG1E0009COM"), + }, + }, + { + /* Infinity E15-5A305-1M */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GM5RGEE0016COM"), + }, + }, { } };