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