summaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-07 13:11:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-07 13:11:27 +0000
commit34996e42f82bfd60bc2c191e5cae3c6ab233ec6c (patch)
tree62db60558cbf089714b48daeabca82bf2b20b20e /drivers/acpi
parentAdding debian version 6.8.12-1. (diff)
downloadlinux-34996e42f82bfd60bc2c191e5cae3c6ab233ec6c.tar.xz
linux-34996e42f82bfd60bc2c191e5cae3c6ab233ec6c.zip
Merging upstream version 6.9.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig17
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/acpi_processor.c5
-rw-r--r--drivers/acpi/acpi_tad.c5
-rw-r--r--drivers/acpi/acpi_video.c28
-rw-r--r--drivers/acpi/acpi_watchdog.c2
-rw-r--r--drivers/acpi/acpica/acevents.h4
-rw-r--r--drivers/acpi/acpica/evregion.c6
-rw-r--r--drivers/acpi/acpica/evxfregn.c54
-rw-r--r--drivers/acpi/acpica/exregion.c23
-rw-r--r--drivers/acpi/apei/Kconfig13
-rw-r--r--drivers/acpi/apei/Makefile2
-rw-r--r--drivers/acpi/apei/apei-internal.h18
-rw-r--r--drivers/acpi/apei/einj-core.c (renamed from drivers/acpi/apei/einj.c)122
-rw-r--r--drivers/acpi/apei/einj-cxl.c113
-rw-r--r--drivers/acpi/apei/ghes.c17
-rw-r--r--drivers/acpi/apei/hest.c51
-rw-r--r--drivers/acpi/arm64/agdi.c8
-rw-r--r--drivers/acpi/bus.c2
-rw-r--r--drivers/acpi/cppc_acpi.c13
-rw-r--r--drivers/acpi/custom_method.c103
-rw-r--r--drivers/acpi/dock.c2
-rw-r--r--drivers/acpi/dptf/dptf_pch_fivr.c6
-rw-r--r--drivers/acpi/dptf/dptf_power.c6
-rw-r--r--drivers/acpi/ec.c28
-rw-r--r--drivers/acpi/evged.c5
-rw-r--r--drivers/acpi/fan_core.c6
-rw-r--r--drivers/acpi/internal.h7
-rw-r--r--drivers/acpi/mipi-disco-img.c81
-rw-r--r--drivers/acpi/nfit/core.c5
-rw-r--r--drivers/acpi/numa/hmat.c34
-rw-r--r--drivers/acpi/numa/srat.c11
-rw-r--r--drivers/acpi/pci_slot.c2
-rw-r--r--drivers/acpi/pfr_telemetry.c6
-rw-r--r--drivers/acpi/pfr_update.c6
-rw-r--r--drivers/acpi/processor_driver.c6
-rw-r--r--drivers/acpi/property.c3
-rw-r--r--drivers/acpi/resource.c39
-rw-r--r--drivers/acpi/riscv/Makefile4
-rw-r--r--drivers/acpi/riscv/cppc.c157
-rw-r--r--drivers/acpi/riscv/cpuidle.c81
-rw-r--r--drivers/acpi/scan.c170
-rw-r--r--drivers/acpi/sleep.c24
-rw-r--r--drivers/acpi/tables.c2
-rw-r--r--drivers/acpi/thermal.c79
-rw-r--r--drivers/acpi/thermal_lib.c8
-rw-r--r--drivers/acpi/utils.c2
-rw-r--r--drivers/acpi/video_detect.c8
-rw-r--r--drivers/acpi/x86/s2idle.c35
-rw-r--r--drivers/acpi/x86/utils.c44
50 files changed, 1067 insertions, 407 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 3c3f8037eb..ff1689bb31 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -286,7 +286,7 @@ config ACPI_CPPC_LIB
config ACPI_PROCESSOR
tristate "Processor"
- depends on X86 || ARM64 || LOONGARCH
+ depends on X86 || ARM64 || LOONGARCH || RISCV
select ACPI_PROCESSOR_IDLE
select ACPI_CPU_FREQ_PSS if X86 || LOONGARCH
select THERMAL
@@ -449,20 +449,6 @@ config ACPI_HED
which is used to report some hardware errors notified via
SCI, mainly the corrected errors.
-config ACPI_CUSTOM_METHOD
- tristate "Allow ACPI methods to be inserted/replaced at run time"
- depends on DEBUG_FS
- help
- This debug facility allows ACPI AML methods to be inserted and/or
- replaced without rebooting the system. For details refer to:
- Documentation/firmware-guide/acpi/method-customizing.rst.
-
- NOTE: This option is security sensitive, because it allows arbitrary
- kernel memory to be written to by root (uid=0) users, allowing them
- to bypass certain security measures (e.g. if root is not allowed to
- load additional kernel modules after boot, this feature may be used
- to override that restriction).
-
config ACPI_BGRT
bool "Boottime Graphics Resource Table support"
depends on EFI && (X86 || ARM64)
@@ -474,7 +460,6 @@ config ACPI_BGRT
config ACPI_REDUCED_HARDWARE_ONLY
bool "Hardware-reduced ACPI support only" if EXPERT
- def_bool n
help
This config item changes the way the ACPI code is built. When this
option is selected, the kernel will use a specialized version of
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 12ef8180d2..8cc8c0d9c8 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -101,7 +101,6 @@ obj-$(CONFIG_ACPI_SBS) += sbshc.o
obj-$(CONFIG_ACPI_SBS) += sbs.o
obj-$(CONFIG_ACPI_HED) += hed.o
obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
-obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 4fe2ef5408..7a0dd35d62 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -161,7 +161,7 @@ static void cpufreq_add_device(const char *name)
pdev = platform_device_register_simple(name, PLATFORM_DEVID_NONE, NULL, 0);
if (IS_ERR(pdev))
- pr_info("%s device creation failed: %ld\n", name, PTR_ERR(pdev));
+ pr_info("%s device creation failed: %pe\n", name, pdev);
}
#ifdef CONFIG_X86
@@ -381,6 +381,9 @@ static int acpi_processor_add(struct acpi_device *device,
struct device *dev;
int result = 0;
+ if (!acpi_device_is_enabled(device))
+ return -ENODEV;
+
pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
if (!pr)
return -ENOMEM;
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
index 33c3b16af5..1d670dbe4d 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -554,7 +554,7 @@ static int acpi_tad_disable_timer(struct device *dev, u32 timer_id)
return acpi_tad_wake_set(dev, "_STV", timer_id, ACPI_TAD_WAKE_DISABLED);
}
-static int acpi_tad_remove(struct platform_device *pdev)
+static void acpi_tad_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
acpi_handle handle = ACPI_HANDLE(dev);
@@ -579,7 +579,6 @@ static int acpi_tad_remove(struct platform_device *pdev)
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
acpi_remove_cmos_rtc_space_handler(handle);
- return 0;
}
static int acpi_tad_probe(struct platform_device *pdev)
@@ -684,7 +683,7 @@ static struct platform_driver acpi_tad_driver = {
.acpi_match_table = acpi_tad_ids,
},
.probe = acpi_tad_probe,
- .remove = acpi_tad_remove,
+ .remove_new = acpi_tad_remove,
};
MODULE_DEVICE_TABLE(acpi, acpi_tad_ids);
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 4afdda9db0..1fda303882 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -612,7 +612,7 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
static int
acpi_video_device_EDID(struct acpi_video_device *device,
- union acpi_object **edid, ssize_t length)
+ union acpi_object **edid, int length)
{
int status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -625,13 +625,11 @@ acpi_video_device_EDID(struct acpi_video_device *device,
if (!device)
return -ENODEV;
- if (length == 128)
- arg0.integer.value = 1;
- else if (length == 256)
- arg0.integer.value = 2;
- else
+ if (!length || (length % 128))
return -EINVAL;
+ arg0.integer.value = length / 128;
+
status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -641,7 +639,8 @@ acpi_video_device_EDID(struct acpi_video_device *device,
if (obj && obj->type == ACPI_TYPE_BUFFER)
*edid = obj;
else {
- acpi_handle_info(device->dev->handle, "Invalid _DDC data\n");
+ acpi_handle_debug(device->dev->handle,
+ "Invalid _DDC data for length %d\n", length);
status = -EFAULT;
kfree(obj);
}
@@ -1447,7 +1446,6 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
for (i = 0; i < video->attached_count; i++) {
video_device = video->attached_array[i].bind_info;
- length = 256;
if (!video_device)
continue;
@@ -1478,18 +1476,14 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
continue;
}
- status = acpi_video_device_EDID(video_device, &buffer, length);
-
- if (ACPI_FAILURE(status) || !buffer ||
- buffer->type != ACPI_TYPE_BUFFER) {
- length = 128;
+ for (length = 512; length > 0; length -= 128) {
status = acpi_video_device_EDID(video_device, &buffer,
length);
- if (ACPI_FAILURE(status) || !buffer ||
- buffer->type != ACPI_TYPE_BUFFER) {
- continue;
- }
+ if (ACPI_SUCCESS(status))
+ break;
}
+ if (!length)
+ continue;
*edid = buffer->buffer.pointer;
return length;
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
index 8e9e001da3..14b2415779 100644
--- a/drivers/acpi/acpi_watchdog.c
+++ b/drivers/acpi/acpi_watchdog.c
@@ -179,7 +179,7 @@ void __init acpi_watchdog_init(void)
pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE,
resources, nresources);
if (IS_ERR(pdev))
- pr_err("Device creation failed: %ld\n", PTR_ERR(pdev));
+ pr_err("Device creation failed: %pe\n", pdev);
kfree(resources);
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index ddd072cbc7..2133085ded 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -191,6 +191,10 @@ void
acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
acpi_adr_space_type space_id, u32 function);
+void
+acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id);
+
acpi_status
acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function);
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 18fdf2bc2d..dc6004daf6 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -20,10 +20,6 @@ extern u8 acpi_gbl_default_address_spaces[];
/* Local prototypes */
-static void
-acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *device_node,
- acpi_adr_space_type space_id);
-
static acpi_status
acpi_ev_reg_run(acpi_handle obj_handle,
u32 level, void *context, void **return_value);
@@ -818,7 +814,7 @@ acpi_ev_reg_run(acpi_handle obj_handle,
*
******************************************************************************/
-static void
+void
acpi_ev_execute_orphan_reg_method(struct acpi_namespace_node *device_node,
acpi_adr_space_type space_id)
{
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 3197e6303c..624361a5f3 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -306,3 +306,57 @@ acpi_execute_reg_methods(acpi_handle device, acpi_adr_space_type space_id)
}
ACPI_EXPORT_SYMBOL(acpi_execute_reg_methods)
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_execute_orphan_reg_method
+ *
+ * PARAMETERS: device - Handle for the device
+ * space_id - The address space ID
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute an "orphan" _REG method that appears under an ACPI
+ * device. This is a _REG method that has no corresponding region
+ * within the device's scope.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_execute_orphan_reg_method(acpi_handle device, acpi_adr_space_type space_id)
+{
+ struct acpi_namespace_node *node;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(acpi_execute_orphan_reg_method);
+
+ /* Parameter validation */
+
+ if (!device) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Convert and validate the device handle */
+
+ node = acpi_ns_validate_handle(device);
+ if (node) {
+
+ /*
+ * If an "orphan" _REG method is present in the device's scope
+ * for the given address space ID, run it.
+ */
+
+ acpi_ev_execute_orphan_reg_method(node, space_id);
+ } else {
+ status = AE_BAD_PARAMETER;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_execute_orphan_reg_method)
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
index 8907b8bf42..c49b9f8de7 100644
--- a/drivers/acpi/acpica/exregion.c
+++ b/drivers/acpi/acpica/exregion.c
@@ -44,7 +44,6 @@ acpi_ex_system_memory_space_handler(u32 function,
struct acpi_mem_mapping *mm = mem_info->cur_mm;
u32 length;
acpi_size map_length;
- acpi_size page_boundary_map_length;
#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED
u32 remainder;
#endif
@@ -138,26 +137,8 @@ acpi_ex_system_memory_space_handler(u32 function,
map_length = (acpi_size)
((mem_info->address + mem_info->length) - address);
- /*
- * If mapping the entire remaining portion of the region will cross
- * a page boundary, just map up to the page boundary, do not cross.
- * On some systems, crossing a page boundary while mapping regions
- * can cause warnings if the pages have different attributes
- * due to resource management.
- *
- * This has the added benefit of constraining a single mapping to
- * one page, which is similar to the original code that used a 4k
- * maximum window.
- */
- page_boundary_map_length = (acpi_size)
- (ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address);
- if (page_boundary_map_length == 0) {
- page_boundary_map_length = ACPI_DEFAULT_PAGE_SIZE;
- }
-
- if (map_length > page_boundary_map_length) {
- map_length = page_boundary_map_length;
- }
+ if (map_length > ACPI_DEFAULT_PAGE_SIZE)
+ map_length = ACPI_DEFAULT_PAGE_SIZE;
/* Create a new mapping starting at the address given */
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index 6b18f8bc7b..3cfe7e7475 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -60,6 +60,19 @@ config ACPI_APEI_EINJ
mainly used for debugging and testing the other parts of
APEI and some other RAS features.
+config ACPI_APEI_EINJ_CXL
+ bool "CXL Error INJection Support"
+ default ACPI_APEI_EINJ
+ depends on ACPI_APEI_EINJ
+ depends on CXL_BUS && CXL_BUS <= ACPI_APEI_EINJ
+ help
+ Support for CXL protocol Error INJection through debugfs/cxl.
+ Availability and which errors are supported is dependent on
+ the host platform. Look to ACPI v6.5 section 18.6.4 and kernel
+ EINJ documentation for more information.
+
+ If unsure say 'n'
+
config ACPI_APEI_ERST_DEBUG
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
depends on ACPI_APEI
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
index 4dfac21287..2c474e6477 100644
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -2,6 +2,8 @@
obj-$(CONFIG_ACPI_APEI) += apei.o
obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
+einj-y := einj-core.o
+einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
apei-y := apei-base.o hest.o erst.o bert.o
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
index 67c2c3b959..cd2766c69d 100644
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -130,4 +130,22 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus)
}
int apei_osc_setup(void);
+
+int einj_get_available_error_type(u32 *type);
+int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
+ u64 param4);
+int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
+ u64 param3, u64 param4);
+bool einj_is_cxl_error_type(u64 type);
+int einj_validate_error_type(u64 type);
+
+#ifndef ACPI_EINJ_CXL_CACHE_CORRECTABLE
+#define ACPI_EINJ_CXL_CACHE_CORRECTABLE BIT(12)
+#define ACPI_EINJ_CXL_CACHE_UNCORRECTABLE BIT(13)
+#define ACPI_EINJ_CXL_CACHE_FATAL BIT(14)
+#define ACPI_EINJ_CXL_MEM_CORRECTABLE BIT(15)
+#define ACPI_EINJ_CXL_MEM_UNCORRECTABLE BIT(16)
+#define ACPI_EINJ_CXL_MEM_FATAL BIT(17)
+#endif
+
#endif
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj-core.c
index 89fb9331c6..bb9f8475ce 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj-core.c
@@ -21,6 +21,7 @@
#include <linux/nmi.h>
#include <linux/delay.h>
#include <linux/mm.h>
+#include <linux/platform_device.h>
#include <asm/unaligned.h>
#include "apei-internal.h"
@@ -36,6 +37,12 @@
#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
ACPI_EINJ_MEMORY_UNCORRECTABLE | \
ACPI_EINJ_MEMORY_FATAL)
+#define CXL_ERROR_MASK (ACPI_EINJ_CXL_CACHE_CORRECTABLE | \
+ ACPI_EINJ_CXL_CACHE_UNCORRECTABLE | \
+ ACPI_EINJ_CXL_CACHE_FATAL | \
+ ACPI_EINJ_CXL_MEM_CORRECTABLE | \
+ ACPI_EINJ_CXL_MEM_UNCORRECTABLE | \
+ ACPI_EINJ_CXL_MEM_FATAL)
/*
* ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
@@ -137,6 +144,11 @@ static struct apei_exec_ins_type einj_ins_type[] = {
*/
static DEFINE_MUTEX(einj_mutex);
+/*
+ * Exported APIs use this flag to exit early if einj_probe() failed.
+ */
+bool einj_initialized __ro_after_init;
+
static void *einj_param;
static void einj_exec_ctx_init(struct apei_exec_context *ctx)
@@ -160,7 +172,7 @@ static int __einj_get_available_error_type(u32 *type)
}
/* Get error injection capabilities of the platform */
-static int einj_get_available_error_type(u32 *type)
+int einj_get_available_error_type(u32 *type)
{
int rc;
@@ -530,8 +542,8 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
}
/* Inject the specified hardware error */
-static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
- u64 param3, u64 param4)
+int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
+ u64 param4)
{
int rc;
u64 base_addr, size;
@@ -554,8 +566,17 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
if (type & ACPI5_VENDOR_BIT) {
if (vendor_flags != SETWA_FLAGS_MEM)
goto inject;
- } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
+ } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) {
goto inject;
+ }
+
+ /*
+ * Injections targeting a CXL 1.0/1.1 port have to be injected
+ * via the einj_cxl_rch_error_inject() path as that does the proper
+ * validation of the given RCRB base (MMIO) address.
+ */
+ if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM))
+ return -EINVAL;
/*
* Disallow crazy address masks that give BIOS leeway to pick
@@ -587,6 +608,21 @@ inject:
return rc;
}
+int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
+ u64 param3, u64 param4)
+{
+ int rc;
+
+ if (!(einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)))
+ return -EINVAL;
+
+ mutex_lock(&einj_mutex);
+ rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
+ mutex_unlock(&einj_mutex);
+
+ return rc;
+}
+
static u32 error_type;
static u32 error_flags;
static u64 error_param1;
@@ -607,12 +643,6 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
{ 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" },
{ BIT(31), "Vendor Defined Error Types" },
};
@@ -641,22 +671,26 @@ static int error_type_get(void *data, u64 *val)
return 0;
}
-static int error_type_set(void *data, u64 val)
+bool einj_is_cxl_error_type(u64 type)
{
+ return (type & CXL_ERROR_MASK) && (!(type & ACPI5_VENDOR_BIT));
+}
+
+int einj_validate_error_type(u64 type)
+{
+ u32 tval, vendor, available_error_type = 0;
int rc;
- u32 available_error_type = 0;
- u32 tval, vendor;
/* Only low 32 bits for error type are valid */
- if (val & GENMASK_ULL(63, 32))
+ if (type & GENMASK_ULL(63, 32))
return -EINVAL;
/*
* Vendor defined types have 0x80000000 bit set, and
* are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
*/
- vendor = val & ACPI5_VENDOR_BIT;
- tval = val & 0x7fffffff;
+ vendor = type & ACPI5_VENDOR_BIT;
+ tval = type & GENMASK(30, 0);
/* Only one error type can be specified */
if (tval & (tval - 1))
@@ -665,9 +699,21 @@ static int error_type_set(void *data, u64 val)
rc = einj_get_available_error_type(&available_error_type);
if (rc)
return rc;
- if (!(val & available_error_type))
+ if (!(type & available_error_type))
return -EINVAL;
}
+
+ return 0;
+}
+
+static int error_type_set(void *data, u64 val)
+{
+ int rc;
+
+ rc = einj_validate_error_type(val);
+ if (rc)
+ return rc;
+
error_type = val;
return 0;
@@ -703,21 +749,21 @@ static int einj_check_table(struct acpi_table_einj *einj_tab)
return 0;
}
-static int __init einj_init(void)
+static int __init einj_probe(struct platform_device *pdev)
{
int rc;
acpi_status status;
struct apei_exec_context ctx;
if (acpi_disabled) {
- pr_info("ACPI disabled.\n");
+ pr_debug("ACPI disabled.\n");
return -ENODEV;
}
status = acpi_get_table(ACPI_SIG_EINJ, 0,
(struct acpi_table_header **)&einj_tab);
if (status == AE_NOT_FOUND) {
- pr_warn("EINJ table not found.\n");
+ pr_debug("EINJ table not found.\n");
return -ENODEV;
} else if (ACPI_FAILURE(status)) {
pr_err("Failed to get EINJ table: %s\n",
@@ -805,7 +851,7 @@ err_put_table:
return rc;
}
-static void __exit einj_exit(void)
+static void einj_remove(struct platform_device *pdev)
{
struct apei_exec_context ctx;
@@ -826,6 +872,40 @@ static void __exit einj_exit(void)
acpi_put_table((struct acpi_table_header *)einj_tab);
}
+static struct platform_device *einj_dev;
+static struct platform_driver einj_driver = {
+ .remove_new = einj_remove,
+ .driver = {
+ .name = "acpi-einj",
+ },
+};
+
+static int __init einj_init(void)
+{
+ struct platform_device_info einj_dev_info = {
+ .name = "acpi-einj",
+ .id = -1,
+ };
+ int rc;
+
+ einj_dev = platform_device_register_full(&einj_dev_info);
+ if (IS_ERR(einj_dev))
+ return PTR_ERR(einj_dev);
+
+ rc = platform_driver_probe(&einj_driver, einj_probe);
+ einj_initialized = rc == 0;
+
+ return 0;
+}
+
+static void __exit einj_exit(void)
+{
+ if (einj_initialized)
+ platform_driver_unregister(&einj_driver);
+
+ platform_device_unregister(einj_dev);
+}
+
module_init(einj_init);
module_exit(einj_exit);
diff --git a/drivers/acpi/apei/einj-cxl.c b/drivers/acpi/apei/einj-cxl.c
new file mode 100644
index 0000000000..8b8be0c907
--- /dev/null
+++ b/drivers/acpi/apei/einj-cxl.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CXL Error INJection support. Used by CXL core to inject
+ * protocol errors into CXL ports.
+ *
+ * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ *
+ * Author: Ben Cheatham <benjamin.cheatham@amd.com>
+ */
+#include <linux/einj-cxl.h>
+#include <linux/seq_file.h>
+#include <linux/pci.h>
+
+#include "apei-internal.h"
+
+/* Defined in einj-core.c */
+extern bool einj_initialized;
+
+static struct { u32 mask; const char *str; } const einj_cxl_error_type_string[] = {
+ { ACPI_EINJ_CXL_CACHE_CORRECTABLE, "CXL.cache Protocol Correctable" },
+ { ACPI_EINJ_CXL_CACHE_UNCORRECTABLE, "CXL.cache Protocol Uncorrectable non-fatal" },
+ { ACPI_EINJ_CXL_CACHE_FATAL, "CXL.cache Protocol Uncorrectable fatal" },
+ { ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Protocol Correctable" },
+ { ACPI_EINJ_CXL_MEM_UNCORRECTABLE, "CXL.mem Protocol Uncorrectable non-fatal" },
+ { ACPI_EINJ_CXL_MEM_FATAL, "CXL.mem Protocol Uncorrectable fatal" },
+};
+
+int einj_cxl_available_error_type_show(struct seq_file *m, void *v)
+{
+ int cxl_err, rc;
+ u32 available_error_type = 0;
+
+ rc = einj_get_available_error_type(&available_error_type);
+ if (rc)
+ return rc;
+
+ for (int pos = 0; pos < ARRAY_SIZE(einj_cxl_error_type_string); pos++) {
+ cxl_err = ACPI_EINJ_CXL_CACHE_CORRECTABLE << pos;
+
+ if (available_error_type & cxl_err)
+ seq_printf(m, "0x%08x\t%s\n",
+ einj_cxl_error_type_string[pos].mask,
+ einj_cxl_error_type_string[pos].str);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_available_error_type_show, CXL);
+
+static int cxl_dport_get_sbdf(struct pci_dev *dport_dev, u64 *sbdf)
+{
+ struct pci_bus *pbus;
+ struct pci_host_bridge *bridge;
+ u64 seg = 0, bus;
+
+ pbus = dport_dev->bus;
+ bridge = pci_find_host_bridge(pbus);
+
+ if (!bridge)
+ return -ENODEV;
+
+ if (bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET)
+ seg = bridge->domain_nr;
+
+ bus = pbus->number;
+ *sbdf = (seg << 24) | (bus << 16) | dport_dev->devfn;
+
+ return 0;
+}
+
+int einj_cxl_inject_rch_error(u64 rcrb, u64 type)
+{
+ int rc;
+
+ /* Only CXL error types can be specified */
+ if (!einj_is_cxl_error_type(type))
+ return -EINVAL;
+
+ rc = einj_validate_error_type(type);
+ if (rc)
+ return rc;
+
+ return einj_cxl_rch_error_inject(type, 0x2, rcrb, GENMASK_ULL(63, 0),
+ 0, 0);
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_rch_error, CXL);
+
+int einj_cxl_inject_error(struct pci_dev *dport, u64 type)
+{
+ u64 param4 = 0;
+ int rc;
+
+ /* Only CXL error types can be specified */
+ if (!einj_is_cxl_error_type(type))
+ return -EINVAL;
+
+ rc = einj_validate_error_type(type);
+ if (rc)
+ return rc;
+
+ rc = cxl_dport_get_sbdf(dport, &param4);
+ if (rc)
+ return rc;
+
+ return einj_error_inject(type, 0x4, 0, 0, 0, param4);
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_error, CXL);
+
+bool einj_cxl_is_initialized(void)
+{
+ return einj_initialized;
+}
+EXPORT_SYMBOL_NS_GPL(einj_cxl_is_initialized, CXL);
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index ab2a82cb1b..512067cac1 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -1455,7 +1455,7 @@ err:
return rc;
}
-static int ghes_remove(struct platform_device *ghes_dev)
+static void ghes_remove(struct platform_device *ghes_dev)
{
int rc;
struct ghes *ghes;
@@ -1492,8 +1492,15 @@ static int ghes_remove(struct platform_device *ghes_dev)
break;
case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
rc = apei_sdei_unregister_ghes(ghes);
- if (rc)
- return rc;
+ if (rc) {
+ /*
+ * Returning early results in a resource leak, but we're
+ * only here if stopping the hardware failed.
+ */
+ dev_err(&ghes_dev->dev, "Failed to unregister ghes (%pe)\n",
+ ERR_PTR(rc));
+ return;
+ }
break;
default:
BUG();
@@ -1507,8 +1514,6 @@ static int ghes_remove(struct platform_device *ghes_dev)
mutex_unlock(&ghes_devs_mutex);
kfree(ghes);
-
- return 0;
}
static struct platform_driver ghes_platform_driver = {
@@ -1516,7 +1521,7 @@ static struct platform_driver ghes_platform_driver = {
.name = "GHES",
},
.probe = ghes_probe,
- .remove = ghes_remove,
+ .remove_new = ghes_remove,
};
void __init acpi_ghes_init(void)
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index 6aef1ee5e1..20d757687e 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -37,6 +37,20 @@ EXPORT_SYMBOL_GPL(hest_disable);
static struct acpi_table_hest *__read_mostly hest_tab;
+/*
+ * Since GHES_ASSIST is not supported, skip initialization of GHES_ASSIST
+ * structures for MCA.
+ * During HEST parsing, detected MCA error sources are cached from early
+ * table entries so that the Flags and Source Id fields from these cached
+ * values are then referred to in later table entries to determine if the
+ * encountered GHES_ASSIST structure should be initialized.
+ */
+static struct {
+ struct acpi_hest_ia_corrected *cmc;
+ struct acpi_hest_ia_machine_check *mc;
+ struct acpi_hest_ia_deferred_check *dmc;
+} mces;
+
static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
[ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */
[ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
@@ -70,22 +84,54 @@ static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
len = sizeof(*cmc) + cmc->num_hardware_banks *
sizeof(struct acpi_hest_ia_error_bank);
+ mces.cmc = cmc;
} else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
struct acpi_hest_ia_machine_check *mc;
mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
len = sizeof(*mc) + mc->num_hardware_banks *
sizeof(struct acpi_hest_ia_error_bank);
+ mces.mc = mc;
} else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) {
struct acpi_hest_ia_deferred_check *mc;
mc = (struct acpi_hest_ia_deferred_check *)hest_hdr;
len = sizeof(*mc) + mc->num_hardware_banks *
sizeof(struct acpi_hest_ia_error_bank);
+ mces.dmc = mc;
}
BUG_ON(len == -1);
return len;
};
+/*
+ * GHES and GHESv2 structures share the same format, starting from
+ * Source Id and ending in Error Status Block Length (inclusive).
+ */
+static bool is_ghes_assist_struct(struct acpi_hest_header *hest_hdr)
+{
+ struct acpi_hest_generic *ghes;
+ u16 related_source_id;
+
+ if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR &&
+ hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2)
+ return false;
+
+ ghes = (struct acpi_hest_generic *)hest_hdr;
+ related_source_id = ghes->related_source_id;
+
+ if (mces.cmc && mces.cmc->flags & ACPI_HEST_GHES_ASSIST &&
+ related_source_id == mces.cmc->header.source_id)
+ return true;
+ if (mces.mc && mces.mc->flags & ACPI_HEST_GHES_ASSIST &&
+ related_source_id == mces.mc->header.source_id)
+ return true;
+ if (mces.dmc && mces.dmc->flags & ACPI_HEST_GHES_ASSIST &&
+ related_source_id == mces.dmc->header.source_id)
+ return true;
+
+ return false;
+}
+
typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data);
static int apei_hest_parse(apei_hest_func_t func, void *data)
@@ -114,6 +160,11 @@ static int apei_hest_parse(apei_hest_func_t func, void *data)
return -EINVAL;
}
+ if (is_ghes_assist_struct(hest_hdr)) {
+ hest_hdr = (void *)hest_hdr + len;
+ continue;
+ }
+
rc = func(hest_hdr, data);
if (rc)
return rc;
diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c
index 8b3c7d42b4..f5f21dd0d2 100644
--- a/drivers/acpi/arm64/agdi.c
+++ b/drivers/acpi/arm64/agdi.c
@@ -58,7 +58,7 @@ static int agdi_probe(struct platform_device *pdev)
return agdi_sdei_probe(pdev, adata);
}
-static int agdi_remove(struct platform_device *pdev)
+static void agdi_remove(struct platform_device *pdev)
{
struct agdi_data *adata = dev_get_platdata(&pdev->dev);
int err, i;
@@ -67,7 +67,7 @@ static int agdi_remove(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev, "Failed to disable sdei-event #%d (%pe)\n",
adata->sdei_event, ERR_PTR(err));
- return 0;
+ return;
}
for (i = 0; i < 3; i++) {
@@ -81,8 +81,6 @@ static int agdi_remove(struct platform_device *pdev)
if (err)
dev_err(&pdev->dev, "Failed to unregister sdei-event #%d (%pe)\n",
adata->sdei_event, ERR_PTR(err));
-
- return 0;
}
static struct platform_driver agdi_driver = {
@@ -90,7 +88,7 @@ static struct platform_driver agdi_driver = {
.name = "agdi",
},
.probe = agdi_probe,
- .remove = agdi_remove,
+ .remove_new = agdi_remove,
};
void __init acpi_agdi_init(void)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 3f84d8ed9d..a87b10eef7 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1102,7 +1102,7 @@ static void acpi_device_remove(struct device *dev)
put_device(dev);
}
-struct bus_type acpi_bus_type = {
+const struct bus_type acpi_bus_type = {
.name = "acpi",
.match = acpi_bus_match,
.probe = acpi_device_probe,
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 1b27ebc6d0..a40b6f3946 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1200,6 +1200,19 @@ int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf)
}
/**
+ * cppc_get_highest_perf - Get the highest performance register value.
+ * @cpunum: CPU from which to get highest performance.
+ * @highest_perf: Return address.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_get_highest_perf(int cpunum, u64 *highest_perf)
+{
+ return cppc_get_perf(cpunum, HIGHEST_PERF, highest_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_highest_perf);
+
+/**
* cppc_get_epp_perf - Get the epp register value.
* @cpunum: CPU from which to get epp preference value.
* @epp_perf: Return address.
diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c
deleted file mode 100644
index d39a9b4747..0000000000
--- a/drivers/acpi/custom_method.c
+++ /dev/null
@@ -1,103 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * custom_method.c - debugfs interface for customizing ACPI control method
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/uaccess.h>
-#include <linux/debugfs.h>
-#include <linux/acpi.h>
-#include <linux/security.h>
-
-#include "internal.h"
-
-MODULE_LICENSE("GPL");
-
-static struct dentry *cm_dentry;
-
-/* /sys/kernel/debug/acpi/custom_method */
-
-static ssize_t cm_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- static char *buf;
- static u32 max_size;
- static u32 uncopied_bytes;
-
- struct acpi_table_header table;
- acpi_status status;
- int ret;
-
- ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
- if (ret)
- return ret;
-
- if (!(*ppos)) {
- /* parse the table header to get the table length */
- if (count <= sizeof(struct acpi_table_header))
- return -EINVAL;
- if (copy_from_user(&table, user_buf,
- sizeof(struct acpi_table_header)))
- return -EFAULT;
- uncopied_bytes = max_size = table.length;
- /* make sure the buf is not allocated */
- kfree(buf);
- buf = kzalloc(max_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- }
-
- if (buf == NULL)
- return -EINVAL;
-
- if ((*ppos > max_size) ||
- (*ppos + count > max_size) ||
- (*ppos + count < count) ||
- (count > uncopied_bytes)) {
- kfree(buf);
- buf = NULL;
- return -EINVAL;
- }
-
- if (copy_from_user(buf + (*ppos), user_buf, count)) {
- kfree(buf);
- buf = NULL;
- return -EFAULT;
- }
-
- uncopied_bytes -= count;
- *ppos += count;
-
- if (!uncopied_bytes) {
- status = acpi_install_method(buf);
- kfree(buf);
- buf = NULL;
- if (ACPI_FAILURE(status))
- return -EINVAL;
- add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
- }
-
- return count;
-}
-
-static const struct file_operations cm_fops = {
- .write = cm_write,
- .llseek = default_llseek,
-};
-
-static int __init acpi_custom_method_init(void)
-{
- cm_dentry = debugfs_create_file("custom_method", S_IWUSR,
- acpi_debugfs_dir, NULL, &cm_fops);
- return 0;
-}
-
-static void __exit acpi_custom_method_exit(void)
-{
- debugfs_remove(cm_dentry);
-}
-
-module_init(acpi_custom_method_init);
-module_exit(acpi_custom_method_exit);
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index a89bdbe001..a7c00ef780 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -380,6 +380,8 @@ static int dock_in_progress(struct dock_station *ds)
/**
* handle_eject_request - handle an undock request checking for error conditions
+ * @ds: The dock station to undock.
+ * @event: The ACPI event number associated with the undock request.
*
* Check to make sure the dock device is still present, then undock and
* hotremove all the devices that may need removing.
diff --git a/drivers/acpi/dptf/dptf_pch_fivr.c b/drivers/acpi/dptf/dptf_pch_fivr.c
index 4919e7abe9..654aaa53c6 100644
--- a/drivers/acpi/dptf/dptf_pch_fivr.c
+++ b/drivers/acpi/dptf/dptf_pch_fivr.c
@@ -141,11 +141,9 @@ static int pch_fivr_add(struct platform_device *pdev)
return 0;
}
-static int pch_fivr_remove(struct platform_device *pdev)
+static void pch_fivr_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &pch_fivr_attribute_group);
-
- return 0;
}
static const struct acpi_device_id pch_fivr_device_ids[] = {
@@ -159,7 +157,7 @@ MODULE_DEVICE_TABLE(acpi, pch_fivr_device_ids);
static struct platform_driver pch_fivr_driver = {
.probe = pch_fivr_add,
- .remove = pch_fivr_remove,
+ .remove_new = pch_fivr_remove,
.driver = {
.name = "dptf_pch_fivr",
.acpi_match_table = pch_fivr_device_ids,
diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c
index 86561eda93..b8187babbb 100644
--- a/drivers/acpi/dptf/dptf_power.c
+++ b/drivers/acpi/dptf/dptf_power.c
@@ -209,7 +209,7 @@ static int dptf_power_add(struct platform_device *pdev)
return 0;
}
-static int dptf_power_remove(struct platform_device *pdev)
+static void dptf_power_remove(struct platform_device *pdev)
{
struct acpi_device *acpi_dev = platform_get_drvdata(pdev);
@@ -221,8 +221,6 @@ static int dptf_power_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group);
else
sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group);
-
- return 0;
}
static const struct acpi_device_id int3407_device_ids[] = {
@@ -242,7 +240,7 @@ MODULE_DEVICE_TABLE(acpi, int3407_device_ids);
static struct platform_driver dptf_power_driver = {
.probe = dptf_power_add,
- .remove = dptf_power_remove,
+ .remove_new = dptf_power_remove,
.driver = {
.name = "dptf_power",
.acpi_match_table = int3407_device_ids,
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 02255795b8..1cec29ab64 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1482,13 +1482,14 @@ static bool install_gpio_irq_event_handler(struct acpi_ec *ec)
static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device,
bool call_reg)
{
+ acpi_handle scope_handle = ec == first_ec ? ACPI_ROOT_OBJECT : ec->handle;
acpi_status status;
acpi_ec_start(ec, false);
if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
acpi_ec_enter_noirq(ec);
- status = acpi_install_address_space_handler_no_reg(ec->handle,
+ status = acpi_install_address_space_handler_no_reg(scope_handle,
ACPI_ADR_SPACE_EC,
&acpi_ec_space_handler,
NULL, ec);
@@ -1497,11 +1498,13 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device,
return -ENODEV;
}
set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
- ec->address_space_handler_holder = ec->handle;
}
if (call_reg && !test_bit(EC_FLAGS_EC_REG_CALLED, &ec->flags)) {
- acpi_execute_reg_methods(ec->handle, ACPI_ADR_SPACE_EC);
+ acpi_execute_reg_methods(scope_handle, ACPI_ADR_SPACE_EC);
+ if (scope_handle != ec->handle)
+ acpi_execute_orphan_reg_method(ec->handle, ACPI_ADR_SPACE_EC);
+
set_bit(EC_FLAGS_EC_REG_CALLED, &ec->flags);
}
@@ -1553,10 +1556,13 @@ static int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device,
static void ec_remove_handlers(struct acpi_ec *ec)
{
+ acpi_handle scope_handle = ec == first_ec ? ACPI_ROOT_OBJECT : ec->handle;
+
if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
if (ACPI_FAILURE(acpi_remove_address_space_handler(
- ec->address_space_handler_holder,
- ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
+ scope_handle,
+ ACPI_ADR_SPACE_EC,
+ &acpi_ec_space_handler)))
pr_err("failed to remove space handler\n");
clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
}
@@ -1595,14 +1601,18 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca
{
int ret;
- ret = ec_install_handlers(ec, device, call_reg);
- if (ret)
- return ret;
-
/* First EC capable of handling transactions */
if (!first_ec)
first_ec = ec;
+ ret = ec_install_handlers(ec, device, call_reg);
+ if (ret) {
+ if (ec == first_ec)
+ first_ec = NULL;
+
+ return ret;
+ }
+
pr_info("EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", ec->command_addr,
ec->data_addr);
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c
index fe6b6792c8..11778c9325 100644
--- a/drivers/acpi/evged.c
+++ b/drivers/acpi/evged.c
@@ -173,10 +173,9 @@ static void ged_shutdown(struct platform_device *pdev)
}
}
-static int ged_remove(struct platform_device *pdev)
+static void ged_remove(struct platform_device *pdev)
{
ged_shutdown(pdev);
- return 0;
}
static const struct acpi_device_id ged_acpi_ids[] = {
@@ -186,7 +185,7 @@ static const struct acpi_device_id ged_acpi_ids[] = {
static struct platform_driver ged_driver = {
.probe = ged_probe,
- .remove = ged_remove,
+ .remove_new = ged_remove,
.shutdown = ged_shutdown,
.driver = {
.name = MODULE_NAME,
diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c
index 9dccbae9e8..ff72e4ef87 100644
--- a/drivers/acpi/fan_core.c
+++ b/drivers/acpi/fan_core.c
@@ -387,7 +387,7 @@ err_end:
return result;
}
-static int acpi_fan_remove(struct platform_device *pdev)
+static void acpi_fan_remove(struct platform_device *pdev)
{
struct acpi_fan *fan = platform_get_drvdata(pdev);
@@ -399,8 +399,6 @@ static int acpi_fan_remove(struct platform_device *pdev)
sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
sysfs_remove_link(&fan->cdev->device.kobj, "device");
thermal_cooling_device_unregister(fan->cdev);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -446,7 +444,7 @@ static const struct dev_pm_ops acpi_fan_pm = {
static struct platform_driver acpi_fan_driver = {
.probe = acpi_fan_probe,
- .remove = acpi_fan_remove,
+ .remove_new = acpi_fan_remove,
.driver = {
.name = "acpi-fan",
.acpi_match_table = fan_device_ids,
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 6588525c45..a96d1bc662 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -121,6 +121,7 @@ int acpi_device_setup_files(struct acpi_device *dev);
void acpi_device_remove_files(struct acpi_device *dev);
void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
+bool acpi_device_is_enabled(const struct acpi_device *adev);
bool acpi_device_is_present(const struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
@@ -184,7 +185,6 @@ enum acpi_ec_event_state {
struct acpi_ec {
acpi_handle handle;
- acpi_handle address_space_handler_holder;
int gpe;
int irq;
unsigned long command_addr;
@@ -301,5 +301,10 @@ 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);
+#ifdef CONFIG_X86
+bool acpi_graph_ignore_port(acpi_handle handle);
+#else
+static inline bool acpi_graph_ignore_port(acpi_handle handle) { return false; }
+#endif
#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/mipi-disco-img.c b/drivers/acpi/mipi-disco-img.c
index 7286cf4579..0ab13751f0 100644
--- a/drivers/acpi/mipi-disco-img.c
+++ b/drivers/acpi/mipi-disco-img.c
@@ -19,6 +19,7 @@
*/
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/limits.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -723,3 +724,83 @@ void acpi_mipi_crs_csi2_cleanup(void)
list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
acpi_mipi_del_crs_csi2(csi2);
}
+
+#ifdef CONFIG_X86
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+/* CPU matches for Dell generations with broken ACPI MIPI DISCO info */
+static const struct x86_cpu_id dell_broken_mipi_disco_cpu_gens[] = {
+ X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
+ {}
+};
+
+static const char *strnext(const char *s1, const char *s2)
+{
+ s1 = strstr(s1, s2);
+
+ if (!s1)
+ return NULL;
+
+ return s1 + strlen(s2);
+}
+
+/**
+ * acpi_graph_ignore_port - Tell whether a port node should be ignored
+ * @handle: The ACPI handle of the node (which may be a port node)
+ *
+ * Return: true if a port node should be ignored and the data to that should
+ * come from other sources instead (Windows ACPI definitions and
+ * ipu-bridge). This is currently used to ignore bad port nodes related to IPU6
+ * ("IPU?") and camera sensor devices ("LNK?") in certain Dell systems with
+ * Intel VSC.
+ */
+bool acpi_graph_ignore_port(acpi_handle handle)
+{
+ const char *path = NULL, *orig_path;
+ static bool dmi_tested, ignore_port;
+
+ if (!dmi_tested) {
+ if (dmi_name_in_vendors("Dell Inc.") &&
+ x86_match_cpu(dell_broken_mipi_disco_cpu_gens))
+ ignore_port = true;
+
+ dmi_tested = true;
+ }
+
+ if (!ignore_port)
+ return false;
+
+ /* Check if the device is either "IPU" or "LNK" (sensor). */
+ orig_path = acpi_handle_path(handle);
+ if (!orig_path)
+ return false;
+ path = strnext(orig_path, "IPU");
+ if (!path)
+ path = strnext(orig_path, "LNK");
+ if (!path)
+ goto out_free;
+
+ if (!(isdigit(path[0]) && path[1] == '.'))
+ goto out_free;
+
+ /* Check if the node has a "PRT" prefix. */
+ path = strnext(path, "PRT");
+ if (path && isdigit(path[0]) && !path[1]) {
+ acpi_handle_debug(handle, "ignoring data node\n");
+
+ kfree(orig_path);
+ return true;
+ }
+
+out_free:
+ kfree(orig_path);
+ return false;
+}
+#endif
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 802f8a56d1..d4595d1985 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -1737,9 +1737,8 @@ __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
if ((nfit_mem->dsm_mask & (1 << func)) == 0)
return;
- out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
- if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER
- || out_obj->buffer.length < sizeof(smart)) {
+ out_obj = acpi_evaluate_dsm_typed(handle, guid, revid, func, &in_obj, ACPI_TYPE_BUFFER);
+ if (!out_obj || out_obj->buffer.length < sizeof(smart)) {
dev_dbg(dev->parent, "%s: failed to retrieve initial health\n",
dev_name(dev));
ACPI_FREE(out_obj);
diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c
index 75e9aac432..2c8ccc91eb 100644
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -74,6 +74,7 @@ struct memory_target {
struct node_cache_attrs cache_attrs;
u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE];
bool registered;
+ bool ext_updated; /* externally updated */
};
struct memory_initiator {
@@ -328,6 +329,35 @@ static void hmat_update_target_access(struct memory_target *target,
}
}
+int hmat_update_target_coordinates(int nid, struct access_coordinate *coord,
+ enum access_coordinate_class access)
+{
+ struct memory_target *target;
+ int pxm;
+
+ if (nid == NUMA_NO_NODE)
+ return -EINVAL;
+
+ pxm = node_to_pxm(nid);
+ guard(mutex)(&target_lock);
+ target = find_mem_target(pxm);
+ if (!target)
+ return -ENODEV;
+
+ hmat_update_target_access(target, ACPI_HMAT_READ_LATENCY,
+ coord->read_latency, access);
+ hmat_update_target_access(target, ACPI_HMAT_WRITE_LATENCY,
+ coord->write_latency, access);
+ hmat_update_target_access(target, ACPI_HMAT_READ_BANDWIDTH,
+ coord->read_bandwidth, access);
+ hmat_update_target_access(target, ACPI_HMAT_WRITE_BANDWIDTH,
+ coord->write_bandwidth, access);
+ target->ext_updated = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hmat_update_target_coordinates);
+
static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
{
struct memory_locality *loc;
@@ -699,6 +729,10 @@ static void hmat_update_target_attrs(struct memory_target *target,
u32 best = 0;
int i;
+ /* Don't update if an external agent has changed the data. */
+ if (target->ext_updated)
+ return;
+
/* Don't update for generic port if there's no device handle */
if ((access == NODE_ACCESS_CLASS_GENPORT_SINK_LOCAL ||
access == NODE_ACCESS_CLASS_GENPORT_SINK_CPU) &&
diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c
index 3ca22db3ed..3b09fd39ee 100644
--- a/drivers/acpi/numa/srat.c
+++ b/drivers/acpi/numa/srat.c
@@ -29,6 +29,8 @@ static int node_to_pxm_map[MAX_NUMNODES]
unsigned char acpi_srat_revision __initdata;
static int acpi_numa __initdata;
+static int last_real_pxm;
+
void __init disable_srat(void)
{
acpi_numa = -1;
@@ -541,6 +543,7 @@ int __init acpi_numa_init(void)
if (node_to_pxm_map[i] > fake_pxm)
fake_pxm = node_to_pxm_map[i];
}
+ last_real_pxm = fake_pxm;
fake_pxm++;
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
&fake_pxm);
@@ -552,6 +555,14 @@ int __init acpi_numa_init(void)
return 0;
}
+bool acpi_node_backed_by_real_pxm(int nid)
+{
+ int pxm = node_to_pxm(nid);
+
+ return pxm <= last_real_pxm;
+}
+EXPORT_SYMBOL_GPL(acpi_node_backed_by_real_pxm);
+
static int acpi_get_pxm(acpi_handle h)
{
unsigned long long pxm;
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index d6cb2c27a2..741bcc9d6d 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -111,7 +111,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
snprintf(name, sizeof(name), "%llu", sun);
pci_slot = pci_create_slot(pci_bus, device, name, NULL);
if (IS_ERR(pci_slot)) {
- pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
+ pr_err("pci_create_slot returned %pe\n", pci_slot);
kfree(slot);
return AE_OK;
}
diff --git a/drivers/acpi/pfr_telemetry.c b/drivers/acpi/pfr_telemetry.c
index 843f678ade..998264a733 100644
--- a/drivers/acpi/pfr_telemetry.c
+++ b/drivers/acpi/pfr_telemetry.c
@@ -347,13 +347,11 @@ static const struct file_operations acpi_pfrt_log_fops = {
.llseek = noop_llseek,
};
-static int acpi_pfrt_log_remove(struct platform_device *pdev)
+static void acpi_pfrt_log_remove(struct platform_device *pdev)
{
struct pfrt_log_device *pfrt_log_dev = platform_get_drvdata(pdev);
misc_deregister(&pfrt_log_dev->miscdev);
-
- return 0;
}
static void pfrt_log_put_idx(void *data)
@@ -427,7 +425,7 @@ static struct platform_driver acpi_pfrt_log_driver = {
.acpi_match_table = acpi_pfrt_log_ids,
},
.probe = acpi_pfrt_log_probe,
- .remove = acpi_pfrt_log_remove,
+ .remove_new = acpi_pfrt_log_remove,
};
module_platform_driver(acpi_pfrt_log_driver);
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
index 98267f163e..8b2910995f 100644
--- a/drivers/acpi/pfr_update.c
+++ b/drivers/acpi/pfr_update.c
@@ -489,13 +489,11 @@ static const struct file_operations acpi_pfru_fops = {
.llseek = noop_llseek,
};
-static int acpi_pfru_remove(struct platform_device *pdev)
+static void acpi_pfru_remove(struct platform_device *pdev)
{
struct pfru_device *pfru_dev = platform_get_drvdata(pdev);
misc_deregister(&pfru_dev->miscdev);
-
- return 0;
}
static void pfru_put_idx(void *data)
@@ -567,7 +565,7 @@ static struct platform_driver acpi_pfru_driver = {
.acpi_match_table = acpi_pfru_ids,
},
.probe = acpi_pfru_probe,
- .remove = acpi_pfru_remove,
+ .remove_new = acpi_pfru_remove,
};
module_platform_driver(acpi_pfru_driver);
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 4bd16b3f07..67db60eda3 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -27,6 +27,7 @@
#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
#define ACPI_PROCESSOR_NOTIFY_POWER 0x81
#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82
+#define ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED 0x85
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI Processor Driver");
@@ -83,6 +84,11 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
acpi_bus_generate_netlink_event(device->pnp.device_class,
dev_name(&device->dev), event, 0);
break;
+ case ACPI_PROCESSOR_NOTIFY_HIGEST_PERF_CHANGED:
+ cpufreq_update_limits(pr->id);
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event, 0);
+ break;
default:
acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event);
break;
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index a6ead52040..2b73580c9f 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -80,6 +80,9 @@ static bool acpi_nondev_subnode_extract(union acpi_object *desc,
struct acpi_data_node *dn;
bool result;
+ if (acpi_graph_ignore_port(handle))
+ return false;
+
dn = kzalloc(sizeof(*dn), GFP_KERNEL);
if (!dn)
return false;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index b5193049d7..b5bf8b81a0 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -468,6 +468,13 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = {
DMI_MATCH(DMI_BOARD_NAME, "B1502CGA"),
},
},
+ {
+ /* Asus ExpertBook B1502CVA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B1502CVA"),
+ },
+ },
{
/* Asus ExpertBook B2402CBA */
.matches = {
@@ -511,6 +518,13 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = {
},
},
{
+ /* Asus Vivobook Pro N6506MV */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "N6506MV"),
+ },
+ },
+ {
/* LG Electronics 17U70P */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
@@ -527,6 +541,12 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = {
*/
static const struct dmi_system_id irq1_edge_low_force_override[] = {
{
+ /* XMG APEX 17 (M23) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxBGxx"),
+ },
+ },
+ {
/* TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
@@ -616,6 +636,25 @@ static const struct dmi_system_id irq1_edge_low_force_override[] = {
DMI_MATCH(DMI_BOARD_NAME, "X577"),
},
},
+ {
+ /* Maibenben X565 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MAIBENBEN"),
+ DMI_MATCH(DMI_BOARD_NAME, "X565"),
+ },
+ },
+ {
+ /* TongFang GXxHRXx/TUXEDO InfinityBook Pro Gen9 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GXxHRXx"),
+ },
+ },
+ {
+ /* TongFang GMxHGxx/TUXEDO Stellaris Slim Gen1 AMD */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "GMxHGxx"),
+ },
+ },
{ }
};
diff --git a/drivers/acpi/riscv/Makefile b/drivers/acpi/riscv/Makefile
index 8b3b126e0b..86b0925f61 100644
--- a/drivers/acpi/riscv/Makefile
+++ b/drivers/acpi/riscv/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += rhct.o
+obj-y += rhct.o
+obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o
+obj-$(CONFIG_ACPI_CPPC_LIB) += cppc.o
diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c
new file mode 100644
index 0000000000..4cdff387de
--- /dev/null
+++ b/drivers/acpi/riscv/cppc.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implement CPPC FFH helper routines for RISC-V.
+ *
+ * Copyright (C) 2024 Ventana Micro Systems Inc.
+ */
+
+#include <acpi/cppc_acpi.h>
+#include <asm/csr.h>
+#include <asm/sbi.h>
+
+#define SBI_EXT_CPPC 0x43505043
+
+/* CPPC interfaces defined in SBI spec */
+#define SBI_CPPC_PROBE 0x0
+#define SBI_CPPC_READ 0x1
+#define SBI_CPPC_READ_HI 0x2
+#define SBI_CPPC_WRITE 0x3
+
+/* RISC-V FFH definitions from RISC-V FFH spec */
+#define FFH_CPPC_TYPE(r) (((r) & GENMASK_ULL(63, 60)) >> 60)
+#define FFH_CPPC_SBI_REG(r) ((r) & GENMASK(31, 0))
+#define FFH_CPPC_CSR_NUM(r) ((r) & GENMASK(11, 0))
+
+#define FFH_CPPC_SBI 0x1
+#define FFH_CPPC_CSR 0x2
+
+struct sbi_cppc_data {
+ u64 val;
+ u32 reg;
+ struct sbiret ret;
+};
+
+static bool cppc_ext_present;
+
+static int __init sbi_cppc_init(void)
+{
+ if (sbi_spec_version >= sbi_mk_version(2, 0) &&
+ sbi_probe_extension(SBI_EXT_CPPC) > 0) {
+ pr_info("SBI CPPC extension detected\n");
+ cppc_ext_present = true;
+ } else {
+ pr_info("SBI CPPC extension NOT detected!!\n");
+ cppc_ext_present = false;
+ }
+
+ return 0;
+}
+device_initcall(sbi_cppc_init);
+
+static void sbi_cppc_read(void *read_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
+
+ data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ,
+ data->reg, 0, 0, 0, 0, 0);
+}
+
+static void sbi_cppc_write(void *write_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
+
+ data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE,
+ data->reg, data->val, 0, 0, 0, 0);
+}
+
+static void cppc_ffh_csr_read(void *read_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;
+
+ switch (data->reg) {
+ /* Support only TIME CSR for now */
+ case CSR_TIME:
+ data->ret.value = csr_read(CSR_TIME);
+ data->ret.error = 0;
+ break;
+ default:
+ data->ret.error = -EINVAL;
+ break;
+ }
+}
+
+static void cppc_ffh_csr_write(void *write_data)
+{
+ struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;
+
+ data->ret.error = -EINVAL;
+}
+
+/*
+ * Refer to drivers/acpi/cppc_acpi.c for the description of the functions
+ * below.
+ */
+bool cpc_ffh_supported(void)
+{
+ return true;
+}
+
+int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)
+{
+ struct sbi_cppc_data data;
+
+ if (WARN_ON_ONCE(irqs_disabled()))
+ return -EPERM;
+
+ if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
+ if (!cppc_ext_present)
+ return -EINVAL;
+
+ data.reg = FFH_CPPC_SBI_REG(reg->address);
+
+ smp_call_function_single(cpu, sbi_cppc_read, &data, 1);
+
+ *val = data.ret.value;
+
+ return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
+ } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
+ data.reg = FFH_CPPC_CSR_NUM(reg->address);
+
+ smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);
+
+ *val = data.ret.value;
+
+ return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
+ }
+
+ return -EINVAL;
+}
+
+int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)
+{
+ struct sbi_cppc_data data;
+
+ if (WARN_ON_ONCE(irqs_disabled()))
+ return -EPERM;
+
+ if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {
+ if (!cppc_ext_present)
+ return -EINVAL;
+
+ data.reg = FFH_CPPC_SBI_REG(reg->address);
+ data.val = val;
+
+ smp_call_function_single(cpu, sbi_cppc_write, &data, 1);
+
+ return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
+ } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {
+ data.reg = FFH_CPPC_CSR_NUM(reg->address);
+ data.val = val;
+
+ smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);
+
+ return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/acpi/riscv/cpuidle.c b/drivers/acpi/riscv/cpuidle.c
new file mode 100644
index 0000000000..624f9bbdb5
--- /dev/null
+++ b/drivers/acpi/riscv/cpuidle.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024, Ventana Micro Systems Inc
+ * Author: Sunil V L <sunilvl@ventanamicro.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpuidle.h>
+#include <linux/suspend.h>
+#include <asm/cpuidle.h>
+#include <asm/sbi.h>
+#include <asm/suspend.h>
+
+#define RISCV_FFH_LPI_TYPE_MASK GENMASK_ULL(63, 60)
+#define RISCV_FFH_LPI_RSVD_MASK GENMASK_ULL(59, 32)
+
+#define RISCV_FFH_LPI_TYPE_SBI BIT_ULL(60)
+
+static int acpi_cpu_init_idle(unsigned int cpu)
+{
+ int i;
+ struct acpi_lpi_state *lpi;
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+
+ if (unlikely(!pr || !pr->flags.has_lpi))
+ return -EINVAL;
+
+ if (!riscv_sbi_hsm_is_supported())
+ return -ENODEV;
+
+ if (pr->power.count <= 1)
+ return -ENODEV;
+
+ for (i = 1; i < pr->power.count; i++) {
+ u32 state;
+
+ lpi = &pr->power.lpi_states[i];
+
+ /*
+ * Validate Entry Method as per FFH spec.
+ * bits[63:60] should be 0x1
+ * bits[59:32] should be 0x0
+ * bits[31:0] represent a SBI power_state
+ */
+ if (((lpi->address & RISCV_FFH_LPI_TYPE_MASK) != RISCV_FFH_LPI_TYPE_SBI) ||
+ (lpi->address & RISCV_FFH_LPI_RSVD_MASK)) {
+ pr_warn("Invalid LPI entry method %#llx\n", lpi->address);
+ return -EINVAL;
+ }
+
+ state = lpi->address;
+ if (!riscv_sbi_suspend_state_is_valid(state)) {
+ pr_warn("Invalid SBI power state %#x\n", state);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int acpi_processor_ffh_lpi_probe(unsigned int cpu)
+{
+ return acpi_cpu_init_idle(cpu);
+}
+
+int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
+{
+ u32 state = lpi->address;
+
+ if (state & SBI_HSM_SUSP_NON_RET_BIT)
+ return CPU_PM_CPU_IDLE_ENTER_PARAM(riscv_sbi_hart_suspend,
+ lpi->index,
+ state);
+ else
+ return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(riscv_sbi_hart_suspend,
+ lpi->index,
+ state);
+}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index eb4ca85d16..d1464324de 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -244,6 +244,53 @@ static int acpi_scan_try_to_offline(struct acpi_device *device)
return 0;
}
+static int acpi_scan_check_and_detach(struct acpi_device *adev, void *check)
+{
+ struct acpi_scan_handler *handler = adev->handler;
+
+ acpi_dev_for_each_child_reverse(adev, acpi_scan_check_and_detach, check);
+
+ if (check) {
+ acpi_bus_get_status(adev);
+ /*
+ * Skip devices that are still there and take the enabled
+ * flag into account.
+ */
+ if (acpi_device_is_enabled(adev))
+ return 0;
+
+ /* Skip device that have not been enumerated. */
+ if (!acpi_device_enumerated(adev)) {
+ dev_dbg(&adev->dev, "Still not enumerated\n");
+ return 0;
+ }
+ }
+
+ adev->flags.match_driver = false;
+ if (handler) {
+ if (handler->detach)
+ handler->detach(adev);
+
+ adev->handler = NULL;
+ } else {
+ device_release_driver(&adev->dev);
+ }
+ /*
+ * Most likely, the device is going away, so put it into D3cold before
+ * that.
+ */
+ acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
+ adev->flags.initialized = false;
+ acpi_device_clear_enumerated(adev);
+
+ return 0;
+}
+
+static void acpi_scan_check_subtree(struct acpi_device *adev)
+{
+ acpi_scan_check_and_detach(adev, (void *)true);
+}
+
static int acpi_scan_hot_remove(struct acpi_device *device)
{
acpi_handle handle = device->handle;
@@ -289,71 +336,62 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
return 0;
}
-static int acpi_scan_device_not_enumerated(struct acpi_device *adev)
+static int acpi_scan_rescan_bus(struct acpi_device *adev)
{
- if (!acpi_device_enumerated(adev)) {
- dev_warn(&adev->dev, "Still not enumerated\n");
- return -EALREADY;
- }
- acpi_bus_trim(adev);
- return 0;
+ struct acpi_scan_handler *handler = adev->handler;
+ int ret;
+
+ if (handler && handler->hotplug.scan_dependent)
+ ret = handler->hotplug.scan_dependent(adev);
+ else
+ ret = acpi_bus_scan(adev->handle);
+
+ if (ret)
+ dev_info(&adev->dev, "Namespace scan failure\n");
+
+ return ret;
}
static int acpi_scan_device_check(struct acpi_device *adev)
{
- int error;
+ struct acpi_device *parent;
- acpi_bus_get_status(adev);
- if (acpi_device_is_present(adev)) {
- /*
- * This function is only called for device objects for which
- * matching scan handlers exist. The only situation in which
- * the scan handler is not attached to this device object yet
- * is when the device has just appeared (either it wasn't
- * present at all before or it was removed and then added
- * again).
- */
- if (adev->handler) {
- dev_dbg(&adev->dev, "Already enumerated\n");
- return 0;
- }
- error = acpi_bus_scan(adev->handle);
- if (error) {
- dev_warn(&adev->dev, "Namespace scan failure\n");
- return error;
- }
- } else {
- error = acpi_scan_device_not_enumerated(adev);
- }
- return error;
-}
+ acpi_scan_check_subtree(adev);
-static int acpi_scan_bus_check(struct acpi_device *adev, void *not_used)
-{
- struct acpi_scan_handler *handler = adev->handler;
- int error;
+ if (!acpi_device_is_present(adev))
+ return 0;
- acpi_bus_get_status(adev);
- if (!acpi_device_is_present(adev)) {
- acpi_scan_device_not_enumerated(adev);
+ /*
+ * This function is only called for device objects for which matching
+ * scan handlers exist. The only situation in which the scan handler
+ * is not attached to this device object yet is when the device has
+ * just appeared (either it wasn't present at all before or it was
+ * removed and then added again).
+ */
+ if (adev->handler) {
+ dev_dbg(&adev->dev, "Already enumerated\n");
return 0;
}
- if (handler && handler->hotplug.scan_dependent)
- return handler->hotplug.scan_dependent(adev);
- error = acpi_bus_scan(adev->handle);
- if (error) {
- dev_warn(&adev->dev, "Namespace scan failure\n");
- return error;
- }
- return acpi_dev_for_each_child(adev, acpi_scan_bus_check, NULL);
+ parent = acpi_dev_parent(adev);
+ if (!parent)
+ parent = adev;
+
+ return acpi_scan_rescan_bus(parent);
+}
+
+static int acpi_scan_bus_check(struct acpi_device *adev)
+{
+ acpi_scan_check_subtree(adev);
+
+ return acpi_scan_rescan_bus(adev);
}
static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
{
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
- return acpi_scan_bus_check(adev, NULL);
+ return acpi_scan_bus_check(adev);
case ACPI_NOTIFY_DEVICE_CHECK:
return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST:
@@ -794,6 +832,7 @@ static const char * const acpi_honor_dep_ids[] = {
"INTC1059", /* IVSC (TGL) driver must be loaded to allow i2c access to camera sensors */
"INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */
"INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */
+ "INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */
NULL
};
@@ -1721,7 +1760,9 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
{"BSG1160", },
{"BSG2150", },
{"CSC3551", },
+ {"CSC3554", },
{"CSC3556", },
+ {"CSC3557", },
{"INT33FE", },
{"INT3515", },
/* Non-conforming _HID for Cirrus Logic already released */
@@ -1919,6 +1960,11 @@ bool acpi_device_is_present(const struct acpi_device *adev)
return adev->status.present || adev->status.functional;
}
+bool acpi_device_is_enabled(const struct acpi_device *adev)
+{
+ return adev->status.present && adev->status.enabled;
+}
+
static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
const char *idstr,
const struct acpi_device_id **matchid)
@@ -2547,32 +2593,6 @@ int acpi_bus_scan(acpi_handle handle)
}
EXPORT_SYMBOL(acpi_bus_scan);
-static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
-{
- struct acpi_scan_handler *handler = adev->handler;
-
- acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, NULL);
-
- adev->flags.match_driver = false;
- if (handler) {
- if (handler->detach)
- handler->detach(adev);
-
- adev->handler = NULL;
- } else {
- device_release_driver(&adev->dev);
- }
- /*
- * Most likely, the device is going away, so put it into D3cold before
- * that.
- */
- acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
- adev->flags.initialized = false;
- acpi_device_clear_enumerated(adev);
-
- return 0;
-}
-
/**
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
@@ -2581,7 +2601,7 @@ static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
*/
void acpi_bus_trim(struct acpi_device *adev)
{
- acpi_bus_trim_one(adev, NULL);
+ acpi_scan_check_and_detach(adev, NULL);
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 728acfeb77..889f1c1a1f 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -502,6 +502,7 @@ static void acpi_pm_finish(void)
/**
* acpi_pm_start - Start system PM transition.
+ * @acpi_state: The target ACPI power state to transition to.
*/
static void acpi_pm_start(u32 acpi_state)
{
@@ -540,8 +541,9 @@ static u32 acpi_suspend_states[] = {
};
/**
- * acpi_suspend_begin - Set the target system sleep state to the state
- * associated with given @pm_state, if supported.
+ * acpi_suspend_begin - Set the target system sleep state to the state
+ * associated with given @pm_state, if supported.
+ * @pm_state: The target system power management state.
*/
static int acpi_suspend_begin(suspend_state_t pm_state)
{
@@ -671,10 +673,11 @@ static const struct platform_suspend_ops acpi_suspend_ops = {
};
/**
- * acpi_suspend_begin_old - Set the target system sleep state to the
- * state associated with given @pm_state, if supported, and
- * execute the _PTS control method. This function is used if the
- * pre-ACPI 2.0 suspend ordering has been requested.
+ * acpi_suspend_begin_old - Set the target system sleep state to the
+ * state associated with given @pm_state, if supported, and
+ * execute the _PTS control method. This function is used if the
+ * pre-ACPI 2.0 suspend ordering has been requested.
+ * @pm_state: The target suspend state for the system.
*/
static int acpi_suspend_begin_old(suspend_state_t pm_state)
{
@@ -967,10 +970,11 @@ static const struct platform_hibernation_ops acpi_hibernation_ops = {
};
/**
- * acpi_hibernation_begin_old - Set the target system sleep state to
- * ACPI_STATE_S4 and execute the _PTS control method. This
- * function is used if the pre-ACPI 2.0 suspend ordering has been
- * requested.
+ * acpi_hibernation_begin_old - Set the target system sleep state to
+ * ACPI_STATE_S4 and execute the _PTS control method. This
+ * function is used if the pre-ACPI 2.0 suspend ordering has been
+ * requested.
+ * @stage: The power management event message.
*/
static int acpi_hibernation_begin_old(pm_message_t stage)
{
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index b07f7d091d..b976e5fc3f 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -253,7 +253,7 @@ int __init_or_acpilib acpi_table_parse_entries_array(
count = acpi_parse_entries_array(id, table_size,
(union fw_table_header *)table_header,
- proc, proc_num, max_entries);
+ 0, proc, proc_num, max_entries);
acpi_put_table(table_header);
return count;
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 4748e80612..a0cfc857fb 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -47,6 +47,8 @@
#define ACPI_THERMAL_TRIP_PASSIVE (-1)
+#define ACPI_THERMAL_MAX_NR_TRIPS (ACPI_THERMAL_MAX_ACTIVE + 3)
+
/*
* This exception is thrown out in two cases:
* 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
@@ -112,7 +114,6 @@ struct acpi_thermal {
unsigned long polling_frequency;
volatile u8 zombie;
struct acpi_thermal_trips trips;
- struct thermal_trip *trip_table;
struct thermal_zone_device *thermal_zone;
int kelvin_offset; /* in millidegrees */
struct work_struct thermal_check_work;
@@ -167,11 +168,17 @@ static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
static int acpi_thermal_temp(struct acpi_thermal *tz, int temp_deci_k)
{
+ int temp;
+
if (temp_deci_k == THERMAL_TEMP_INVALID)
return THERMAL_TEMP_INVALID;
- return deci_kelvin_to_millicelsius_with_offset(temp_deci_k,
+ temp = deci_kelvin_to_millicelsius_with_offset(temp_deci_k,
tz->kelvin_offset);
+ if (temp <= 0)
+ return THERMAL_TEMP_INVALID;
+
+ return temp;
}
static bool acpi_thermal_trip_valid(struct acpi_thermal_trip *acpi_trip)
@@ -451,26 +458,19 @@ fail:
return false;
}
-static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
+static void acpi_thermal_get_trip_points(struct acpi_thermal *tz)
{
- unsigned int count = 0;
int i;
- if (acpi_thermal_init_trip(tz, ACPI_THERMAL_TRIP_PASSIVE))
- count++;
+ acpi_thermal_init_trip(tz, ACPI_THERMAL_TRIP_PASSIVE);
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
- if (acpi_thermal_init_trip(tz, i))
- count++;
- else
+ if (!acpi_thermal_init_trip(tz, i))
break;
-
}
while (++i < ACPI_THERMAL_MAX_ACTIVE)
tz->trips.active[i].trip.temp_dk = THERMAL_TEMP_INVALID;
-
- return count;
}
/* sys I/F for generic thermal sysfs support */
@@ -626,7 +626,7 @@ acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
return acpi_thermal_bind_unbind_cdev(thermal, cdev, false);
}
-static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+static const struct thermal_zone_device_ops acpi_thermal_zone_ops = {
.bind = acpi_thermal_bind_cooling_device,
.unbind = acpi_thermal_unbind_cooling_device,
.get_temp = thermal_get_temp,
@@ -662,19 +662,21 @@ static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz)
}
static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz,
+ const struct thermal_trip *trip_table,
unsigned int trip_count,
int passive_delay)
{
int result;
- tz->thermal_zone = thermal_zone_device_register_with_trips("acpitz",
- tz->trip_table,
- trip_count,
- 0, tz,
- &acpi_thermal_zone_ops,
- NULL,
- passive_delay,
- tz->polling_frequency * 100);
+ if (trip_count)
+ tz->thermal_zone = thermal_zone_device_register_with_trips(
+ "acpitz", trip_table, trip_count, tz,
+ &acpi_thermal_zone_ops, NULL, passive_delay,
+ tz->polling_frequency * 100);
+ else
+ tz->thermal_zone = thermal_tripless_zone_device_register(
+ "acpitz", tz, &acpi_thermal_zone_ops, NULL);
+
if (IS_ERR(tz->thermal_zone))
return PTR_ERR(tz->thermal_zone);
@@ -823,10 +825,10 @@ static void acpi_thermal_free_thermal_zone(struct acpi_thermal *tz)
static int acpi_thermal_add(struct acpi_device *device)
{
+ struct thermal_trip trip_table[ACPI_THERMAL_MAX_NR_TRIPS] = { 0 };
struct acpi_thermal_trip *acpi_trip;
struct thermal_trip *trip;
struct acpi_thermal *tz;
- unsigned int trip_count;
int crit_temp, hot_temp;
int passive_delay = 0;
int result;
@@ -848,21 +850,10 @@ static int acpi_thermal_add(struct acpi_device *device)
acpi_thermal_aml_dependency_fix(tz);
/* Get trip points [_CRT, _PSV, etc.] (required). */
- trip_count = acpi_thermal_get_trip_points(tz);
+ acpi_thermal_get_trip_points(tz);
crit_temp = acpi_thermal_get_critical_trip(tz);
- if (crit_temp != THERMAL_TEMP_INVALID)
- trip_count++;
-
hot_temp = acpi_thermal_get_hot_trip(tz);
- if (hot_temp != THERMAL_TEMP_INVALID)
- trip_count++;
-
- if (!trip_count) {
- pr_warn(FW_BUG "No valid trip points!\n");
- result = -ENODEV;
- goto free_memory;
- }
/* Get temperature [_TMP] (required). */
result = acpi_thermal_get_temperature(tz);
@@ -881,13 +872,7 @@ static int acpi_thermal_add(struct acpi_device *device)
acpi_thermal_guess_offset(tz, crit_temp);
- trip = kcalloc(trip_count, sizeof(*trip), GFP_KERNEL);
- if (!trip) {
- result = -ENOMEM;
- goto free_memory;
- }
-
- tz->trip_table = trip;
+ trip = trip_table;
if (crit_temp != THERMAL_TEMP_INVALID) {
trip->type = THERMAL_TRIP_CRITICAL;
@@ -923,9 +908,14 @@ static int acpi_thermal_add(struct acpi_device *device)
trip++;
}
- result = acpi_thermal_register_thermal_zone(tz, trip_count, passive_delay);
+ if (trip == trip_table)
+ pr_warn(FW_BUG "No valid trip points!\n");
+
+ result = acpi_thermal_register_thermal_zone(tz, trip_table,
+ trip - trip_table,
+ passive_delay);
if (result)
- goto free_trips;
+ goto free_memory;
refcount_set(&tz->thermal_check_count, 3);
mutex_init(&tz->thermal_check_lock);
@@ -944,8 +934,6 @@ static int acpi_thermal_add(struct acpi_device *device)
flush_wq:
flush_workqueue(acpi_thermal_pm_queue);
acpi_thermal_unregister_thermal_zone(tz);
-free_trips:
- kfree(tz->trip_table);
free_memory:
acpi_thermal_free_thermal_zone(tz);
@@ -966,7 +954,6 @@ static void acpi_thermal_remove(struct acpi_device *device)
flush_workqueue(acpi_thermal_pm_queue);
acpi_thermal_unregister_thermal_zone(tz);
- kfree(tz->trip_table);
acpi_thermal_free_thermal_zone(tz);
}
diff --git a/drivers/acpi/thermal_lib.c b/drivers/acpi/thermal_lib.c
index 4e0519ca97..6214d6ebe1 100644
--- a/drivers/acpi/thermal_lib.c
+++ b/drivers/acpi/thermal_lib.c
@@ -100,7 +100,7 @@ static int thermal_temp(int error, int temp_decik, int *ret_temp)
*/
int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
{
- int temp_decik;
+ int temp_decik = 0;
int ret = acpi_active_trip_temp(adev, id, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
@@ -119,7 +119,7 @@ EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp);
*/
int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
{
- int temp_decik;
+ int temp_decik = 0;
int ret = acpi_passive_trip_temp(adev, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
@@ -139,7 +139,7 @@ EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp);
*/
int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
{
- int temp_decik;
+ int temp_decik = 0;
int ret = acpi_hot_trip_temp(adev, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
@@ -158,7 +158,7 @@ EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp);
*/
int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
{
- int temp_decik;
+ int temp_decik = 0;
int ret = acpi_critical_trip_temp(adev, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index abac5cc254..202234ba54 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -559,7 +559,7 @@ EXPORT_SYMBOL(acpi_evaluate_ost);
*
* Caller must free the returned buffer
*/
-static char *acpi_handle_path(acpi_handle handle)
+char *acpi_handle_path(acpi_handle handle)
{
struct acpi_buffer buffer = {
.length = ACPI_ALLOCATE_BUFFER,
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 9fdcc620c6..2cc3821b2b 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -499,6 +499,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
{
.callback = video_detect_force_native,
+ /* Lenovo Slim 7 16ARH7 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UX"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
/* Lenovo ThinkPad X131e (3371 AMD version) */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index 7d64e655f1..dd0b40b9bb 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -488,7 +488,19 @@ static int lps0_device_attach(struct acpi_device *adev,
rev_id = 1;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
- lps0_dsm_func_mask_microsoft = -EINVAL;
+ if (lps0_dsm_func_mask > 0 && lps0_dsm_func_mask_microsoft > 0) {
+ unsigned int func_mask;
+
+ /*
+ * Log a message if the _DSM function sets for two
+ * different UUIDs overlap.
+ */
+ func_mask = lps0_dsm_func_mask & lps0_dsm_func_mask_microsoft;
+ if (func_mask)
+ acpi_handle_info(adev->handle,
+ "Duplicate LPS0 _DSM functions (mask: 0x%x)\n",
+ func_mask);
+ }
}
if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
@@ -549,19 +561,22 @@ int acpi_s2idle_prepare_late(void)
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
/* LPS0 entry */
- if (lps0_dsm_func_mask > 0)
- acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
- ACPI_LPS0_ENTRY_AMD :
- ACPI_LPS0_ENTRY,
+ if (lps0_dsm_func_mask > 0 && acpi_s2idle_vendor_amd())
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY_AMD,
lps0_dsm_func_mask, lps0_dsm_guid);
+
if (lps0_dsm_func_mask_microsoft > 0) {
- /* modern standby entry */
+ /* Modern Standby entry */
acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
}
+ if (lps0_dsm_func_mask > 0 && !acpi_s2idle_vendor_amd())
+ acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
+ lps0_dsm_func_mask, lps0_dsm_guid);
+
list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
if (handler->prepare)
handler->prepare();
@@ -600,14 +615,14 @@ void acpi_s2idle_restore_early(void)
ACPI_LPS0_EXIT_AMD :
ACPI_LPS0_EXIT,
lps0_dsm_func_mask, lps0_dsm_guid);
- if (lps0_dsm_func_mask_microsoft > 0)
+
+ if (lps0_dsm_func_mask_microsoft > 0) {
acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
-
- /* Modern standby exit */
- if (lps0_dsm_func_mask_microsoft > 0)
+ /* Modern Standby exit */
acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
+ }
/* Screen on */
if (lps0_dsm_func_mask_microsoft > 0)
diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c
index 90c3d2eab9..448e0d14fd 100644
--- a/drivers/acpi/x86/utils.c
+++ b/drivers/acpi/x86/utils.c
@@ -197,16 +197,16 @@ bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *s
}
/*
- * AMD systems from Renoir and Lucienne *require* that the NVME controller
+ * AMD systems from Renoir onwards *require* that the NVME controller
* is put into D3 over a Modern Standby / suspend-to-idle cycle.
*
* This is "typically" accomplished using the `StorageD3Enable`
* property in the _DSD that is checked via the `acpi_storage_d3` function
- * but this property was introduced after many of these systems launched
- * and most OEM systems don't have it in their BIOS.
+ * but some OEM systems still don't have it in their BIOS.
*
* The Microsoft documentation for StorageD3Enable mentioned that Windows has
- * a hardcoded allowlist for D3 support, which was used for these platforms.
+ * a hardcoded allowlist for D3 support as well as a registry key to override
+ * the BIOS, which has been used for these cases.
*
* This allows quirking on Linux in a similar fashion.
*
@@ -219,19 +219,15 @@ bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *s
* https://bugzilla.kernel.org/show_bug.cgi?id=216773
* https://bugzilla.kernel.org/show_bug.cgi?id=217003
* 2) On at least one HP system StorageD3Enable is missing on the second NVME
- disk in the system.
+ * disk in the system.
+ * 3) On at least one HP Rembrandt system StorageD3Enable is missing on the only
+ * NVME device.
*/
-static const struct x86_cpu_id storage_d3_cpu_ids[] = {
- X86_MATCH_VENDOR_FAM_MODEL(AMD, 23, 24, NULL), /* Picasso */
- X86_MATCH_VENDOR_FAM_MODEL(AMD, 23, 96, NULL), /* Renoir */
- X86_MATCH_VENDOR_FAM_MODEL(AMD, 23, 104, NULL), /* Lucienne */
- X86_MATCH_VENDOR_FAM_MODEL(AMD, 25, 80, NULL), /* Cezanne */
- {}
-};
-
bool force_storage_d3(void)
{
- return x86_match_cpu(storage_d3_cpu_ids);
+ if (!cpu_feature_enabled(X86_FEATURE_ZEN))
+ return false;
+ return acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0;
}
/*
@@ -260,9 +256,10 @@ bool force_storage_d3(void)
#define ACPI_QUIRK_SKIP_I2C_CLIENTS BIT(0)
#define ACPI_QUIRK_UART1_SKIP BIT(1)
#define ACPI_QUIRK_UART1_TTY_UART2_SKIP BIT(2)
-#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY BIT(3)
-#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY BIT(4)
-#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS BIT(5)
+#define ACPI_QUIRK_PNP_UART1_SKIP BIT(3)
+#define ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY BIT(4)
+#define ACPI_QUIRK_USE_ACPI_AC_AND_BATTERY BIT(5)
+#define ACPI_QUIRK_SKIP_GPIO_EVENT_HANDLERS BIT(6)
static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
/*
@@ -342,6 +339,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"),
},
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_PNP_UART1_SKIP |
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
},
{
@@ -440,14 +438,18 @@ static int acpi_dmi_skip_serdev_enumeration(struct device *controller_parent, bo
if (ret)
return 0;
- /* to not match on PNP enumerated debug UARTs */
- if (!dev_is_platform(controller_parent))
- return 0;
-
dmi_id = dmi_first_match(acpi_quirk_skip_dmi_ids);
if (dmi_id)
quirks = (unsigned long)dmi_id->driver_data;
+ if (!dev_is_platform(controller_parent)) {
+ /* PNP enumerated UARTs */
+ if ((quirks & ACPI_QUIRK_PNP_UART1_SKIP) && uid == 1)
+ *skip = true;
+
+ return 0;
+ }
+
if ((quirks & ACPI_QUIRK_UART1_SKIP) && uid == 1)
*skip = true;