diff options
Diffstat (limited to 'drivers/thermal/intel')
7 files changed, 314 insertions, 4 deletions
diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig index 4c699f0896b5..4ced7bdcd62c 100644 --- a/drivers/thermal/intel/int340x_thermal/Kconfig +++ b/drivers/thermal/intel/int340x_thermal/Kconfig @@ -12,6 +12,7 @@ config INT340X_THERMAL select ACPI_THERMAL_LIB select INTEL_SOC_DTS_IOSF_CORE select INTEL_TCC + select ACPI_PLATFORM_PROFILE select PROC_THERMAL_MMIO_RAPL if POWERCAP help Newer laptops and tablets that use ACPI may have thermal sensors and diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile index 184318d1792b..436be34b21a9 100644 --- a/drivers/thermal/intel/int340x_thermal/Makefile +++ b/drivers/thermal/intel/int340x_thermal/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_power_floor.o +obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_soc_slider.o obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index cb149bcdd7d5..ce5d53be108b 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -220,9 +220,6 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps int i, result = 0; struct psvt *psvts; - if (!acpi_has_method(handle, "PSVT")) - return -ENODEV; - status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer); if (ACPI_FAILURE(status)) return -ENODEV; diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c index 29fcece48cad..48e7849d4816 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c @@ -338,10 +338,17 @@ static int tcc_offset_save = -1; int proc_thermal_suspend(struct device *dev) { + struct proc_thermal_device *proc_dev; + tcc_offset_save = intel_tcc_get_offset(-1); if (tcc_offset_save < 0) dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save); + proc_dev = dev_get_drvdata(dev); + + if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) + proc_thermal_soc_power_slider_suspend(proc_dev); + return 0; } EXPORT_SYMBOL_GPL(proc_thermal_suspend); @@ -357,6 +364,9 @@ int proc_thermal_resume(struct device *dev) if (tcc_offset_save >= 0) intel_tcc_set_offset(-1, tcc_offset_save); + if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) + proc_thermal_soc_power_slider_resume(proc_dev); + return 0; } EXPORT_SYMBOL_GPL(proc_thermal_resume); @@ -432,8 +442,18 @@ int proc_thermal_mmio_add(struct pci_dev *pdev, } } + if (feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) { + ret = proc_thermal_soc_power_slider_add(pdev, proc_priv); + if (ret) { + dev_info(&pdev->dev, "failed to add soc power efficiency slider\n"); + goto err_rem_wlt; + } + } + return 0; +err_rem_wlt: + proc_thermal_wt_hint_remove(pdev); err_rem_rfim: proc_thermal_rfim_remove(pdev); err_rem_ptc: diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h index 49398794124a..30760475102f 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h @@ -69,6 +69,7 @@ struct rapl_mmio_regs { #define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40 #define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80 #define PROC_THERMAL_FEATURE_PTC 0x100 +#define PROC_THERMAL_FEATURE_SOC_POWER_SLIDER 0x200 #if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL) int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); @@ -127,4 +128,9 @@ int proc_thermal_mmio_add(struct pci_dev *pdev, void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); void proc_thermal_ptc_remove(struct pci_dev *pdev); + +int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); +void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv); +void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv); + #endif diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c index d4d7e8e147d2..e2471768d355 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c @@ -498,7 +498,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT | - PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) }, + PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC | + PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) }, { PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT | diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c new file mode 100644 index 000000000000..49ff3bae7271 --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Processor Thermal Device Interface for Reading and Writing + * SoC Power Slider Values from User Space. + * + * Operation: + * The SOC_EFFICIENCY_SLIDER_0_0_0_MCHBAR register is accessed + * using the MMIO (Memory-Mapped I/O) interface with an MMIO offset of 0x5B38. + * Although this register is 64 bits wide, only bits 7:0 are used, + * and the other bits remain unchanged. + * + * Bit definitions + * + * Bits 2:0 (Slider value): + * The SoC optimizer slider value indicates the system wide energy performance + * hint. The slider has no specific units and ranges from 0 (highest + * performance) to 6 (highest energy efficiency). Value of 7 is reserved. + * Bits 3 : Reserved + * Bits 6:4 (Offset) + * Offset allows the SoC to automatically switch slider position in range + * [slider value (bits 2:0) + offset] to improve power efficiency based on + * internal SoC algorithms. + * Bit 7 (Enable): + * If this bit is set, the SoC Optimization sliders will be processed by the + * SoC firmware. + * + * Copyright (c) 2025, Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/pci.h> +#include <linux/platform_profile.h> +#include "processor_thermal_device.h" + +#define SOC_POWER_SLIDER_OFFSET 0x5B38 + +enum power_slider_preference { + SOC_POWER_SLIDER_PERFORMANCE, + SOC_POWER_SLIDER_BALANCE, + SOC_POWER_SLIDER_POWERSAVE, +}; + +#define SOC_SLIDER_VALUE_MINIMUM 0x00 +#define SOC_SLIDER_VALUE_BALANCE 0x03 +#define SOC_SLIDER_VALUE_MAXIMUM 0x06 + +#define SLIDER_MASK GENMASK_ULL(2, 0) +#define SLIDER_ENABLE_BIT 7 + +static u8 slider_values[] = { + [SOC_POWER_SLIDER_PERFORMANCE] = SOC_SLIDER_VALUE_MINIMUM, + [SOC_POWER_SLIDER_BALANCE] = SOC_SLIDER_VALUE_BALANCE, + [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM, +}; + +/* Lock to protect module param updates */ +static DEFINE_MUTEX(slider_param_lock); + +static int slider_balanced_param = SOC_SLIDER_VALUE_BALANCE; + +static int slider_def_balance_set(const char *arg, const struct kernel_param *kp) +{ + u8 slider_val; + int ret; + + guard(mutex)(&slider_param_lock); + + ret = kstrtou8(arg, 16, &slider_val); + if (!ret) { + if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] || + slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE]) + return -EINVAL; + + slider_balanced_param = slider_val; + } + + return ret; +} + +static int slider_def_balance_get(char *buf, const struct kernel_param *kp) +{ + guard(mutex)(&slider_param_lock); + return sysfs_emit(buf, "%02x\n", slider_values[SOC_POWER_SLIDER_BALANCE]); +} + +static const struct kernel_param_ops slider_def_balance_ops = { + .set = slider_def_balance_set, + .get = slider_def_balance_get, +}; + +module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644); +MODULE_PARM_DESC(slider_balance, "Set slider default value for balance"); + +static u8 slider_offset; + +static int slider_def_offset_set(const char *arg, const struct kernel_param *kp) +{ + u8 offset; + int ret; + + guard(mutex)(&slider_param_lock); + + ret = kstrtou8(arg, 16, &offset); + if (!ret) { + if (offset > SOC_SLIDER_VALUE_MAXIMUM) + return -EINVAL; + + slider_offset = offset; + } + + return ret; +} + +static int slider_def_offset_get(char *buf, const struct kernel_param *kp) +{ + guard(mutex)(&slider_param_lock); + return sysfs_emit(buf, "%02x\n", slider_offset); +} + +static const struct kernel_param_ops slider_offset_ops = { + .set = slider_def_offset_set, + .get = slider_def_offset_get, +}; + +/* + * To enhance power efficiency dynamically, the firmware can optionally + * auto-adjust the slider value based on the current workload. This + * adjustment is controlled by the "slider_offset" module parameter. + * This offset permits the firmware to increase the slider value + * up to and including "SoC slider + slider offset,". + */ +module_param_cb(slider_offset, &slider_offset_ops, NULL, 0644); +MODULE_PARM_DESC(slider_offset, "Set slider offset"); + +/* Convert from platform power profile option to SoC slider value */ +static int convert_profile_to_power_slider(enum platform_profile_option profile) +{ + switch (profile) { + case PLATFORM_PROFILE_LOW_POWER: + return slider_values[SOC_POWER_SLIDER_POWERSAVE]; + case PLATFORM_PROFILE_BALANCED: + return slider_values[SOC_POWER_SLIDER_BALANCE]; + case PLATFORM_PROFILE_PERFORMANCE: + return slider_values[SOC_POWER_SLIDER_PERFORMANCE]; + default: + break; + } + + return -EOPNOTSUPP; +} + +/* Convert to platform power profile option from SoC slider values */ +static int convert_power_slider_to_profile(u8 slider) +{ + if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE]) + return PLATFORM_PROFILE_PERFORMANCE; + if (slider == slider_values[SOC_POWER_SLIDER_BALANCE]) + return PLATFORM_PROFILE_BALANCED; + if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE]) + return PLATFORM_PROFILE_LOW_POWER; + + return -EOPNOTSUPP; +} + +static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv) +{ + return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); +} + +static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val) +{ + writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); +} + +#define SLIDER_OFFSET_MASK GENMASK_ULL(6, 4) + +static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider) +{ + u64 val; + + val = read_soc_slider(proc_priv); + val &= ~SLIDER_MASK; + val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT); + + /* Set the slider offset from module params */ + val &= ~SLIDER_OFFSET_MASK; + val |= FIELD_PREP(SLIDER_OFFSET_MASK, slider_offset); + + write_soc_slider(proc_priv, val); +} + +/* profile get/set callbacks are called with a profile lock, so no need for local locks */ + +static int power_slider_platform_profile_set(struct device *dev, + enum platform_profile_option profile) +{ + struct proc_thermal_device *proc_priv; + int slider; + + proc_priv = dev_get_drvdata(dev); + if (!proc_priv) + return -EOPNOTSUPP; + + guard(mutex)(&slider_param_lock); + + slider_values[SOC_POWER_SLIDER_BALANCE] = slider_balanced_param; + + slider = convert_profile_to_power_slider(profile); + if (slider < 0) + return slider; + + set_soc_power_profile(proc_priv, slider); + + return 0; +} + +static int power_slider_platform_profile_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct proc_thermal_device *proc_priv; + int slider, ret; + u64 val; + + proc_priv = dev_get_drvdata(dev); + if (!proc_priv) + return -EOPNOTSUPP; + + val = read_soc_slider(proc_priv); + slider = FIELD_GET(SLIDER_MASK, val); + + ret = convert_power_slider_to_profile(slider); + if (ret < 0) + return ret; + + *profile = ret; + + return 0; +} + +static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops power_slider_platform_profile_ops = { + .probe = power_slider_platform_profile_probe, + .profile_get = power_slider_platform_profile_get, + .profile_set = power_slider_platform_profile_set, +}; + +int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) +{ + struct device *ppdev; + + set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]); + + ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv, + &power_slider_platform_profile_ops); + + return PTR_ERR_OR_ZERO(ppdev); +} +EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL"); + +static u64 soc_slider_save; + +void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv) +{ + soc_slider_save = read_soc_slider(proc_priv); +} +EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL"); + +void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv) +{ + write_soc_slider(proc_priv, soc_slider_save); +} +EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL"); + +MODULE_IMPORT_NS("INT340X_THERMAL"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Processor Thermal Power Slider Interface"); |