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