From nobody Sun Apr 5 14:58:35 2026 Received: from relay12.grserver.gr (relay12.grserver.gr [88.99.38.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A9E936C9F2; Mon, 9 Mar 2026 20:52:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=88.99.38.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089559; cv=none; b=kXXV4qRbEjIxeBzWfnlpsq9DzY1vyiai5vCeFSb0BT1VjU+KUr+cNlL/RUhg17dGRuD/ef0qFL4hvk012ISCYnHYPDyvvciRMci52/OOLpbyd5RFCsVZBhMkd5a52cIbIoFdYgg4EhFW0hXNBqeVGSfQ2m7PvABTENvnY8DRuO0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089559; c=relaxed/simple; bh=Qnfh89u0vhjQKmaP9xDR4f/PmYXUJgUwzK6sihpDThE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=e7tW/CO+W0TvlztJs4lpTI73bT20VnokIpra8RGdOBaSB19hhJOQPWt0uS0l6IKuSZ1jsz6AbD5l9dkBwlLUudiBYSLG1fQGMeYKi/M++JK7MJFbr1PKslQN4+B0ZLVAWnqNbV7k7TmVJAFesFnZRYY9exIZckgp5NmqC89RD1Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=D9F/aWyz; arc=none smtp.client-ip=88.99.38.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="D9F/aWyz" Received: from relay12 (localhost [127.0.0.1]) by relay12.grserver.gr (Proxmox) with ESMTP id 12645BC05C; Mon, 9 Mar 2026 22:52:35 +0200 (EET) Received: from linux3247.grserver.gr (linux3247.grserver.gr [213.158.90.240]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by relay12.grserver.gr (Proxmox) with ESMTPS id 4F52ABC061; Mon, 9 Mar 2026 22:52:34 +0200 (EET) Received: from antheas-z13 (unknown [IPv6:2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1]) by linux3247.grserver.gr (Postfix) with ESMTPSA id 189CD201CC1; Mon, 9 Mar 2026 22:52:33 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1773089553; bh=CNISal0/YL04BRuVzJuK743WbSRUUzSJiPN9xDYerQU=; h=From:To:Subject; b=D9F/aWyzZ0EuCmy+ti7Q+6oh2OrkV7IRv+nMD1tp7i0l1SkX1WNY02yNZPgfeZVGs vA1WotnZ/EMZlZ2Ee3We6rwhLPxlvs2taxbHVj9Lsfz0brvyTKnHqO8QMBtPb4fiuk lwuJPUbZTgjFvkFvYYAOhLFi07qrg4et3oqX7qLsSNyGoskBIyCIMVgPXdPXYXYcy0 j42f577EQQCpxywxlsFHWMUhtRKCdFN+2qs0FJUex8ap22wqpRCfjYsjETg+1LCCqo ounjgnlMxxL3bPbgsIt6izFk/MHeFh1h63hZLm064+gSTcetFdXd6/ZCCzRc+6dji1 B7+T7LDuC3Ncw== Authentication-Results: linux3247.grserver.gr; spf=pass (sender IP is 2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1) smtp.mailfrom=lkml@antheas.dev smtp.helo=antheas-z13 Received-SPF: pass (linux3247.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: Mario.Limonciello@amd.com Cc: W_Armin@gmx.de, sashal@kernel.org, Shyam-Sundar.S-k@amd.com, derekjohn.clark@gmail.com, denis.benato@linux.dev, i@rong.moe, linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, Antheas Kapenekakis Subject: [RFC v4 1/4] Documentation: firmware-attributes: generalize save_settings entry Date: Mon, 9 Mar 2026 21:51:22 +0100 Message-ID: <20260309205125.293148-2-lkml@antheas.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260309205125.293148-1-lkml@antheas.dev> References: <20260309205125.293148-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <177308955370.1090732.13543690827643628899@linux3247.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 1.4.3 at linux3247.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" The save_settings interface is also implemented by amd_dptc, which has the same bulk/single/save semantics but no save-count limitation. Generalize the description to cover both drivers: move the Lenovo 48-save architectural constraint into a driver-specific notes section and add the amd_dptc behavior alongside it. Signed-off-by: Antheas Kapenekakis --- .../testing/sysfs-class-firmware-attributes | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Do= cumentation/ABI/testing/sysfs-class-firmware-attributes index 2713efa509b4..e0b43b14fa19 100644 --- a/Documentation/ABI/testing/sysfs-class-firmware-attributes +++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes @@ -388,31 +388,42 @@ What: /sys/class/firmware-attributes/*/attributes/sa= ve_settings Date: August 2023 KernelVersion: 6.6 Contact: Mark Pearson + Antheas Kapenekakis Description: - On Lenovo platforms there is a limitation in the number of times an attr= ibute can be - saved. This is an architectural limitation and it limits the number of a= ttributes - that can be modified to 48. - A solution for this is instead of the attribute being saved after every = modification, - to allow a user to bulk set the attributes, and then trigger a final sav= e. This allows - unlimited attributes. + Controls how writes to current_value are applied to the hardware. =20 Read the attribute to check what save mode is enabled (single or bulk). E.g: - # cat /sys/class/firmware-attributes/thinklmi/attributes/save_settings + # cat /sys/class/firmware-attributes/*/attributes/save_settings single =20 Write the attribute with 'bulk' to enable bulk save mode. - Write the attribute with 'single' to enable saving, after every attribut= e set. - The default setting is single mode. + Write the attribute with 'single' to enable saving, after every + attribute set. The default setting is single mode. E.g: - # echo bulk > /sys/class/firmware-attributes/thinklmi/attributes/save_se= ttings + # echo bulk > /sys/class/firmware-attributes/*/attributes/save_settings =20 - When in bulk mode write 'save' to trigger a save of all currently modifi= ed attributes. - Note, once a save has been triggered, in bulk mode, attributes can no lo= nger be set and - will return a permissions error. This is to prevent users hitting the 48= + save limitation - (which requires entering the BIOS to clear the error condition) + When in bulk mode write 'save' to trigger an apply of all + currently staged attributes. E.g: - # echo save > /sys/class/firmware-attributes/thinklmi/attributes/save_se= ttings + # echo save > /sys/class/firmware-attributes/*/attributes/save_settings + + Driver-specific notes: + + thinklmi (Lenovo): On Lenovo platforms there is a limitation in + the number of times an attribute can be saved. This is an + architectural limitation and it limits the number of attributes + that can be modified to 48. + + Once a save has been triggered in bulk mode, attributes can no + longer be set and will return a permissions error. This is to + prevent users hitting the 48+ save limitation (which requires + entering the BIOS to clear the error condition). + + amd-dptc (AMD DPTC): No save-count limitation. 'save' can be + called any number of times. Returns -ENOENT if no values have + been staged. In addition, when in 'single' mode, the driver + uses pm ops to trigger a save of staged attributes on resume. =20 What: /sys/class/firmware-attributes/*/attributes/debug_cmd Date: July 2021 --=20 2.52.0 From nobody Sun Apr 5 14:58:35 2026 Received: from relay11.grserver.gr (relay11.grserver.gr [78.46.171.57]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A1A736D517; Mon, 9 Mar 2026 20:52:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=78.46.171.57 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089566; cv=none; b=dtayHgWioWDHAQ7h7mHBo2XR3JNseTDAkznpSPJABjKTVgoyNSaopzRDrLYl2y0e9JDCFCER57GdP11/mLojfTKGirdynueE5zFXhk9GAplhBxG26n4Yu34j0a1wlqFXAckpoeAuq/NAwgXe0+wUJPtS87SjgQiJVWHg2b7I+pM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089566; c=relaxed/simple; bh=lNwm8Pv0uptDaI1TKeg3iVv89jcGePaO4Jk3Ahu9F3Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kyiRFFiSmB8e7QN6a5jUSpYcnm3Hx43CEWZHn5RJAkEf6UeW2UQuz7ACu5EctcAIRsx1cFDbkR/ucrP9lKUNhH0sSd1gZWvT0jskuyXgE8/91w/+HOTB/vZsiR55IqM6LVsc3p82VZSy4a6FRhMq3hyrltXAd3akg/0NCLmqb4A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=m4oNGab2; arc=none smtp.client-ip=78.46.171.57 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="m4oNGab2" Received: from relay11 (localhost.localdomain [127.0.0.1]) by relay11.grserver.gr (Proxmox) with ESMTP id 06154C975C; Mon, 9 Mar 2026 22:52:37 +0200 (EET) Received: from linux3247.grserver.gr (linux3247.grserver.gr [213.158.90.240]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by relay11.grserver.gr (Proxmox) with ESMTPS id 414ECC9A3D; Mon, 9 Mar 2026 22:52:35 +0200 (EET) Received: from antheas-z13 (unknown [IPv6:2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1]) by linux3247.grserver.gr (Postfix) with ESMTPSA id 4EAB9201CBF; Mon, 9 Mar 2026 22:52:34 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1773089555; bh=R2WkCwfjWziqx9cMeiBdk0iAhd8dSG3tFYtJPlO0DxA=; h=From:To:Subject; b=m4oNGab2f7gMmWMkLFV/1qgQwVYOuJ7Bew7XIVmsGeni134f3TDTcHLI85nkNJ3xW wbtqiplZEnpi27Lt+PX25p06x0ooir19dqG9JjgQCs/uxyJMH+VbeIczyoVfKK4RmU gthnXZC8HzzKO0gzKfOW/tiqo+G9IIi3qotoBN3VPD/Ok7qw/2Lfvy6oESGEChQ1nc 8FICZlaEU9VsNcNczTv5MokRcmjNvAYo0f7J+wJTRg6PQSaJuqgVRW1OSNtmoM8K+A lrRgDse5AJ1MwH2g1FXkY2GZAWWj/m0qQOpr5rfgbw6IoflxdPud/N2HSxhkOaoNpI NTmYj2djgy3zw== Authentication-Results: linux3247.grserver.gr; spf=pass (sender IP is 2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1) smtp.mailfrom=lkml@antheas.dev smtp.helo=antheas-z13 Received-SPF: pass (linux3247.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: Mario.Limonciello@amd.com Cc: W_Armin@gmx.de, sashal@kernel.org, Shyam-Sundar.S-k@amd.com, derekjohn.clark@gmail.com, denis.benato@linux.dev, i@rong.moe, linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, Antheas Kapenekakis Subject: [RFC v4 2/4] platform/x86/amd: dptc: Add AMD DPTCi driver Date: Mon, 9 Mar 2026 21:51:23 +0100 Message-ID: <20260309205125.293148-3-lkml@antheas.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260309205125.293148-1-lkml@antheas.dev> References: <20260309205125.293148-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <177308955488.1090834.5985560364396960076@linux3247.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 1.4.3 at linux3247.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" Add a driver for AMD AGESA ALIB Function 0x0C, the Dynamic Power and Thermal Configuration interface (DPTCi). This exposes TDP and thermal parameters for AMD APU-based handheld devices via the firmware-attributes sysfs ABI. Parameters are staged and atomically committed through ALIB. The driver supports two save modes: "single" (apply immediately on write) and "bulk" (stage values, then commit with "save"). An "expanded_limits" toggle widens the allowed parameter ranges beyond device defaults. Initial device support: GPD Win 5 (AMD Ryzen AI MAX). Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Antheas Kapenekakis --- MAINTAINERS | 6 + drivers/platform/x86/amd/Kconfig | 14 + drivers/platform/x86/amd/Makefile | 2 + drivers/platform/x86/amd/dptc.c | 746 ++++++++++++++++++++++++++++++ 4 files changed, 768 insertions(+) create mode 100644 drivers/platform/x86/amd/dptc.c diff --git a/MAINTAINERS b/MAINTAINERS index 89007f9ed35e..ebda8e82bf35 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1103,6 +1103,12 @@ S: Supported F: drivers/gpu/drm/amd/display/dc/dml/ F: drivers/gpu/drm/amd/display/dc/dml2_0/ =20 +AMD DPTC DRIVER +M: Antheas Kapenekakis +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/amd/dptc.c + AMD FAM15H PROCESSOR POWER MONITORING DRIVER M: Huang Rui L: linux-hwmon@vger.kernel.org diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kc= onfig index b813f9265368..d610092467fc 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -44,3 +44,17 @@ config AMD_ISP_PLATFORM =20 This driver can also be built as a module. If so, the module will be called amd_isp4. + +config AMD_DPTC + tristate "AMD Dynamic Power and Thermal Configuration Interface (DPTCi)" + depends on X86_64 && ACPI && DMI + select FIRMWARE_ATTRIBUTES_CLASS + help + Driver for AMD AGESA ALIB Function 0x0C, the Dynamic Power and + Thermal Configuration Interface (DPTCi). Exposes TDP and thermal + parameters for AMD APU-based handheld devices via the + firmware-attributes sysfs ABI, allowing userspace tools to stage + and atomically commit power limit settings. Requires a DMI match + for the device and a recognized AMD SoC. + + If built as a module, the module will be called amd_dptc. diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/M= akefile index f6ff0c837f34..862a609bfe38 100644 --- a/drivers/platform/x86/amd/Makefile +++ b/drivers/platform/x86/amd/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_AMD_PMF) +=3D pmf/ obj-$(CONFIG_AMD_WBRF) +=3D wbrf.o obj-$(CONFIG_AMD_ISP_PLATFORM) +=3D amd_isp4.o obj-$(CONFIG_AMD_HFI) +=3D hfi/ +obj-$(CONFIG_AMD_DPTC) +=3D amd_dptc.o +amd_dptc-y :=3D dptc.o diff --git a/drivers/platform/x86/amd/dptc.c b/drivers/platform/x86/amd/dpt= c.c new file mode 100644 index 000000000000..b884cdfa3f82 --- /dev/null +++ b/drivers/platform/x86/amd/dptc.c @@ -0,0 +1,746 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AMD Dynamic Power and Thermal Configuration Interface (DPTCi) driver + * + * Exposes AMD APU power and thermal parameters via the firmware-attributes + * sysfs ABI. Parameters are staged and atomically committed through the + * AGESA ALIB Function 0x0C (Dynamic Power and Thermal Configuration + * interface). + * + * Reference: AMD AGESA Publication #44065, Appendix E.5 + * https://docs.amd.com/v/u/en-US/44065_Arch2008 + * + * Copyright (C) 2026 Antheas Kapenekakis + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../firmware_attributes_class.h" + +#define DRIVER_NAME "amd-dptc" + +#define ALIB_FUNC_DPTC 0x0C +#define ALIB_PATH "\\_SB.ALIB" + +#define ALIB_ID_TEMP_TARGET 0x03 +#define ALIB_ID_STAPM_LIMIT 0x05 +#define ALIB_ID_FAST_LIMIT 0x06 +#define ALIB_ID_SLOW_LIMIT 0x07 +#define ALIB_ID_SKIN_LIMIT 0x2E + +enum dptc_param_idx { + DPTC_PPT_PL1_SPL, /* STAPM + skin limit (set together) */ + DPTC_PPT_PL2_SPPT, /* slow PPT limit */ + DPTC_PPT_PL3_FPPT, /* fast PPT limit */ + DPTC_CPU_TEMP, /* thermal control target */ + DPTC_NUM_PARAMS, +}; + +struct dptc_param_limits { + u32 expanded_min; + u32 device_min; + u32 def; + u32 device_max; + u32 expanded_max; +}; + +struct dptc_device_limits { + struct dptc_param_limits params[DPTC_NUM_PARAMS]; +}; + +struct dptc_param_desc { + const char *name; + const char *display_name; + u16 scale; /* sysfs-to-ALIB multiplier (e.g. 1000 for W->mW) */ + u8 param_id; + u8 param_id2; /* secondary ALIB ID, 0 if none */ +}; + +static const struct dptc_param_desc dptc_params[DPTC_NUM_PARAMS] =3D { + [DPTC_PPT_PL1_SPL] =3D { "ppt_pl1_spl", "Sustained power limit (W)", + 1000, ALIB_ID_STAPM_LIMIT, + ALIB_ID_SKIN_LIMIT }, + [DPTC_PPT_PL2_SPPT] =3D { "ppt_pl2_sppt", "Slow PPT limit (W)", + 1000, ALIB_ID_SLOW_LIMIT }, + [DPTC_PPT_PL3_FPPT] =3D { "ppt_pl3_fppt", "Fast PPT limit (W)", + 1000, ALIB_ID_FAST_LIMIT }, + [DPTC_CPU_TEMP] =3D { "cpu_temp", "Thermal control limit (C)", + 1, ALIB_ID_TEMP_TARGET }, +}; + +/* AI MAX Handheld class: GPD Win 5 */ +static const struct dptc_device_limits limits_maxhh =3D { + .params =3D { + [DPTC_PPT_PL1_SPL] =3D { 1, 4, 25, 80, 100 }, + [DPTC_PPT_PL2_SPPT] =3D { 1, 4, 27, 82, 100 }, + [DPTC_PPT_PL3_FPPT] =3D { 1, 4, 40, 85, 100 }, + [DPTC_CPU_TEMP] =3D { 60, 70, 95, 95, 100 }, + }, +}; + +/* Substring matches against boot_cpu_data.x86_model_id; order matters. */ +static const char * const dptc_soc_table[] =3D { + /* AI MAX */ + "AMD RYZEN AI MAX+ 395", + "AMD RYZEN AI MAX+ 385", + "AMD RYZEN AI MAX 380", + NULL, +}; + +static const struct dmi_system_id dptc_dmi_table[] =3D { + /* GPD */ + { + .ident =3D "GPD Win 5", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1618-05"), + }, + .driver_data =3D (void *)&limits_maxhh, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, dptc_dmi_table); + +enum dptc_save_mode { SAVE_SINGLE, SAVE_BULK }; + +struct dptc_priv; + +struct dptc_attr_sysfs { + struct dptc_priv *priv; + struct kobj_attribute current_value; + struct kobj_attribute default_value; + struct kobj_attribute min_value; + struct kobj_attribute max_value; + struct kobj_attribute scalar_increment; + struct kobj_attribute display_name; + struct kobj_attribute type; + struct attribute *attrs[8]; + struct attribute_group group; + int idx; +}; + +struct dptc_priv { + struct device *fw_attr_dev; + struct kset *fw_attr_kset; + + const struct dptc_device_limits *dev_limits; + + bool expanded; + + enum dptc_save_mode save_mode; + + u32 staged[DPTC_NUM_PARAMS]; + + /* Protects staged, expanded, and save_mode */ + struct mutex lock; + + struct dptc_attr_sysfs params[DPTC_NUM_PARAMS]; + struct dptc_attr_sysfs expanded_attr; + struct kobj_attribute save_settings_attr; +}; + +static struct platform_device *dptc_pdev; + +static u32 dptc_get_min(struct dptc_priv *dptc, int idx) +{ + return dptc->expanded ? dptc->dev_limits->params[idx].expanded_min + : dptc->dev_limits->params[idx].device_min; +} + +static u32 dptc_get_max(struct dptc_priv *dptc, int idx) +{ + return dptc->expanded ? dptc->dev_limits->params[idx].expanded_max + : dptc->dev_limits->params[idx].device_max; +} + +static u32 dptc_get_default(struct dptc_priv *dptc, int idx) +{ + return dptc->dev_limits->params[idx].def; +} + +static int dptc_alib_call(const u8 *ids, const u32 *vals, int count) +{ + union acpi_object in_params[2]; + struct acpi_object_list input; + acpi_status status; + u32 buf_size; + int i, off; + u8 *buf; + + if (count =3D=3D 0) + return -ENOENT; + + /* Buffer layout: WORD total_size + count * (BYTE id + DWORD value) */ + buf_size =3D 2 + count * 5; + buf =3D kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + put_unaligned_le16(buf_size, buf); + + for (i =3D 0; i < count; i++) { + off =3D 2 + i * 5; + buf[off] =3D ids[i]; + put_unaligned_le32(vals[i], &buf[off + 1]); + } + + in_params[0].type =3D ACPI_TYPE_INTEGER; + in_params[0].integer.value =3D ALIB_FUNC_DPTC; + in_params[1].type =3D ACPI_TYPE_BUFFER; + in_params[1].buffer.length =3D buf_size; + in_params[1].buffer.pointer =3D buf; + + input.count =3D 2; + input.pointer =3D in_params; + + status =3D acpi_evaluate_object(NULL, ALIB_PATH, &input, NULL); + kfree(buf); + + if (ACPI_FAILURE(status)) { + pr_err("ALIB call failed: %s\n", + acpi_format_exception(status)); + return -EIO; + } + + pr_debug("sent %d ALIB parameter(s)\n", count); + return 0; +} + +static int dptc_alib_fill_param(u8 *ids, u32 *vals, int offset, + enum dptc_param_idx param, u32 val) +{ + u32 hw_val =3D val * dptc_params[param].scale; + + ids[offset] =3D dptc_params[param].param_id; + vals[offset++] =3D hw_val; + + if (dptc_params[param].param_id2) { + ids[offset] =3D dptc_params[param].param_id2; + vals[offset++] =3D hw_val; + } + + return offset; +} + +static int dptc_alib_send_one(int idx, u32 val) +{ + u32 vals[2]; + u8 ids[2]; + + return dptc_alib_call(ids, vals, + dptc_alib_fill_param(ids, vals, 0, idx, val)); +} + +static int dptc_alib_save(struct dptc_priv *dptc) +{ + u32 vals[DPTC_NUM_PARAMS * 2]; + u8 ids[DPTC_NUM_PARAMS * 2]; + int count =3D 0; + int i; + + for (i =3D 0; i < DPTC_NUM_PARAMS; i++) { + if (!dptc->staged[i]) + continue; + count =3D dptc_alib_fill_param(ids, vals, count, i, + dptc->staged[i]); + } + + return dptc_alib_call(ids, vals, count); +} + +/* Sysfs callbacks */ + +static ssize_t dptc_current_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, current_value); + struct dptc_priv *dptc =3D ps->priv; + + guard(mutex)(&dptc->lock); + + if (!dptc->staged[ps->idx]) + return sysfs_emit(buf, "\n"); + return sysfs_emit(buf, "%u\n", dptc->staged[ps->idx]); +} + +static ssize_t dptc_current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, current_value); + struct dptc_priv *dptc =3D ps->priv; + u32 val, min, max; + int ret; + + guard(mutex)(&dptc->lock); + + if (count =3D=3D 1 && buf[0] =3D=3D '\n') { + dptc->staged[ps->idx] =3D 0; + return count; + } + + ret =3D kstrtou32(buf, 10, &val); + if (ret) + return ret; + + min =3D dptc_get_min(dptc, ps->idx); + max =3D dptc_get_max(dptc, ps->idx); + if (val < min || (max && val > max)) + return -EINVAL; + dptc->staged[ps->idx] =3D val; + if (dptc->save_mode =3D=3D SAVE_SINGLE) + ret =3D dptc_alib_send_one(ps->idx, val); + + return ret ? ret : count; +} + +static ssize_t dptc_default_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, default_value); + + return sysfs_emit(buf, "%u\n", dptc_get_default(ps->priv, ps->idx)); +} + +static ssize_t dptc_min_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, min_value); + struct dptc_priv *dptc =3D ps->priv; + + guard(mutex)(&dptc->lock); + + return sysfs_emit(buf, "%u\n", dptc_get_min(dptc, ps->idx)); +} + +static ssize_t dptc_max_value_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, max_value); + struct dptc_priv *dptc =3D ps->priv; + + guard(mutex)(&dptc->lock); + + return sysfs_emit(buf, "%u\n", dptc_get_max(dptc, ps->idx)); +} + +static ssize_t dptc_scalar_increment_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "1\n"); +} + +static ssize_t dptc_display_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, display_name); + return sysfs_emit(buf, "%s\n", dptc_params[ps->idx].display_name); +} + +static ssize_t dptc_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "integer\n"); +} + +static ssize_t dptc_save_settings_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct dptc_priv *dptc =3D + container_of(attr, struct dptc_priv, save_settings_attr); + + guard(mutex)(&dptc->lock); + + if (dptc->save_mode =3D=3D SAVE_SINGLE) + return sysfs_emit(buf, "single\n"); + return sysfs_emit(buf, "bulk\n"); +} + +static ssize_t dptc_save_settings_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct dptc_priv *dptc =3D + container_of(attr, struct dptc_priv, save_settings_attr); + int ret =3D 0; + + guard(mutex)(&dptc->lock); + + if (sysfs_streq(buf, "save")) + ret =3D dptc_alib_save(dptc); + else if (sysfs_streq(buf, "single")) + dptc->save_mode =3D SAVE_SINGLE; + else if (sysfs_streq(buf, "bulk")) + dptc->save_mode =3D SAVE_BULK; + else + return -EINVAL; + + return ret ? ret : count; +} + +static ssize_t dptc_expanded_current_value_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, current_value); + struct dptc_priv *dptc =3D ps->priv; + + guard(mutex)(&dptc->lock); + + return sysfs_emit(buf, "%d\n", dptc->expanded); +} + +static ssize_t dptc_expanded_current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct dptc_attr_sysfs *ps =3D + container_of(attr, struct dptc_attr_sysfs, current_value); + struct dptc_priv *dptc =3D ps->priv; + bool val; + int ret; + + ret =3D kstrtobool(buf, &val); + if (ret) + return ret; + + guard(mutex)(&dptc->lock); + + dptc->expanded =3D val; + /* Clear staged values: limits changed, old values may be out of range */ + memset(dptc->staged, 0, sizeof(dptc->staged)); + + return count; +} + +static ssize_t dptc_expanded_default_value_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "0\n"); +} + +static ssize_t dptc_expanded_min_value_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "0\n"); +} + +static ssize_t dptc_expanded_max_value_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "1\n"); +} + +static ssize_t dptc_expanded_scalar_increment_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "1\n"); +} + +static ssize_t dptc_expanded_display_name_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "Expanded Limits\n"); +} + +static ssize_t dptc_expanded_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "integer\n"); +} + +/* Sysfs setup */ + +static void dptc_setup_param_sysfs(struct dptc_priv *dptc, + struct dptc_attr_sysfs *ps, int idx) +{ + ps->priv =3D dptc; + ps->idx =3D idx; + + sysfs_attr_init(&ps->current_value.attr); + ps->current_value.attr.name =3D "current_value"; + ps->current_value.attr.mode =3D 0644; + ps->current_value.show =3D dptc_current_value_show; + ps->current_value.store =3D dptc_current_value_store; + + sysfs_attr_init(&ps->default_value.attr); + ps->default_value.attr.name =3D "default_value"; + ps->default_value.attr.mode =3D 0444; + ps->default_value.show =3D dptc_default_value_show; + + sysfs_attr_init(&ps->min_value.attr); + ps->min_value.attr.name =3D "min_value"; + ps->min_value.attr.mode =3D 0444; + ps->min_value.show =3D dptc_min_value_show; + + sysfs_attr_init(&ps->max_value.attr); + ps->max_value.attr.name =3D "max_value"; + ps->max_value.attr.mode =3D 0444; + ps->max_value.show =3D dptc_max_value_show; + + sysfs_attr_init(&ps->scalar_increment.attr); + ps->scalar_increment.attr.name =3D "scalar_increment"; + ps->scalar_increment.attr.mode =3D 0444; + ps->scalar_increment.show =3D dptc_scalar_increment_show; + + sysfs_attr_init(&ps->display_name.attr); + ps->display_name.attr.name =3D "display_name"; + ps->display_name.attr.mode =3D 0444; + ps->display_name.show =3D dptc_display_name_show; + + sysfs_attr_init(&ps->type.attr); + ps->type.attr.name =3D "type"; + ps->type.attr.mode =3D 0444; + ps->type.show =3D dptc_type_show; + + ps->attrs[0] =3D &ps->current_value.attr; + ps->attrs[1] =3D &ps->default_value.attr; + ps->attrs[2] =3D &ps->min_value.attr; + ps->attrs[3] =3D &ps->max_value.attr; + ps->attrs[4] =3D &ps->scalar_increment.attr; + ps->attrs[5] =3D &ps->display_name.attr; + ps->attrs[6] =3D &ps->type.attr; + ps->attrs[7] =3D NULL; + + ps->group.name =3D dptc_params[idx].name; + ps->group.attrs =3D ps->attrs; +} + +static void dptc_setup_expanded_sysfs(struct dptc_priv *dptc, + struct dptc_attr_sysfs *ps) +{ + ps->priv =3D dptc; + sysfs_attr_init(&ps->current_value.attr); + ps->current_value.attr.name =3D "current_value"; + ps->current_value.attr.mode =3D 0644; + ps->current_value.show =3D dptc_expanded_current_value_show; + ps->current_value.store =3D dptc_expanded_current_value_store; + + sysfs_attr_init(&ps->default_value.attr); + ps->default_value.attr.name =3D "default_value"; + ps->default_value.attr.mode =3D 0444; + ps->default_value.show =3D dptc_expanded_default_value_show; + + sysfs_attr_init(&ps->min_value.attr); + ps->min_value.attr.name =3D "min_value"; + ps->min_value.attr.mode =3D 0444; + ps->min_value.show =3D dptc_expanded_min_value_show; + + sysfs_attr_init(&ps->max_value.attr); + ps->max_value.attr.name =3D "max_value"; + ps->max_value.attr.mode =3D 0444; + ps->max_value.show =3D dptc_expanded_max_value_show; + + sysfs_attr_init(&ps->scalar_increment.attr); + ps->scalar_increment.attr.name =3D "scalar_increment"; + ps->scalar_increment.attr.mode =3D 0444; + ps->scalar_increment.show =3D dptc_expanded_scalar_increment_show; + + sysfs_attr_init(&ps->display_name.attr); + ps->display_name.attr.name =3D "display_name"; + ps->display_name.attr.mode =3D 0444; + ps->display_name.show =3D dptc_expanded_display_name_show; + + sysfs_attr_init(&ps->type.attr); + ps->type.attr.name =3D "type"; + ps->type.attr.mode =3D 0444; + ps->type.show =3D dptc_expanded_type_show; + + ps->attrs[0] =3D &ps->current_value.attr; + ps->attrs[1] =3D &ps->default_value.attr; + ps->attrs[2] =3D &ps->min_value.attr; + ps->attrs[3] =3D &ps->max_value.attr; + ps->attrs[4] =3D &ps->scalar_increment.attr; + ps->attrs[5] =3D &ps->display_name.attr; + ps->attrs[6] =3D &ps->type.attr; + ps->attrs[7] =3D NULL; + + ps->group.name =3D "expanded_limits"; + ps->group.attrs =3D ps->attrs; +} + +static void dptc_fw_dev_unregister(void *data) +{ + device_unregister(data); +} + +static void dptc_kset_unregister(void *data) +{ + kset_unregister(data); +} + +static int dptc_resume(struct device *dev) +{ + struct dptc_priv *dptc =3D dev_get_drvdata(dev); + int ret; + + guard(mutex)(&dptc->lock); + + /* In bulk mode, do not use pm ops for userspace flexibility. */ + if (dptc->save_mode =3D=3D SAVE_SINGLE) + ret =3D dptc_alib_save(dptc); + else + ret =3D 0; + + if (ret && ret !=3D -ENOENT) + dev_warn(dev, "failed to restore settings on resume: %d\n", ret); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(dptc_pm_ops, NULL, dptc_resume); + +static int dptc_probe(struct platform_device *pdev) +{ + const struct dmi_system_id *dmi_match =3D dev_get_platdata(&pdev->dev); + struct device *dev =3D &pdev->dev; + struct dptc_priv *dptc; + int i, ret; + + dptc =3D devm_kzalloc(dev, sizeof(*dptc), GFP_KERNEL); + if (!dptc) + return -ENOMEM; + + platform_set_drvdata(pdev, dptc); + + ret =3D devm_mutex_init(dev, &dptc->lock); + if (ret) + return ret; + + dptc->dev_limits =3D dmi_match->driver_data; + dev_info(dev, "%s (%s)\n", dmi_match->ident, + boot_cpu_data.x86_model_id); + + dptc->fw_attr_dev =3D device_create(&firmware_attributes_class, + NULL, MKDEV(0, 0), NULL, DRIVER_NAME); + if (IS_ERR(dptc->fw_attr_dev)) + return PTR_ERR(dptc->fw_attr_dev); + + ret =3D devm_add_action_or_reset(dev, dptc_fw_dev_unregister, + dptc->fw_attr_dev); + if (ret) + return ret; + + dptc->fw_attr_kset =3D kset_create_and_add("attributes", NULL, + &dptc->fw_attr_dev->kobj); + if (!dptc->fw_attr_kset) + return -ENOMEM; + + ret =3D devm_add_action_or_reset(dev, dptc_kset_unregister, + dptc->fw_attr_kset); + if (ret) + return ret; + + for (i =3D 0; i < DPTC_NUM_PARAMS; i++) { + dptc_setup_param_sysfs(dptc, &dptc->params[i], i); + ret =3D sysfs_create_group(&dptc->fw_attr_kset->kobj, + &dptc->params[i].group); + if (ret) + return ret; + } + + dptc_setup_expanded_sysfs(dptc, &dptc->expanded_attr); + ret =3D sysfs_create_group(&dptc->fw_attr_kset->kobj, + &dptc->expanded_attr.group); + if (ret) + return ret; + + sysfs_attr_init(&dptc->save_settings_attr.attr); + dptc->save_settings_attr.attr.name =3D "save_settings"; + dptc->save_settings_attr.attr.mode =3D 0644; + dptc->save_settings_attr.show =3D dptc_save_settings_show; + dptc->save_settings_attr.store =3D dptc_save_settings_store; + ret =3D sysfs_create_file(&dptc->fw_attr_kset->kobj, + &dptc->save_settings_attr.attr); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver dptc_driver =3D { + .driver =3D { + .name =3D DRIVER_NAME, + .pm =3D pm_sleep_ptr(&dptc_pm_ops), + }, + .probe =3D dptc_probe, +}; + +static int __init dptc_init(void) +{ + const struct dmi_system_id *match; + bool soc_found =3D false; + int i, ret; + + match =3D dmi_first_match(dptc_dmi_table); + if (!match) + return -ENODEV; + + if (!acpi_has_method(NULL, ALIB_PATH)) { + pr_warn("ALIB method not present\n"); + return -ENODEV; + } + + for (i =3D 0; dptc_soc_table[i]; i++) { + if (strstr(boot_cpu_data.x86_model_id, + dptc_soc_table[i])) { + soc_found =3D true; + break; + } + } + if (!soc_found) { + pr_warn("unrecognized SoC '%s'\n", + boot_cpu_data.x86_model_id); + return -ENODEV; + } + + dptc_pdev =3D platform_device_register_data(NULL, DRIVER_NAME, -1, + match, sizeof(*match)); + if (IS_ERR(dptc_pdev)) + return PTR_ERR(dptc_pdev); + + ret =3D platform_driver_register(&dptc_driver); + if (ret) { + platform_device_unregister(dptc_pdev); + return ret; + } + + return 0; +} + +static void __exit dptc_exit(void) +{ + platform_driver_unregister(&dptc_driver); + platform_device_unregister(dptc_pdev); +} + +module_init(dptc_init); +module_exit(dptc_exit); + +MODULE_AUTHOR("Antheas Kapenekakis "); +MODULE_DESCRIPTION("AMD DPTCi ACPI Driver"); +MODULE_LICENSE("GPL"); --=20 2.52.0 From nobody Sun Apr 5 14:58:35 2026 Received: from relay12.grserver.gr (relay12.grserver.gr [88.99.38.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 58AB336BCC5; Mon, 9 Mar 2026 20:52:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=88.99.38.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089560; cv=none; b=QzReNA4963FCidqhTAVkLSu0oTHHMbuD0nWPxY1sYGHIoz5P9r4Y1glvJFvfCYlKsVo20VK8Ar72brVl5+M/9PWqgwGn4XZ4Jfq68eBLru0oIqsWS8KzOQgazwA9p3EJSvqQxTol+QFr66kc5+T2PE/VXJZOGeuo0o59KMAzjEo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089560; c=relaxed/simple; bh=x5HTQ7UO5QYRYigmkxOGJJE8LcxExe3Dl5SYbxuhSX4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dVdf8pnieZ25gJJj35Xujr4ILpMgcr54yF3DFr3vYW2NbBglmpy8gFSAsFVL/s4Ls3Z9c7EnGnXFzlRK+5FeJE04uu+vGoUewP7nJQz3jSJrAHYfgQ1qaDtvw65LIR0PP2Y6tWKjraZm4UJ7LSjlirfT7eJG4t+JVsso+KzeFBY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=FHqE9jOs; arc=none smtp.client-ip=88.99.38.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="FHqE9jOs" Received: from relay12 (localhost [127.0.0.1]) by relay12.grserver.gr (Proxmox) with ESMTP id EF86BBC067; Mon, 9 Mar 2026 22:52:36 +0200 (EET) Received: from linux3247.grserver.gr (linux3247.grserver.gr [213.158.90.240]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by relay12.grserver.gr (Proxmox) with ESMTPS id 30F60BC061; Mon, 9 Mar 2026 22:52:36 +0200 (EET) Received: from antheas-z13 (unknown [IPv6:2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1]) by linux3247.grserver.gr (Postfix) with ESMTPSA id 3D92D201CC0; Mon, 9 Mar 2026 22:52:35 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1773089555; bh=bBO/KAUufqMsifmJKJbxnvoskd7fXecqkhvYIRgP1Rc=; h=From:To:Subject; b=FHqE9jOsc7xYHZOqmqSps3/T6fiAeuR+AmbM4O+NSLCnOAZHjNVhssWkS/zeUn3xk Ykr5M+ScGrYhvggPzqRKAwqFWh1HbSwqUD9hyeoVHJu1XNsEMz4+QDsA211QDvq4NG uuGKguJwrOX11yIA59Ei+9cFZchrtLliNXoxNN1jbakchRTgZEwlj0PVwa3pzpQcdF V+ERvWJP4gVfBrin4sax0n6IbskPJflWmGjpofT0lZWC7CG6QjEaYoO/D4P1P2oUBr /xzw3J7erTXZnTcdcAF721g/ql/1g+1fQST2KAKC6e1bFxXi1GVg2jQ5An+f5V4hVg Vn/3NGlRgi+aA== Authentication-Results: linux3247.grserver.gr; spf=pass (sender IP is 2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1) smtp.mailfrom=lkml@antheas.dev smtp.helo=antheas-z13 Received-SPF: pass (linux3247.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: Mario.Limonciello@amd.com Cc: W_Armin@gmx.de, sashal@kernel.org, Shyam-Sundar.S-k@amd.com, derekjohn.clark@gmail.com, denis.benato@linux.dev, i@rong.moe, linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, Antheas Kapenekakis Subject: [RFC v4 3/4] platform/x86/amd: dptc: Add platform profile support Date: Mon, 9 Mar 2026 21:51:24 +0100 Message-ID: <20260309205125.293148-4-lkml@antheas.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260309205125.293148-1-lkml@antheas.dev> References: <20260309205125.293148-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <177308955584.1090913.16578991063375942967@linux3247.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 1.4.3 at linux3247.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" Register a platform_profile handler so the driver exposes standard power profiles (low-power, balanced, performance) alongside the manual tunable interface. When a non-custom profile is active, parameter writes are blocked (-EBUSY) and current_value reflects the profile's preset values. Selecting the "custom" profile returns control to the user for manual staging and committing. On resume, the active profile is automatically re-applied. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/amd/Kconfig | 1 + drivers/platform/x86/amd/dptc.c | 109 ++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kc= onfig index d610092467fc..41ffbd722524 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -48,6 +48,7 @@ config AMD_ISP_PLATFORM config AMD_DPTC tristate "AMD Dynamic Power and Thermal Configuration Interface (DPTCi)" depends on X86_64 && ACPI && DMI + select ACPI_PLATFORM_PROFILE select FIRMWARE_ATTRIBUTES_CLASS help Driver for AMD AGESA ALIB Function 0x0C, the Dynamic Power and diff --git a/drivers/platform/x86/amd/dptc.c b/drivers/platform/x86/amd/dpt= c.c index b884cdfa3f82..f4db95affb1b 100644 --- a/drivers/platform/x86/amd/dptc.c +++ b/drivers/platform/x86/amd/dptc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -56,8 +57,13 @@ struct dptc_param_limits { u32 expanded_max; }; =20 +struct dptc_profile { + u32 vals[DPTC_NUM_PARAMS]; /* 0 =3D don't set / unstage this param */ +}; + struct dptc_device_limits { struct dptc_param_limits params[DPTC_NUM_PARAMS]; + struct dptc_profile profiles[PLATFORM_PROFILE_LAST]; }; =20 struct dptc_param_desc { @@ -88,6 +94,11 @@ static const struct dptc_device_limits limits_maxhh =3D { [DPTC_PPT_PL3_FPPT] =3D { 1, 4, 40, 85, 100 }, [DPTC_CPU_TEMP] =3D { 60, 70, 95, 95, 100 }, }, + .profiles =3D { + [PLATFORM_PROFILE_LOW_POWER] =3D { .vals =3D { 15, 15, 25, 0 } }, + [PLATFORM_PROFILE_BALANCED] =3D { .vals =3D { 25, 27, 40, 0 } }, + [PLATFORM_PROFILE_PERFORMANCE] =3D { .vals =3D { 60, 63, 85, 0 } }, + }, }; =20 /* Substring matches against boot_cpu_data.x86_model_id; order matters. */ @@ -139,11 +150,14 @@ struct dptc_priv { =20 bool expanded; =20 + enum platform_profile_option profile; + struct device *ppdev; + enum dptc_save_mode save_mode; =20 u32 staged[DPTC_NUM_PARAMS]; =20 - /* Protects staged, expanded, and save_mode */ + /* Protects staged, expanded, save_mode, and profile */ struct mutex lock; =20 struct dptc_attr_sysfs params[DPTC_NUM_PARAMS]; @@ -271,6 +285,14 @@ static ssize_t dptc_current_value_show(struct kobject = *kobj, =20 guard(mutex)(&dptc->lock); =20 + if (dptc->profile !=3D PLATFORM_PROFILE_CUSTOM) { + u32 val =3D dptc->dev_limits->profiles[dptc->profile].vals[ps->idx]; + + if (!val) + return sysfs_emit(buf, "\n"); + return sysfs_emit(buf, "%u\n", val); + } + if (!dptc->staged[ps->idx]) return sysfs_emit(buf, "\n"); return sysfs_emit(buf, "%u\n", dptc->staged[ps->idx]); @@ -288,6 +310,9 @@ static ssize_t dptc_current_value_store(struct kobject = *kobj, =20 guard(mutex)(&dptc->lock); =20 + if (dptc->profile !=3D PLATFORM_PROFILE_CUSTOM) + return -EBUSY; + if (count =3D=3D 1 && buf[0] =3D=3D '\n') { dptc->staged[ps->idx] =3D 0; return count; @@ -425,6 +450,9 @@ static ssize_t dptc_expanded_current_value_store(struct= kobject *kobj, =20 guard(mutex)(&dptc->lock); =20 + if (dptc->profile !=3D PLATFORM_PROFILE_CUSTOM) + return -EBUSY; + dptc->expanded =3D val; /* Clear staged values: limits changed, old values may be out of range */ memset(dptc->staged, 0, sizeof(dptc->staged)); @@ -593,6 +621,75 @@ static void dptc_kset_unregister(void *data) kset_unregister(data); } =20 +/* Platform profile */ + +static int dptc_apply_profile(struct dptc_priv *dptc, + enum platform_profile_option profile) +{ + const struct dptc_profile *pp; + int i; + + memset(dptc->staged, 0, sizeof(dptc->staged)); + + if (profile =3D=3D PLATFORM_PROFILE_CUSTOM) + return 0; + + pp =3D &dptc->dev_limits->profiles[profile]; + for (i =3D 0; i < DPTC_NUM_PARAMS; i++) { + if (!pp->vals[i]) + continue; + dptc->staged[i] =3D pp->vals[i]; + } + + return dptc_alib_save(dptc); +} + +static int dptc_pp_probe(void *drvdata, unsigned long *choices) +{ + struct dptc_priv *dptc =3D drvdata; + int i, j; + + set_bit(PLATFORM_PROFILE_CUSTOM, choices); + for (i =3D 0; i < PLATFORM_PROFILE_LAST; i++) { + for (j =3D 0; j < DPTC_NUM_PARAMS; j++) { + if (dptc->dev_limits->profiles[i].vals[j]) { + set_bit(i, choices); + break; + } + } + } + return 0; +} + +static int dptc_pp_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct dptc_priv *dptc =3D dev_get_drvdata(dev); + + guard(mutex)(&dptc->lock); + + *profile =3D dptc->profile; + return 0; +} + +static int dptc_pp_set(struct device *dev, + enum platform_profile_option profile) +{ + struct dptc_priv *dptc =3D dev_get_drvdata(dev); + + guard(mutex)(&dptc->lock); + + dptc->profile =3D profile; + + return dptc_apply_profile(dptc, profile); +} + +static const struct platform_profile_ops dptc_pp_ops =3D { + .probe =3D dptc_pp_probe, + .profile_get =3D dptc_pp_get, + .profile_set =3D dptc_pp_set, +}; + static int dptc_resume(struct device *dev) { struct dptc_priv *dptc =3D dev_get_drvdata(dev); @@ -601,7 +698,9 @@ static int dptc_resume(struct device *dev) guard(mutex)(&dptc->lock); =20 /* In bulk mode, do not use pm ops for userspace flexibility. */ - if (dptc->save_mode =3D=3D SAVE_SINGLE) + if (dptc->profile !=3D PLATFORM_PROFILE_CUSTOM) + ret =3D dptc_apply_profile(dptc, dptc->profile); + else if (dptc->save_mode =3D=3D SAVE_SINGLE) ret =3D dptc_alib_save(dptc); else ret =3D 0; @@ -679,6 +778,12 @@ static int dptc_probe(struct platform_device *pdev) if (ret) return ret; =20 + dptc->profile =3D PLATFORM_PROFILE_CUSTOM; + dptc->ppdev =3D devm_platform_profile_register(dev, "amd-dptc", dptc, + &dptc_pp_ops); + if (IS_ERR(dptc->ppdev)) + return PTR_ERR(dptc->ppdev); + return 0; } =20 --=20 2.52.0 From nobody Sun Apr 5 14:58:35 2026 Received: from relay14.grserver.gr (relay14.grserver.gr [157.180.73.62]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53F2D36D518; Mon, 9 Mar 2026 20:52:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=157.180.73.62 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089562; cv=none; b=o+NvkMgXDUutXGGHBOvmKSIhMRBL99lwc8Rmp+Jv+XAKCjqEx0PDGWRoDfox4xLWNexQlhJ6FmjUNJUz0hfb/QpB3ISQQW/tWLDmehf6gru0ZUJhuaf194oZ8meYw7/Jn2DE/WSwQi7xsdARHz5gM74RmTlviox+ebG3WsQncms= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773089562; c=relaxed/simple; bh=Gr+PB21Hc5E58lQOP3fAT+Mw2A1uL8Do9S0KFbeprRo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LBckf20p8YeOoh0O+3BvpmJGRKwCnUGfUv+DntczhdnlRv4bW91/nA2vOrOCz3UPLTjdC6TWklMdggOfikMpTqZS6cdFtcHYNjjINMvptwdI8PjeONhSpGy9x/vbwQ8GTCEX5YMJgQ6kIeGDI1hWsLLYR440XrugABcU91SuH4o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=GPY6DS11; arc=none smtp.client-ip=157.180.73.62 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="GPY6DS11" Received: from relay14 (localhost [127.0.0.1]) by relay14.grserver.gr (Proxmox) with ESMTP id 64D4A407FA; Mon, 9 Mar 2026 20:52:38 +0000 (UTC) Received: from linux3247.grserver.gr (linux3247.grserver.gr [213.158.90.240]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by relay14.grserver.gr (Proxmox) with ESMTPS id 3AD42408F0; Mon, 9 Mar 2026 20:52:37 +0000 (UTC) Received: from antheas-z13 (unknown [IPv6:2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1]) by linux3247.grserver.gr (Postfix) with ESMTPSA id 2A518201CBF; Mon, 9 Mar 2026 22:52:36 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1773089556; bh=h7bMbUUi7ojolGBNGc2ERcNw4ar8lurqF44AqwyazKo=; h=From:To:Subject; b=GPY6DS11he8MtmeugT9MErgbD0DGhEKa2hCK64rXsmySoMOdIKFi6lYHACCOXrfBR 5srhQnNQdrVdYAjpJmYAIYgIP6n6/KpZhaNmtX5saL8ZfO8jAv9bGxPtWj4HrG7zRX bYOxl1Y1ni85S6vtGHUul8thBYx5Ibe82dF1x7NLFJjHBgmFBUnCxN3oAHtWtkRjiV TUmFJCxp6jzj5M55fGOjN/UD6e9IZKG1Qeyy0BU05n6y+x+wAhnnX/3o1dHhShcYMp iDV/vMEYHKqFLcxMiB8EKpYjcUgv7+129Fk3pti12Axx5PIa07tr/Oty7SKYX2qHfb XTbguHcvEZ/iQ== Authentication-Results: linux3247.grserver.gr; spf=pass (sender IP is 2a05:f6c5:43c3:0:378a:d3f6:f8b0:bed1) smtp.mailfrom=lkml@antheas.dev smtp.helo=antheas-z13 Received-SPF: pass (linux3247.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: Mario.Limonciello@amd.com Cc: W_Armin@gmx.de, sashal@kernel.org, Shyam-Sundar.S-k@amd.com, derekjohn.clark@gmail.com, denis.benato@linux.dev, i@rong.moe, linux-kernel@vger.kernel.org, platform-driver-x86@vger.kernel.org, Antheas Kapenekakis Subject: [RFC v4 4/4] platform/x86/amd: dptc: Add device entries for handheld PCs Date: Mon, 9 Mar 2026 21:51:25 +0100 Message-ID: <20260309205125.293148-5-lkml@antheas.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260309205125.293148-1-lkml@antheas.dev> References: <20260309205125.293148-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <177308955670.1090999.14348522395993720316@linux3247.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 1.4.3 at linux3247.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" Add limit tables and DMI entries for AMD APU-based handheld devices: - 15W class: AYANEO AIR - 18W class: AYANEO AIR Plus/Pro - 25W class: AYANEO AIR 1S, NEXT/KUN - 28W class: GPD Win Mini/4/Max 2, GPD Duo, GPD Pocket 4, AYANEO 2, OrangePi NEO-01, SuiPlay0X1 - 30W class: AYANEO FLIP/GEEK/SLIDE/3, AOKZOE A1/A1X/A2, OneXPlayer F1Pro/2/X1/G1, AYN Loki Max, Zeenix Pro Also add SoC strings for Ryzen AI, Ryzen 8000/7040/6000/5000 families, extending support beyond the initial AI MAX series. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/amd/dptc.c | 420 ++++++++++++++++++++++++++++++++ 1 file changed, 420 insertions(+) diff --git a/drivers/platform/x86/amd/dptc.c b/drivers/platform/x86/amd/dpt= c.c index f4db95affb1b..b2820f8652f1 100644 --- a/drivers/platform/x86/amd/dptc.c +++ b/drivers/platform/x86/amd/dptc.c @@ -86,6 +86,81 @@ static const struct dptc_param_desc dptc_params[DPTC_NUM= _PARAMS] =3D { 1, ALIB_ID_TEMP_TARGET }, }; =20 +/* 15W class: AYANEO AIR (Ryzen 5 5560U) */ +static const struct dptc_device_limits limits_15w =3D { + .params =3D { + [DPTC_PPT_PL1_SPL] =3D { 1, 5, 10, 15, 18 }, + [DPTC_PPT_PL2_SPPT] =3D { 1, 5, 10, 15, 18 }, + [DPTC_PPT_PL3_FPPT] =3D { 1, 5, 12, 18, 22 }, + [DPTC_CPU_TEMP] =3D { 60, 70, 85, 90, 100 }, + }, + .profiles =3D { + [PLATFORM_PROFILE_LOW_POWER] =3D { .vals =3D { 5, 5, 8, 0 } }, + [PLATFORM_PROFILE_BALANCED] =3D { .vals =3D { 10, 12, 15, 0 } }, + [PLATFORM_PROFILE_PERFORMANCE] =3D { .vals =3D { 15, 15, 18, 0 } }, + }, +}; + +/* 18W class: AYANEO AIR Plus/Pro (Ryzen 5 5560U, Ryzen 7 5825U) */ +static const struct dptc_device_limits limits_18w =3D { + .params =3D { + [DPTC_PPT_PL1_SPL] =3D { 1, 5, 15, 18, 22 }, + [DPTC_PPT_PL2_SPPT] =3D { 1, 5, 15, 18, 22 }, + [DPTC_PPT_PL3_FPPT] =3D { 1, 5, 15, 20, 25 }, + [DPTC_CPU_TEMP] =3D { 60, 70, 85, 90, 100 }, + }, + .profiles =3D { + [PLATFORM_PROFILE_LOW_POWER] =3D { .vals =3D { 5, 5, 8, 0 } }, + [PLATFORM_PROFILE_BALANCED] =3D { .vals =3D { 12, 14, 15, 0 } }, + [PLATFORM_PROFILE_PERFORMANCE] =3D { .vals =3D { 18, 18, 20, 0 } }, + }, +}; + +/* 25W class: Ryzen 5000 handhelds (AYANEO NEXT, KUN) */ +static const struct dptc_device_limits limits_25w =3D { + .params =3D { + [DPTC_PPT_PL1_SPL] =3D { 1, 4, 15, 25, 32 }, + [DPTC_PPT_PL2_SPPT] =3D { 1, 4, 20, 27, 35 }, + [DPTC_PPT_PL3_FPPT] =3D { 1, 4, 25, 30, 37 }, + [DPTC_CPU_TEMP] =3D { 60, 70, 85, 90, 100 }, + }, + .profiles =3D { + [PLATFORM_PROFILE_LOW_POWER] =3D { .vals =3D { 8, 8, 12, 0 } }, + [PLATFORM_PROFILE_BALANCED] =3D { .vals =3D { 15, 17, 20, 0 } }, + [PLATFORM_PROFILE_PERFORMANCE] =3D { .vals =3D { 25, 27, 30, 0 } }, + }, +}; + +/* 28W class: GPD Win series, AYANEO 2, OrangePi NEO-01 */ +static const struct dptc_device_limits limits_28w =3D { + .params =3D { + [DPTC_PPT_PL1_SPL] =3D { 1, 4, 15, 28, 32 }, + [DPTC_PPT_PL2_SPPT] =3D { 1, 4, 20, 30, 35 }, + [DPTC_PPT_PL3_FPPT] =3D { 1, 4, 25, 32, 37 }, + [DPTC_CPU_TEMP] =3D { 60, 70, 85, 90, 100 }, + }, + .profiles =3D { + [PLATFORM_PROFILE_LOW_POWER] =3D { .vals =3D { 8, 8, 12, 0 } }, + [PLATFORM_PROFILE_BALANCED] =3D { .vals =3D { 15, 17, 22, 0 } }, + [PLATFORM_PROFILE_PERFORMANCE] =3D { .vals =3D { 25, 28, 32, 0 } }, + }, +}; + +/* 30W class: OneXPlayer, AYANEO FLIP/GEEK/SLIDE/3, AOKZOE */ +static const struct dptc_device_limits limits_30w =3D { + .params =3D { + [DPTC_PPT_PL1_SPL] =3D { 1, 4, 15, 30, 40 }, + [DPTC_PPT_PL2_SPPT] =3D { 1, 4, 20, 32, 43 }, + [DPTC_PPT_PL3_FPPT] =3D { 1, 4, 25, 41, 50 }, + [DPTC_CPU_TEMP] =3D { 60, 70, 85, 90, 100 }, + }, + .profiles =3D { + [PLATFORM_PROFILE_LOW_POWER] =3D { .vals =3D { 8, 8, 12, 0 } }, + [PLATFORM_PROFILE_BALANCED] =3D { .vals =3D { 15, 17, 25, 0 } }, + [PLATFORM_PROFILE_PERFORMANCE] =3D { .vals =3D { 25, 28, 41, 0 } }, + }, +}; + /* AI MAX Handheld class: GPD Win 5 */ static const struct dptc_device_limits limits_maxhh =3D { .params =3D { @@ -107,11 +182,56 @@ static const char * const dptc_soc_table[] =3D { "AMD RYZEN AI MAX+ 395", "AMD RYZEN AI MAX+ 385", "AMD RYZEN AI MAX 380", + /* Ryzen AI */ + "AMD Ryzen AI 9 HX 370", + /* Ryzen 8000 */ + "AMD Ryzen 7 8840U", + /* Ryzen 7040 */ + "AMD Ryzen 7 7840U", + /* Ryzen 6000 */ + "AMD Ryzen 7 6800U", + "AMD Ryzen 7 6600U", + /* Ryzen 5000 */ + "AMD Ryzen 7 5800U", + "AMD Ryzen 7 5700U", + "AMD Ryzen 5 5560U", NULL, }; =20 static const struct dmi_system_id dptc_dmi_table[] =3D { /* GPD */ + { + .ident =3D "GPD Win Mini", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Win Mini 2024", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Win Mini 2024", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1617-02-L"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Win 4", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"), + }, + .driver_data =3D (void *)&limits_28w, + }, { .ident =3D "GPD Win 5", .matches =3D { @@ -120,6 +240,306 @@ static const struct dmi_system_id dptc_dmi_table[] = =3D { }, .driver_data =3D (void *)&limits_maxhh, }, + { + .ident =3D "GPD Win Max 2", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Win Max 2 2024", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1619-05"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Duo", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Duo", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1622-01-L"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Pocket 4", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "GPD Pocket 4", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1628-04-L"), + }, + .driver_data =3D (void *)&limits_28w, + }, + /* OrangePi */ + { + .ident =3D "OrangePi NEO-01", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"), + }, + .driver_data =3D (void *)&limits_28w, + }, + /* AYN */ + { + .ident =3D "AYN Loki Max", + .matches =3D { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ayn"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Loki Max"), + }, + .driver_data =3D (void *)&limits_30w, + }, + /* Tectoy (Zeenix Pro =3D Loki Max) */ + { + .ident =3D "Zeenix Pro", + .matches =3D { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Tectoy"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Zeenix Pro"), + }, + .driver_data =3D (void *)&limits_30w, + }, + /* AOKZOE */ + { + .ident =3D "AOKZOE A1 AR07", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "AOKZOE A1 Pro", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "AOKZOE A1X", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1X"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "AOKZOE A2 Pro", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A2 Pro"), + }, + .driver_data =3D (void *)&limits_30w, + }, + /* OneXPlayer (Intel variants filtered by SoC table) */ + { + .ident =3D "ONEXPLAYER F1Pro", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1Pro"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "ONEXPLAYER F1 EVA-02", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-02"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "ONEXPLAYER 2", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "ONEXPLAYER X1 A", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 A"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "ONEXPLAYER X1z", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1z"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "ONEXPLAYER X1Pro", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Pro"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "ONEXPLAYER G1 A", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"), + }, + .driver_data =3D (void *)&limits_30w, + }, + /* AYANEO - 15W */ + { + .ident =3D "AYANEO AIR", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"), + }, + .driver_data =3D (void *)&limits_15w, + }, + /* AYANEO - 18W */ + { + .ident =3D "AYANEO AIR Plus", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Plus"), + }, + .driver_data =3D (void *)&limits_18w, + }, + { + .ident =3D "AYANEO AIR Pro", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"), + }, + .driver_data =3D (void *)&limits_18w, + }, + /* AYANEO - 25W */ + { + .ident =3D "AYANEO AIR 1S", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_MATCH(DMI_BOARD_NAME, "AIR 1S"), + }, + .driver_data =3D (void *)&limits_25w, + }, + { + .ident =3D "AYANEO NEXT Advance", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEXT Advance"), + }, + .driver_data =3D (void *)&limits_25w, + }, + { + .ident =3D "AYANEO NEXT Lite", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEXT Lite"), + }, + .driver_data =3D (void *)&limits_25w, + }, + { + .ident =3D "AYANEO NEXT Pro", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEXT Pro"), + }, + .driver_data =3D (void *)&limits_25w, + }, + { + .ident =3D "AYANEO NEXT", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEXT"), + }, + .driver_data =3D (void *)&limits_25w, + }, + { + .ident =3D "AYANEO KUN", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"), + }, + .driver_data =3D (void *)&limits_25w, + }, + { + .ident =3D "AYANEO KUN", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO KUN"), + }, + .driver_data =3D (void *)&limits_25w, + }, + /* AYANEO - 28W */ + { + .ident =3D "AYANEO 2", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"), + }, + .driver_data =3D (void *)&limits_28w, + }, + { + .ident =3D "SuiPlay0X1", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "Mysten Labs, Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "SuiPlay0X1"), + }, + .driver_data =3D (void *)&limits_28w, + }, + /* AYANEO - 30W */ + { + /* DMI_MATCH catches all FLIP variants (DS, KB, 1S DS, 1S KB) */ + .ident =3D "AYANEO FLIP", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_MATCH(DMI_BOARD_NAME, "FLIP"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + /* DMI_MATCH catches GEEK and GEEK 1S */ + .ident =3D "AYANEO GEEK", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_MATCH(DMI_BOARD_NAME, "GEEK"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "AYANEO SLIDE", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SLIDE"), + }, + .driver_data =3D (void *)&limits_30w, + }, + { + .ident =3D "AYANEO 3", + .matches =3D { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"), + }, + .driver_data =3D (void *)&limits_30w, + }, { } }; MODULE_DEVICE_TABLE(dmi, dptc_dmi_table); --=20 2.52.0