File Index
Files and directories
Sign in
FILE

platform_temperature_control.c

File preview with syntax highlighting.

linux-6.19.14/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c C
  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
 45struct 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
 55struct 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
 66static 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 */
 78static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30};
 79
 80/* These will represent sysfs attribute names */
 81static 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 */
 89static DEFINE_MUTEX(ptc_lock);
 90
 91static 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)\
119static 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
127static 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
145static 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)\
172static 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
180PTC_SHOW(temperature_target);
181PTC_STORE(temperature_target);
182PTC_SHOW(enable);
183PTC_STORE(enable);
184PTC_SHOW(thermal_tolerance);
185PTC_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
196static 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
219static struct ptc_data ptc_instance[PTC_MAX_INSTANCES];
220static struct dentry *ptc_debugfs;
221
222#define PTC_TEMP_OVERRIDE_ENABLE_INDEX	4
223#define PTC_TEMP_OVERRIDE_INDEX		5
224
225static 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
258static const struct file_operations ptc_fops = {
259	.open = simple_open,
260	.write = ptc_temperature_write,
261	.llseek = generic_file_llseek,
262};
263
264static 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
273static void ptc_delete_debugfs(void)
274{
275	debugfs_remove_recursive(ptc_debugfs);
276}
277
278int 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}
294EXPORT_SYMBOL_GPL(proc_thermal_ptc_add);
295
296void 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}
309EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove);
310
311MODULE_IMPORT_NS("INT340X_THERMAL");
312MODULE_LICENSE("GPL");
313MODULE_DESCRIPTION("Processor Thermal PTC Interface");
~/ Read-only file preview.