File Index
Files and directories
Sign in
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 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.