Skip to content

Commit 2a8aadb

Browse files
committed
Merge branch 'thermal-intel'
Merge an update of the Intel int340x thermal driver adding Platform Temperature Control (PTC) support to it (Srinivas Pandruvada). * thermal-intel: thermal: int340x: processor_thermal: Platform temperature control documentation thermal: intel: int340x: Enable platform temperature control thermal: intel: int340x: Add platform temperature control interface
2 parents 3f7cd28 + fdccdb6 commit 2a8aadb

File tree

6 files changed

+285
-3
lines changed

6 files changed

+285
-3
lines changed

Documentation/driver-api/thermal/intel_dptf.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,27 @@ ABI.
191191
User space can specify any one of the available workload type using
192192
this interface.
193193

194+
:file:`/sys/bus/pci/devices/0000\:00\:04.0/ptc_0_control`
195+
:file:`/sys/bus/pci/devices/0000\:00\:04.0/ptc_1_control`
196+
:file:`/sys/bus/pci/devices/0000\:00\:04.0/ptc_2_control`
197+
198+
All these controls needs admin privilege to update.
199+
200+
``enable`` (RW)
201+
1 for enable, 0 for disable. Shows the current enable status of
202+
platform temperature control feature. User space can enable/disable
203+
hardware controls.
204+
205+
``temperature_target`` (RW)
206+
Update a new temperature target in milli degree celsius for hardware to
207+
use for the temperature control.
208+
209+
Given that this is platform temperature control, it is expected that a
210+
single user-level manager owns and manages the controls. If multiple
211+
user-level software applications attempt to write different targets, it
212+
can lead to unexpected behavior.
213+
214+
194215
DPTF Processor thermal RFIM interface
195216
--------------------------------------------
196217

