FILE
processor_thermal_soc_slider.c
File preview with syntax highlighting.
linux-6.19.14/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Processor Thermal Device Interface for Reading and Writing 4 * SoC Power Slider Values from User Space. 5 * 6 * Operation: 7 * The SOC_EFFICIENCY_SLIDER_0_0_0_MCHBAR register is accessed 8 * using the MMIO (Memory-Mapped I/O) interface with an MMIO offset of 0x5B38. 9 * Although this register is 64 bits wide, only bits 7:0 are used, 10 * and the other bits remain unchanged. 11 * 12 * Bit definitions 13 * 14 * Bits 2:0 (Slider value): 15 * The SoC optimizer slider value indicates the system wide energy performance 16 * hint. The slider has no specific units and ranges from 0 (highest 17 * performance) to 6 (highest energy efficiency). Value of 7 is reserved. 18 * Bits 3 : Reserved 19 * Bits 6:4 (Offset) 20 * Offset allows the SoC to automatically switch slider position in range 21 * [slider value (bits 2:0) + offset] to improve power efficiency based on 22 * internal SoC algorithms. 23 * Bit 7 (Enable): 24 * If this bit is set, the SoC Optimization sliders will be processed by the 25 * SoC firmware. 26 * 27 * Copyright (c) 2025, Intel Corporation. 28 */ 29 30#include <linux/bitfield.h> 31#include <linux/pci.h> 32#include <linux/platform_profile.h> 33#include "processor_thermal_device.h" 34 35#define SOC_POWER_SLIDER_OFFSET 0x5B38 36 37enum power_slider_preference { 38 SOC_POWER_SLIDER_PERFORMANCE, 39 SOC_POWER_SLIDER_BALANCE, 40 SOC_POWER_SLIDER_POWERSAVE, 41}; 42 43#define SOC_SLIDER_VALUE_MINIMUM 0x00 44#define SOC_SLIDER_VALUE_BALANCE 0x03 45#define SOC_SLIDER_VALUE_MAXIMUM 0x06 46 47#define SLIDER_MASK GENMASK_ULL(2, 0) 48#define SLIDER_ENABLE_BIT 7 49 50static u8 slider_values[] = { 51 [SOC_POWER_SLIDER_PERFORMANCE] = SOC_SLIDER_VALUE_MINIMUM, 52 [SOC_POWER_SLIDER_BALANCE] = SOC_SLIDER_VALUE_BALANCE, 53 [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM, 54}; 55 56/* Lock to protect module param updates */ 57static DEFINE_MUTEX(slider_param_lock); 58 59static int slider_balanced_param = SOC_SLIDER_VALUE_BALANCE; 60 61static int slider_def_balance_set(const char *arg, const struct kernel_param *kp) 62{ 63 u8 slider_val; 64 int ret; 65 66 guard(mutex)(&slider_param_lock); 67 68 ret = kstrtou8(arg, 16, &slider_val); 69 if (!ret) { 70 if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] || 71 slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE]) 72 return -EINVAL; 73 74 slider_balanced_param = slider_val; 75 } 76 77 return ret; 78} 79 80static int slider_def_balance_get(char *buf, const struct kernel_param *kp) 81{ 82 guard(mutex)(&slider_param_lock); 83 return sysfs_emit(buf, "%02x\n", slider_values[SOC_POWER_SLIDER_BALANCE]); 84} 85 86static const struct kernel_param_ops slider_def_balance_ops = { 87 .set = slider_def_balance_set, 88 .get = slider_def_balance_get, 89}; 90 91module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644); 92MODULE_PARM_DESC(slider_balance, "Set slider default value for balance"); 93 94static u8 slider_offset; 95 96static int slider_def_offset_set(const char *arg, const struct kernel_param *kp) 97{ 98 u8 offset; 99 int ret; 100 101 guard(mutex)(&slider_param_lock); 102 103 ret = kstrtou8(arg, 16, &offset); 104 if (!ret) { 105 if (offset > SOC_SLIDER_VALUE_MAXIMUM) 106 return -EINVAL; 107 108 slider_offset = offset; 109 } 110 111 return ret; 112} 113 114static int slider_def_offset_get(char *buf, const struct kernel_param *kp) 115{ 116 guard(mutex)(&slider_param_lock); 117 return sysfs_emit(buf, "%02x\n", slider_offset); 118} 119 120static const struct kernel_param_ops slider_offset_ops = { 121 .set = slider_def_offset_set, 122 .get = slider_def_offset_get, 123}; 124 125/* 126 * To enhance power efficiency dynamically, the firmware can optionally 127 * auto-adjust the slider value based on the current workload. This 128 * adjustment is controlled by the "slider_offset" module parameter. 129 * This offset permits the firmware to increase the slider value 130 * up to and including "SoC slider + slider offset,". 131 */ 132module_param_cb(slider_offset, &slider_offset_ops, NULL, 0644); 133MODULE_PARM_DESC(slider_offset, "Set slider offset"); 134 135/* Convert from platform power profile option to SoC slider value */ 136static int convert_profile_to_power_slider(enum platform_profile_option profile) 137{ 138 switch (profile) { 139 case PLATFORM_PROFILE_LOW_POWER: 140 return slider_values[SOC_POWER_SLIDER_POWERSAVE]; 141 case PLATFORM_PROFILE_BALANCED: 142 return slider_values[SOC_POWER_SLIDER_BALANCE]; 143 case PLATFORM_PROFILE_PERFORMANCE: 144 return slider_values[SOC_POWER_SLIDER_PERFORMANCE]; 145 default: 146 break; 147 } 148 149 return -EOPNOTSUPP; 150} 151 152/* Convert to platform power profile option from SoC slider values */ 153static int convert_power_slider_to_profile(u8 slider) 154{ 155 if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE]) 156 return PLATFORM_PROFILE_PERFORMANCE; 157 if (slider == slider_values[SOC_POWER_SLIDER_BALANCE]) 158 return PLATFORM_PROFILE_BALANCED; 159 if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE]) 160 return PLATFORM_PROFILE_LOW_POWER; 161 162 return -EOPNOTSUPP; 163} 164 165static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv) 166{ 167 return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); 168} 169 170static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val) 171{ 172 writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); 173} 174 175#define SLIDER_OFFSET_MASK GENMASK_ULL(6, 4) 176 177static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider) 178{ 179 u8 offset; 180 u64 val; 181 182 val = read_soc_slider(proc_priv); 183 val &= ~SLIDER_MASK; 184 val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT); 185 186 if (slider == SOC_SLIDER_VALUE_MINIMUM || slider == SOC_SLIDER_VALUE_MAXIMUM) 187 offset = 0; 188 else 189 offset = slider_offset; 190 191 /* Set the slider offset from module params */ 192 val &= ~SLIDER_OFFSET_MASK; 193 val |= FIELD_PREP(SLIDER_OFFSET_MASK, offset); 194 195 write_soc_slider(proc_priv, val); 196} 197 198/* profile get/set callbacks are called with a profile lock, so no need for local locks */ 199 200static int power_slider_platform_profile_set(struct device *dev, 201 enum platform_profile_option profile) 202{ 203 struct proc_thermal_device *proc_priv; 204 int slider; 205 206 proc_priv = dev_get_drvdata(dev); 207 if (!proc_priv) 208 return -EOPNOTSUPP; 209 210 guard(mutex)(&slider_param_lock); 211 212 slider_values[SOC_POWER_SLIDER_BALANCE] = slider_balanced_param; 213 214 slider = convert_profile_to_power_slider(profile); 215 if (slider < 0) 216 return slider; 217 218 set_soc_power_profile(proc_priv, slider); 219 220 return 0; 221} 222 223static int power_slider_platform_profile_get(struct device *dev, 224 enum platform_profile_option *profile) 225{ 226 struct proc_thermal_device *proc_priv; 227 int slider, ret; 228 u64 val; 229 230 proc_priv = dev_get_drvdata(dev); 231 if (!proc_priv) 232 return -EOPNOTSUPP; 233 234 val = read_soc_slider(proc_priv); 235 slider = FIELD_GET(SLIDER_MASK, val); 236 237 ret = convert_power_slider_to_profile(slider); 238 if (ret < 0) 239 return ret; 240 241 *profile = ret; 242 243 return 0; 244} 245 246static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices) 247{ 248 set_bit(PLATFORM_PROFILE_LOW_POWER, choices); 249 set_bit(PLATFORM_PROFILE_BALANCED, choices); 250 set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); 251 252 return 0; 253} 254 255static const struct platform_profile_ops power_slider_platform_profile_ops = { 256 .probe = power_slider_platform_profile_probe, 257 .profile_get = power_slider_platform_profile_get, 258 .profile_set = power_slider_platform_profile_set, 259}; 260 261int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) 262{ 263 struct device *ppdev; 264 265 set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]); 266 267 ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv, 268 &power_slider_platform_profile_ops); 269 270 return PTR_ERR_OR_ZERO(ppdev); 271} 272EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL"); 273 274static u64 soc_slider_save; 275 276void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv) 277{ 278 soc_slider_save = read_soc_slider(proc_priv); 279} 280EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL"); 281 282void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv) 283{ 284 write_soc_slider(proc_priv, soc_slider_save); 285} 286EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL"); 287 288MODULE_IMPORT_NS("INT340X_THERMAL"); 289MODULE_LICENSE("GPL"); 290MODULE_DESCRIPTION("Processor Thermal Power Slider Interface");
~/
Read-only file preview.