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/debugfs.h>
42 #include <linux/pci.h>
43 #include "processor_thermal_device.h"
44
45 struct mmio_reg {
46 int bits;
47 u16 mask;
48 u16 shift;
49 u16 units;
50 };
51
52 #define MAX_ATTR_GROUP_NAME_LEN 32
53 #define PTC_MAX_ATTRS 4
54
55 struct ptc_data {
56 u32 offset;
57 struct pci_dev *pdev;
58 struct attribute_group ptc_attr_group;
59 struct attribute *ptc_attrs[PTC_MAX_ATTRS];
60 struct device_attribute temperature_target_attr;
61 struct device_attribute enable_attr;
62 struct device_attribute thermal_tolerance_attr;
63 char group_name[MAX_ATTR_GROUP_NAME_LEN];
64 };
65
66 static const struct mmio_reg ptc_mmio_regs[] = {
67 { 8, 0xFF, 0, 500}, /* temperature_target, units 0.5C*/
68 { 1, 0x01, 8, 0}, /* enable */
69 { 3, 0x7, 9, 0}, /* gain */
70 { 8, 0xFF, 16, 0}, /* min_performance_level */
71 { 1, 0x1, 12, 0}, /* temperature_override_enable */
72 { 8, 0xFF, 24, 500}, /* temperature_override, units 0.5C */
73 };
74
75 #define PTC_MAX_INSTANCES 3
76
77 /* Unique offset for each PTC instance */
78 static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30};
79
80 /* These will represent sysfs attribute names */
81 static const char * const ptc_strings[] = {
82 "temperature_target",
83 "enable",
84 "thermal_tolerance",
85 NULL
86 };
87
88 /* Lock to protect concurrent read/write and read-modify-write */
89 static DEFINE_MUTEX(ptc_lock);
90
ptc_mmio_show(struct ptc_data * data,struct device * dev,struct device_attribute * attr,char * buf)91 static ssize_t ptc_mmio_show(struct ptc_data *data, struct device *dev,
92 struct device_attribute *attr, char *buf)
93 {
94 struct pci_dev *pdev = to_pci_dev(dev);
95 struct proc_thermal_device *proc_priv;
96 const struct mmio_reg *mmio_regs;
97 int ret, units;
98 u64 reg_val;
99
100 proc_priv = pci_get_drvdata(pdev);
101 mmio_regs = ptc_mmio_regs;
102 ret = match_string(ptc_strings, -1, attr->attr.name);
103 if (ret < 0)
104 return ret;
105
106 units = mmio_regs[ret].units;
107
108 guard(mutex)(&ptc_lock);
109
110 reg_val = readq((void __iomem *) (proc_priv->mmio_base + data->offset));
111 ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;
112 if (units)
113 ret *= units;
114
115 return sysfs_emit(buf, "%d\n", ret);
116 }
117
118 #define PTC_SHOW(suffix)\
119 static ssize_t suffix##_show(struct device *dev,\
120 struct device_attribute *attr,\
121 char *buf)\
122 {\
123 struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
124 return ptc_mmio_show(data, dev, attr, buf);\
125 }
126
ptc_mmio_write(struct pci_dev * pdev,u32 offset,int index,u32 value)127 static void ptc_mmio_write(struct pci_dev *pdev, u32 offset, int index, u32 value)
128 {
129 struct proc_thermal_device *proc_priv;
130 u64 mask, reg_val;
131
132 proc_priv = pci_get_drvdata(pdev);
133
134 mask = GENMASK_ULL(ptc_mmio_regs[index].shift + ptc_mmio_regs[index].bits - 1,
135 ptc_mmio_regs[index].shift);
136
137 guard(mutex)(&ptc_lock);
138
139 reg_val = readq((void __iomem *) (proc_priv->mmio_base + offset));
140 reg_val &= ~mask;
141 reg_val |= (value << ptc_mmio_regs[index].shift);
142 writeq(reg_val, (void __iomem *) (proc_priv->mmio_base + offset));
143 }
144
ptc_store(struct ptc_data * data,struct device * dev,struct device_attribute * attr,const char * buf,size_t count)145 static int ptc_store(struct ptc_data *data, struct device *dev, struct device_attribute *attr,
146 const char *buf, size_t count)
147 {
148 struct pci_dev *pdev = to_pci_dev(dev);
149 unsigned int input;
150 int ret;
151
152 ret = kstrtouint(buf, 10, &input);
153 if (ret)
154 return ret;
155
156 ret = match_string(ptc_strings, -1, attr->attr.name);
157 if (ret < 0)
158 return ret;
159
160 if (ptc_mmio_regs[ret].units)
161 input /= ptc_mmio_regs[ret].units;
162
163 if (input > ptc_mmio_regs[ret].mask)
164 return -EINVAL;
165
166 ptc_mmio_write(pdev, data->offset, ret, input);
167
168 return count;
169 }
170
171 #define PTC_STORE(suffix)\
172 static ssize_t suffix##_store(struct device *dev,\
173 struct device_attribute *attr,\
174 const char *buf, size_t count)\
175 {\
176 struct ptc_data *data = container_of(attr, struct ptc_data, suffix##_attr);\
177 return ptc_store(data, dev, attr, buf, count);\
178 }
179
180 PTC_SHOW(temperature_target);
181 PTC_STORE(temperature_target);
182 PTC_SHOW(enable);
183 PTC_STORE(enable);
184 PTC_SHOW(thermal_tolerance);
185 PTC_STORE(thermal_tolerance);
186
187 #define ptc_init_attribute(_name)\
188 do {\
189 sysfs_attr_init(&data->_name##_attr.attr);\
190 data->_name##_attr.show = _name##_show;\
191 data->_name##_attr.store = _name##_store;\
192 data->_name##_attr.attr.name = #_name;\
193 data->_name##_attr.attr.mode = 0644;\
194 } while (0)
195
ptc_create_groups(struct pci_dev * pdev,int instance,struct ptc_data * data)196 static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data *data)
197 {
198 int ret, index = 0;
199
200 ptc_init_attribute(temperature_target);
201 ptc_init_attribute(enable);
202 ptc_init_attribute(thermal_tolerance);
203
204 data->ptc_attrs[index++] = &data->temperature_target_attr.attr;
205 data->ptc_attrs[index++] = &data->enable_attr.attr;
206 data->ptc_attrs[index++] = &data->thermal_tolerance_attr.attr;
207 data->ptc_attrs[index] = NULL;
208
209 snprintf(data->group_name, MAX_ATTR_GROUP_NAME_LEN,
210 "ptc_%d_control", instance);
211 data->ptc_attr_group.name = data->group_name;
212 data->ptc_attr_group.attrs = data->ptc_attrs;
213
214 ret = sysfs_create_group(&pdev->dev.kobj, &data->ptc_attr_group);
215
216 return ret;
217 }
218
219 static struct ptc_data ptc_instance[PTC_MAX_INSTANCES];
220 static struct dentry *ptc_debugfs;
221
222 #define PTC_TEMP_OVERRIDE_ENABLE_INDEX 4
223 #define PTC_TEMP_OVERRIDE_INDEX 5
224
ptc_temperature_write(struct file * file,const char __user * data,size_t count,loff_t * ppos)225 static ssize_t ptc_temperature_write(struct file *file, const char __user *data,
226 size_t count, loff_t *ppos)
227 {
228 struct ptc_data *ptc_instance = file->private_data;
229 struct pci_dev *pdev = ptc_instance->pdev;
230 char buf[32];
231 ssize_t len;
232 u32 value;
233
234 len = min(count, sizeof(buf) - 1);
235 if (copy_from_user(buf, data, len))
236 return -EFAULT;
237
238 buf[len] = '\0';
239 if (kstrtouint(buf, 0, &value))
240 return -EINVAL;
241
242 if (ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units)
243 value /= ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units;
244
245 if (value > ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].mask)
246 return -EINVAL;
247
248 if (!value) {
249 ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 0);
250 } else {
251 ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_INDEX, value);
252 ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 1);
253 }
254
255 return count;
256 }
257
258 static const struct file_operations ptc_fops = {
259 .open = simple_open,
260 .write = ptc_temperature_write,
261 .llseek = generic_file_llseek,
262 };
263
ptc_create_debugfs(void)264 static void ptc_create_debugfs(void)
265 {
266 ptc_debugfs = debugfs_create_dir("platform_temperature_control", NULL);
267
268 debugfs_create_file("temperature_0", 0200, ptc_debugfs, &ptc_instance[0], &ptc_fops);
269 debugfs_create_file("temperature_1", 0200, ptc_debugfs, &ptc_instance[1], &ptc_fops);
270 debugfs_create_file("temperature_2", 0200, ptc_debugfs, &ptc_instance[2], &ptc_fops);
271 }
272
ptc_delete_debugfs(void)273 static void ptc_delete_debugfs(void)
274 {
275 debugfs_remove_recursive(ptc_debugfs);
276 }
277
proc_thermal_ptc_add(struct pci_dev * pdev,struct proc_thermal_device * proc_priv)278 int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
279 {
280 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
281 int i;
282
283 for (i = 0; i < PTC_MAX_INSTANCES; i++) {
284 ptc_instance[i].offset = ptc_offsets[i];
285 ptc_instance[i].pdev = pdev;
286 ptc_create_groups(pdev, i, &ptc_instance[i]);
287 }
288
289 ptc_create_debugfs();
290 }
291
292 return 0;
293 }
294 EXPORT_SYMBOL_GPL(proc_thermal_ptc_add);
295
proc_thermal_ptc_remove(struct pci_dev * pdev)296 void proc_thermal_ptc_remove(struct pci_dev *pdev)
297 {
298 struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
299
300 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_PTC) {
301 int i;
302
303 for (i = 0; i < PTC_MAX_INSTANCES; i++)
304 sysfs_remove_group(&pdev->dev.kobj, &ptc_instance[i].ptc_attr_group);
305
306 ptc_delete_debugfs();
307 }
308 }
309 EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove);
310
311 MODULE_IMPORT_NS("INT340X_THERMAL");
312 MODULE_LICENSE("GPL");
313 MODULE_DESCRIPTION("Processor Thermal PTC Interface");
314