drivers/thermal/intel/int340x_thermal/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci_legacy.o
99
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci.o
1010
obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o
1111
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_rfim.o
12+
obj-$(CONFIG_INT340X_THERMAL) += platform_temperature_control.o
1213
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o
1314
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o
1415
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* processor thermal device platform temperature controls
4+
* Copyright (c) 2025, Intel Corporation.
5+
*/
6+
7+
/*
8+
* Platform temperature controls hardware interface
9+
*
10+
* The hardware control interface is via MMIO offsets in the processor
11+
* thermal device MMIO space. There are three instances of MMIO registers.
12+
* All registers are 64 bit wide with RW access.
13+
*
14+
* Name: PLATFORM_TEMPERATURE_CONTROL
15+
* Offsets: 0x5B20, 0x5B28, 0x5B30
16+
*
17+
* Bits Description
18+
* 7:0 TARGET_TEMP : Target temperature limit to which the control
19+
* mechanism is regulating. Units: 0.5C.
20+
* 8:8 ENABLE: Read current enable status of the feature or enable
21+
* feature.
22+
* 11:9 GAIN: Sets the aggressiveness of control loop from 0 to 7
23+
* 7 graceful, favors performance at the expense of temperature
24+
* overshoots
25+
* 0 aggressive, favors tight regulation over performance
26+
* 12:12 TEMPERATURE_OVERRIDE_EN
27+
* When set, hardware will use TEMPERATURE_OVERRIDE values instead
28+
* of reading from corresponding sensor.
29+
* 15:13 RESERVED
30+
* 23:16 MIN_PERFORMANCE_LEVEL: Minimum Performance level below which the
31+
* there will be no throttling. 0 - all levels of throttling allowed
32+
* including survivability actions. 255 - no throttling allowed.
33+
* 31:24 TEMPERATURE_OVERRIDE: Allows SW to override the input temperature.
34+
* hardware will use this value instead of the sensor temperature.
35+
* Units: 0.5C.
36+
* 63:32 RESERVED
37+
*/
38+
39+
#include <linux/kernel.h>
40+
#include <linux/module.h>
41+
#include <linux/pci.h>
42+
#include "processor_thermal_device.h"
43+
44+
struct mmio_reg {
45+
int bits;
46+
u16 mask;
47+
u16 shift;
48+
u16 units;
49+
};
50+
51+
#define MAX_ATTR_GROUP_NAME_LEN 32
52+
#define PTC_MAX_ATTRS 3
53+
54+
struct ptc_data {
55+
u32 offset;
56+
struct attribute_group ptc_attr_group;
57+
struct attribute *ptc_attrs[PTC_MAX_ATTRS];
58+
struct device_attribute temperature_target_attr;
59+
struct device_attribute enable_attr;
60+
char group_name[MAX_ATTR_GROUP_NAME_LEN];
61+
};
62+
63+
static const struct mmio_reg ptc_mmio_regs[] = {
64+
{ 8, 0xFF, 0, 500}, /* temperature_target, units 0.5C*/
65+
{ 1, 0x01, 8, 0}, /* enable */
66+
{ 3, 0x7, 9, 0}, /* gain */
67+
{ 8, 0xFF, 16, 0}, /* min_performance_level */
68+
{ 1, 0x1, 12, 0}, /* temperature_override_enable */
69+
{ 8, 0xFF, 24, 500}, /* temperature_override, units 0.5C */
70+
};
71+
72+
#define PTC_MAX_INSTANCES 3
73+
74+
/* Unique offset for each PTC instance */
75+
static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30};
76+
77+
/* These will represent sysfs attribute names */
78+
static const char * const ptc_strings[] = {
79+
"temperature_target",
80+
"enable",
81+
NULL
82+
};
83+
84+
/* Lock to protect concurrent read/write and read-modify-write */
85+
static DEFINE_MUTEX(ptc_lock);
86+
87+
static ssize_t ptc_mmio_show(struct ptc_data *data, struct device *dev,
88+
struct device_attribute *attr, char *buf)
89+
{
90+
struct pci_dev *pdev = to_pci_dev(dev);
91+
struct proc_thermal_device *proc_priv;
92+
const struct mmio_reg *mmio_regs;
93+
int ret, units;
94+
u64 reg_val;
95+
96+
proc_priv = pci_get_drvdata(pdev);
97+
mmio_regs = ptc_mmio_regs;
98+
ret = match_string(ptc_strings, -1, attr->attr.name);
99+
if (ret < 0)
100+
return ret;
101+
102+
units = mmio_regs[ret].units;
103+
104+
guard(mutex)(&ptc_lock);
105+
106+
reg_val = readq((void __iomem *) (proc_priv->mmio_base + data->offset));
107+
ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;
108+
if (units)
109+
ret *= units;
110+
111+
return sysfs_emit(buf, "%d\n", ret);
112+
}
113+
114+
#define PTC_SHOW(suffix)\
115+
static ssize_t suffix##_show(struct device *dev,\
116+
struct device_attribute *attr,\
117+
char *buf)\
118+
{\
119+
struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
120+
return ptc_mmio_show(data, dev, attr, buf);\
121+
}
122+
123+
static void ptc_mmio_write(struct pci_dev *pdev, u32 offset, int index, u32 value)
124+
{
125+
struct proc_thermal_device *proc_priv;
126+
u64 mask, reg_val;
127+
128+
proc_priv = pci_get_drvdata(pdev);
129+
130+
mask = GENMASK_ULL(ptc_mmio_regs[index].shift + ptc_mmio_regs[index].bits - 1,
131+
ptc_mmio_regs[index].shift);
132+
133+
guard(mutex)(&ptc_lock);
134+
135+
reg_val = readq((void __iomem *) (proc_priv->mmio_base + offset));
136+
reg_val &= ~mask;
137+
reg_val |= (value << ptc_mmio_regs[index].shift);
138+
writeq(reg_val, (void __iomem *) (proc_priv->mmio_base + offset));
139+
}
140+
141+
static int ptc_store(struct ptc_data *data, struct device *dev, struct device_attribute *attr,
142+
const char *buf, size_t count)
143+
{
144+
struct pci_dev *pdev = to_pci_dev(dev);
145+
unsigned int input;
146+
int ret;
147+
148+
ret = kstrtouint(buf, 10, &input);
149+
if (ret)
150+
return ret;
151+
152+
ret = match_string(ptc_strings, -1, attr->attr.name);
153+
if (ret < 0)
154+
return ret;
155+
156+
if (ptc_mmio_regs[ret].units)
157+
input /= ptc_mmio_regs[ret].units;
158+
159+
if (input > ptc_mmio_regs[ret].mask)
160+
return -EINVAL;
161+
162+
ptc_mmio_write(pdev, data->offset, ret, input);
163+
164+
return count;
165+
}
166+
167+
#define PTC_STORE(suffix)\
168+
static ssize_t suffix##_store(struct device *dev,\
169+
struct device_attribute *attr,\
170+
const char *buf, size_t count)\
171+
{\
172+
struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
173+
return ptc_store(data, dev, attr, buf, count);\
174+
}
175+
176+
PTC_SHOW(temperature_target);
177+
PTC_STORE(temperature_target);
178+
PTC_SHOW(enable);
179+
PTC_STORE(enable);
180+
181+
#define ptc_init_attribute(_name)\
182+
do {\
183+
sysfs_attr_init(&data->_name##_attr.attr);\
184+
data->_name##_attr.show = _name##_show;\
185+
data->_name##_attr.store = _name##_store;\
186+
data->_name##_attr.attr.name = #_name;\
187+
data->_name##_attr.attr.mode = 0644;\
188+
} while (0)
189+
190+
static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data *data)
191+
{
192+
int ret, index = 0;
193+
194+
ptc_init_attribute(temperature_target);
195+
ptc_init_attribute(enable);
196+
197+
data->ptc_attrs[index++] = &data->temperature_target_attr.attr;
198+
data->ptc_attrs[index++] = &data->enable_attr.attr;
199+
data->ptc_attrs[index] = NULL;
200+
201+
snprintf(data->group_name, MAX_ATTR_GROUP_NAME_LEN,
202+
"ptc_%d_control", instance);
203+
data->ptc_attr_group.name = data->group_name;
204+
data->ptc_attr_group.attrs = data->ptc_attrs;
205+
206+
ret = sysfs_create_group(&pdev->dev.kobj, &data->ptc_attr_group);
207+
208+
return ret;
209+
}
210+
211+
static struct ptc_data ptc_instance[PTC_MAX_INSTANCES];
212+
213+
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
214+
{
215+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
216+
int i;
217+
218+
for (i = 0; i < PTC_MAX_INSTANCES; i++) {
219+
ptc_instance[i].offset = ptc_offsets[i];
220+
ptc_create_groups(pdev, i, &ptc_instance[i]);
221+
}
222+
}
223+
224+
return 0;
225+
}
226+
EXPORT_SYMBOL_GPL(proc_thermal_ptc_add);
227+
228+
void proc_thermal_ptc_remove(struct pci_dev *pdev)
229+
{
230+
struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
231+
232+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
233+
int i;
234+
235+
for (i = 0; i < PTC_MAX_INSTANCES; i++)
236+
sysfs_remove_group(&pdev->dev.kobj, &ptc_instance[i].ptc_attr_group);
237+
}
238+
}
239+
EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove);
240+
241+
MODULE_IMPORT_NS("INT340X_THERMAL");
242+
MODULE_LICENSE("GPL");
243+
MODULE_DESCRIPTION("Processor Thermal PTC Interface");

