xref: /linux/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
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