Skip to content

Commit 0d50f4d

Browse files
spandruvadarafaeljw
authored andcommitted
thermal: intel: int340x: Add support for power slider
Add support for system wide energy performance preference using a SoC slider interface defined via processor thermal PCI device MMIO space. Using Linux platform-profile class API, register a new platform profile. Provide three platform power profile choices: "performance", "balanced" and "low-power". Profile sysfs is located at: /sys/class/platform-profile/platform-profile-* where attribute "name" is presented as "SoC Power Slider". At boot by default the slider is set to balanced mode. This profile is changed by user space based on user preference via power profile daemon or directly writing to the "profile" sysfs attribute. Add a CPU model specific processor thermal device feature PROC_THERMAL_FEATURE_SOC_POWER_SLIDER. When enabled for a CPU model, slider interface is registered. During system suspend callback save slider register and restore during resume callback. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Link: https://patch.msgid.link/20250825132315.75521-2-srinivas.pandruvada@linux.intel.com [ rjw: Removal of redundant outer parens from one expression ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 3d9ab61 commit 0d50f4d

5 files changed

Lines changed: 222 additions & 0 deletions

File tree

drivers/thermal/intel/int340x_thermal/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ config INT340X_THERMAL
1212
select ACPI_THERMAL_LIB
1313
select INTEL_SOC_DTS_IOSF_CORE
1414
select INTEL_TCC
15+
select ACPI_PLATFORM_PROFILE
1516
select PROC_THERMAL_MMIO_RAPL if POWERCAP
1617
help
1718
Newer laptops and tablets that use ACPI may have thermal sensors and

drivers/thermal/intel/int340x_thermal/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o
1414
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o
1515
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o
1616
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_power_floor.o
17+
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_soc_slider.o
1718
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
1819
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

drivers/thermal/intel/int340x_thermal/processor_thermal_device.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,17 @@ static int tcc_offset_save = -1;
338338

339339
int proc_thermal_suspend(struct device *dev)
340340
{
341+
struct proc_thermal_device *proc_dev;
342+
341343
tcc_offset_save = intel_tcc_get_offset(-1);
342344
if (tcc_offset_save < 0)
343345
dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save);
344346

347+
proc_dev = dev_get_drvdata(dev);
348+
349+
if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER)
350+
proc_thermal_soc_power_slider_suspend(proc_dev);
351+
345352
return 0;
346353
}
347354
EXPORT_SYMBOL_GPL(proc_thermal_suspend);
@@ -357,6 +364,9 @@ int proc_thermal_resume(struct device *dev)
357364
if (tcc_offset_save >= 0)
358365
intel_tcc_set_offset(-1, tcc_offset_save);
359366

367+
if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER)
368+
proc_thermal_soc_power_slider_resume(proc_dev);
369+
360370
return 0;
361371
}
362372
EXPORT_SYMBOL_GPL(proc_thermal_resume);
@@ -432,8 +442,18 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
432442
}
433443
}
434444

445+
if (feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) {
446+
ret = proc_thermal_soc_power_slider_add(pdev, proc_priv);
447+
if (ret) {
448+
dev_info(&pdev->dev, "failed to add soc power efficiency slider\n");
449+
goto err_rem_wlt;
450+
}
451+
}
452+
435453
return 0;
436454

455+
err_rem_wlt:
456+
proc_thermal_wt_hint_remove(pdev);
437457
err_rem_rfim:
438458
proc_thermal_rfim_remove(pdev);
439459
err_rem_ptc:

drivers/thermal/intel/int340x_thermal/processor_thermal_device.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ struct rapl_mmio_regs {
6969
#define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40
7070
#define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80
7171
#define PROC_THERMAL_FEATURE_PTC 0x100
72+
#define PROC_THERMAL_FEATURE_SOC_POWER_SLIDER 0x200
7273

7374
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
7475
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,
127128
void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
128129
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
129130
void proc_thermal_ptc_remove(struct pci_dev *pdev);
131+
132+
int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
133+
void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv);
134+
void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv);
135+
130136
#endif
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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+
37+
enum 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+
50+
static 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+
/* Convert from platform power profile option to SoC slider value */
57+
static int convert_profile_to_power_slider(enum platform_profile_option profile)
58+
{
59+
switch (profile) {
60+
case PLATFORM_PROFILE_LOW_POWER:
61+
return slider_values[SOC_POWER_SLIDER_POWERSAVE];
62+
case PLATFORM_PROFILE_BALANCED:
63+
return slider_values[SOC_POWER_SLIDER_BALANCE];
64+
case PLATFORM_PROFILE_PERFORMANCE:
65+
return slider_values[SOC_POWER_SLIDER_PERFORMANCE];
66+
default:
67+
break;
68+
}
69+
70+
return -EOPNOTSUPP;
71+
}
72+
73+
/* Convert to platform power profile option from SoC slider values */
74+
static int convert_power_slider_to_profile(u8 slider)
75+
{
76+
if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE])
77+
return PLATFORM_PROFILE_PERFORMANCE;
78+
if (slider == slider_values[SOC_POWER_SLIDER_BALANCE])
79+
return PLATFORM_PROFILE_BALANCED;
80+
if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE])
81+
return PLATFORM_PROFILE_LOW_POWER;
82+
83+
return -EOPNOTSUPP;
84+
}
85+
86+
static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv)
87+
{
88+
return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET);
89+
}
90+
91+
static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val)
92+
{
93+
writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET);
94+
}
95+
96+
static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider)
97+
{
98+
u64 val;
99+
100+
val = read_soc_slider(proc_priv);
101+
val &= ~SLIDER_MASK;
102+
val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT);
103+
write_soc_slider(proc_priv, val);
104+
}
105+
106+
/* profile get/set callbacks are called with a profile lock, so no need for local locks */
107+
108+
static int power_slider_platform_profile_set(struct device *dev,
109+
enum platform_profile_option profile)
110+
{
111+
struct proc_thermal_device *proc_priv;
112+
int slider;
113+
114+
proc_priv = dev_get_drvdata(dev);
115+
if (!proc_priv)
116+
return -EOPNOTSUPP;
117+
118+
slider = convert_profile_to_power_slider(profile);
119+
if (slider < 0)
120+
return slider;
121+
122+
set_soc_power_profile(proc_priv, slider);
123+
124+
return 0;
125+
}
126+
127+
static int power_slider_platform_profile_get(struct device *dev,
128+
enum platform_profile_option *profile)
129+
{
130+
struct proc_thermal_device *proc_priv;
131+
int slider, ret;
132+
u64 val;
133+
134+
proc_priv = dev_get_drvdata(dev);
135+
if (!proc_priv)
136+
return -EOPNOTSUPP;
137+
138+
val = read_soc_slider(proc_priv);
139+
slider = FIELD_GET(SLIDER_MASK, val);
140+
141+
ret = convert_power_slider_to_profile(slider);
142+
if (ret < 0)
143+
return ret;
144+
145+
*profile = ret;
146+
147+
return 0;
148+
}
149+
150+
static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices)
151+
{
152+
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
153+
set_bit(PLATFORM_PROFILE_BALANCED, choices);
154+
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
155+
156+
return 0;
157+
}
158+
159+
static const struct platform_profile_ops power_slider_platform_profile_ops = {
160+
.probe = power_slider_platform_profile_probe,
161+
.profile_get = power_slider_platform_profile_get,
162+
.profile_set = power_slider_platform_profile_set,
163+
};
164+
165+
int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
166+
{
167+
struct device *ppdev;
168+
169+
set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]);
170+
171+
ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv,
172+
&power_slider_platform_profile_ops);
173+
174+
return PTR_ERR_OR_ZERO(ppdev);
175+
}
176+
EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL");
177+
178+
static u64 soc_slider_save;
179+
180+
void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv)
181+
{
182+
soc_slider_save = read_soc_slider(proc_priv);
183+
}
184+
EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL");
185+
186+
void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv)
187+
{
188+
write_soc_slider(proc_priv, soc_slider_save);
189+
}
190+
EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL");
191+
192+
MODULE_IMPORT_NS("INT340X_THERMAL");
193+
MODULE_LICENSE("GPL");
194+
MODULE_DESCRIPTION("Processor Thermal Power Slider Interface");

0 commit comments

Comments
 (0)