drivers/thermal/intel/int340x_thermal/processor_thermal_device.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,21 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
399399
}
400400
}
401401

402+
if (feature_mask & PROC_THERMAL_FEATURE_PTC) {
403+
ret = proc_thermal_ptc_add(pdev, proc_priv);
404+
if (ret) {
405+
dev_err(&pdev->dev, "failed to add PTC MMIO interface\n");
406+
goto err_rem_rapl;
407+
}
408+
}
409+
402410
if (feature_mask & PROC_THERMAL_FEATURE_FIVR ||
403411
feature_mask & PROC_THERMAL_FEATURE_DVFS ||
404412
feature_mask & PROC_THERMAL_FEATURE_DLVR) {
405413
ret = proc_thermal_rfim_add(pdev, proc_priv);
406414
if (ret) {
407415
dev_err(&pdev->dev, "failed to add RFIM interface\n");
408-
goto err_rem_rapl;
416+
goto err_rem_ptc;
409417
}
410418
}
411419

@@ -427,6 +435,8 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
427435

428436
err_rem_rfim:
429437
proc_thermal_rfim_remove(pdev);
438+
err_rem_ptc:
439+
proc_thermal_ptc_remove(pdev);
430440
err_rem_rapl:
431441
proc_thermal_rapl_remove();
432442

@@ -439,6 +449,9 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *
439449
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_RAPL)
440450
proc_thermal_rapl_remove();
441451

452+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC)
453+
proc_thermal_ptc_remove(pdev);
454+
442455
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR ||
443456
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS ||
444457
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR)

drivers/thermal/intel/int340x_thermal/processor_thermal_device.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct rapl_mmio_regs {
6767
#define PROC_THERMAL_FEATURE_WT_HINT 0x20
6868
#define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40
6969
#define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80
70+
#define PROC_THERMAL_FEATURE_PTC 0x100
7071

7172
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
7273
int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
@@ -123,4 +124,6 @@ int proc_thermal_mmio_add(struct pci_dev *pdev,
123124
struct proc_thermal_device *proc_priv,
124125
kernel_ulong_t feature_mask);
125126
void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
127+
int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
128+
void proc_thermal_ptc_remove(struct pci_dev *pdev);
126129
#endif

drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
486486
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },
487487
{ PCI_DEVICE_DATA(INTEL, LNLM_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |
488488
PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
489-
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
489+
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR |
490+
PROC_THERMAL_FEATURE_PTC) },
490491
{ PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL |
491492
PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR |
492493
PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },
@@ -497,7 +498,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
497498
{ PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL |
498499
PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS |
499500
PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT |
500-
PROC_THERMAL_FEATURE_POWER_FLOOR) },
501+
PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) },
501502
{ },
502503
};
503504

0 commit comments

Comments
 (0)