From: Mario Limonciello <mario.limonciello@xxxxxxx> A number of users resort to using reverse engineered software like ryzenadj to manipulate debugging interfaces for modifying APU settings. At a glance these tools are useful, but the problem is they break state machines in other software such as the PMF driver or the OEM EC. Offer a knob for PMF to allow 'manual control' which will users can directly change things like fPPT and sPPT. As this can be harmful for a system to try to push limits outside of a thermal design, taint the kernel and show a critical message when in use. Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx> --- Documentation/ABI/testing/sysfs-amd-pmf | 10 +++ drivers/platform/x86/amd/pmf/Makefile | 1 + drivers/platform/x86/amd/pmf/core.c | 9 +++ drivers/platform/x86/amd/pmf/manual.c | 88 +++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/pmf.h | 5 ++ drivers/platform/x86/amd/pmf/sps.c | 4 ++ 6 files changed, 117 insertions(+) create mode 100644 drivers/platform/x86/amd/pmf/manual.c diff --git a/Documentation/ABI/testing/sysfs-amd-pmf b/Documentation/ABI/testing/sysfs-amd-pmf index 7fc0e1c2b76b..6f3d5cbf443f 100644 --- a/Documentation/ABI/testing/sysfs-amd-pmf +++ b/Documentation/ABI/testing/sysfs-amd-pmf @@ -11,3 +11,13 @@ Description: Reading this file tells if the AMD Platform Management(PMF) To turn off CnQF user can write "off" to the sysfs node. Note: Systems that support auto mode will not have this sysfs file available. + +What: /sys/devices/platform/*/{spl, fppt, sppt, sppt_apu_only, stt_min, stt_limit_apu, stt_skip_temp} +Date: December 2024 +Contact: Mario Limonciello <mario.limonciello@xxxxxxx> +Description: Manual control of AMD PMF APU coefficients + . + These files are used to manually control the APU coefficients. + In order to write to these files the module most be + loaded with manual_control=1 and the user must write "custom" + to the ACPI platform profile. diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index 7d6079b02589..81444d6f4428 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_AMD_PMF) += amd-pmf.o amd-pmf-objs := core.o acpi.o sps.o \ auto-mode.o cnqf.o \ + manual.o \ tee-if.o spc.o pmf-quirks.o diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index d6af0ca036f1..52a68ca094be 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -53,6 +53,10 @@ static bool force_load; module_param(force_load, bool, 0444); MODULE_PARM_DESC(force_load, "Force load this driver on supported older platforms (experimental)"); +bool pmf_manual_control; +module_param_named(manual_control, pmf_manual_control, bool, 0444); +MODULE_PARM_DESC(manual_control, "Expose manual control knobs (experimental)"); + static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data) { struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier); @@ -349,6 +353,10 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n"); } + if (pmf_manual_control) { + amd_pmf_init_manual_control(dev); + return; + } amd_pmf_init_smart_pc(dev); if (dev->smart_pc_enabled) { dev_dbg(dev->dev, "Smart PC Solution Enabled\n"); @@ -485,6 +493,7 @@ static void amd_pmf_remove(struct platform_device *pdev) static const struct attribute_group *amd_pmf_driver_groups[] = { &cnqf_feature_attribute_group, + &manual_attribute_group, NULL, }; diff --git a/drivers/platform/x86/amd/pmf/manual.c b/drivers/platform/x86/amd/pmf/manual.c new file mode 100644 index 000000000000..b33fc3cd8d61 --- /dev/null +++ b/drivers/platform/x86/amd/pmf/manual.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD Platform Management Framework Driver + * + * Copyright (c) 2024, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Mario Limonciello <mario.limonciello@xxxxxxx> + */ + +#include "pmf.h" + +#define pmf_manual_attribute(_name, _set_command, _get_command) \ +static ssize_t _name##_store(struct device *d, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct amd_pmf_dev *dev = dev_get_drvdata(d); \ + uint val; \ + \ + if (dev->current_profile != PLATFORM_PROFILE_CUSTOM) { \ + dev_warn_once(dev->dev, \ + "Manual control is disabled, please set " \ + "platform profile to custom.\n"); \ + return -EINVAL; \ + } \ + \ + if (kstrtouint(buf, 10, &val) < 0) \ + return -EINVAL; \ + \ + amd_pmf_send_cmd(dev, _set_command, false, val, NULL); \ + \ + return count; \ +} \ +static ssize_t _name##_show(struct device *d, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct amd_pmf_dev *dev = dev_get_drvdata(d); \ + uint val; \ + \ + amd_pmf_send_cmd(dev, _get_command, true, ARG_NONE, &val); \ + \ + return sysfs_emit(buf, "%u\n", val); \ +} + +pmf_manual_attribute(spl, SET_SPL, GET_SPL); +static DEVICE_ATTR_RW(spl); +pmf_manual_attribute(fppt, SET_FPPT, GET_FPPT); +static DEVICE_ATTR_RW(fppt); +pmf_manual_attribute(sppt, SET_SPPT, GET_SPPT); +static DEVICE_ATTR_RW(sppt); +pmf_manual_attribute(sppt_apu_only, SET_SPPT_APU_ONLY, GET_SPPT_APU_ONLY); +static DEVICE_ATTR_RW(sppt_apu_only); +pmf_manual_attribute(stt_min, SET_STT_MIN_LIMIT, GET_STT_MIN_LIMIT); +static DEVICE_ATTR_RW(stt_min); +pmf_manual_attribute(stt_limit_apu, SET_STT_LIMIT_APU, GET_STT_LIMIT_APU); +static DEVICE_ATTR_RW(stt_limit_apu); +pmf_manual_attribute(stt_skin_temp, SET_STT_LIMIT_HS2, GET_STT_LIMIT_HS2); +static DEVICE_ATTR_RW(stt_skin_temp); + +static umode_t manual_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + return pmf_manual_control ? 0660 : 0; +} + +static struct attribute *manual_attrs[] = { + &dev_attr_spl.attr, + &dev_attr_fppt.attr, + &dev_attr_sppt.attr, + &dev_attr_sppt_apu_only.attr, + &dev_attr_stt_min.attr, + &dev_attr_stt_limit_apu.attr, + &dev_attr_stt_skin_temp.attr, + NULL, +}; + +const struct attribute_group manual_attribute_group = { + .attrs = manual_attrs, + .is_visible = manual_attr_is_visible, +}; + +void amd_pmf_init_manual_control(struct amd_pmf_dev *dev) +{ + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); + pr_crit("Manual PMF control is enabled, please disable it before " + "reporting any bugs unrelated to PMF.\n"); +} diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 8ce8816da9c1..ca3df63cf190 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -798,4 +798,9 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table * /* Quirk infrastructure */ void amd_pmf_quirks_init(struct amd_pmf_dev *dev); +/* Manual configuration */ +extern bool pmf_manual_control; +extern const struct attribute_group manual_attribute_group; +void amd_pmf_init_manual_control(struct amd_pmf_dev *dev); + #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index 92f7fb22277d..6db88e523a86 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -305,6 +305,8 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf) case PLATFORM_PROFILE_LOW_POWER: mode = POWER_MODE_POWER_SAVER; break; + case PLATFORM_PROFILE_CUSTOM: + return 0; default: dev_err(pmf->dev, "Unknown Platform Profile.\n"); return -EOPNOTSUPP; @@ -412,6 +414,8 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev) set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices); set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices); set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices); + if (pmf_manual_control) + set_bit(PLATFORM_PROFILE_CUSTOM, dev->pprof.choices); /* Create platform_profile structure and register */ err = platform_profile_register(&dev->pprof); -- 2.43.0