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 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 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 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 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 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 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 273 static void ptc_delete_debugfs(void) 274 { 275 debugfs_remove_recursive(ptc_debugfs); 276 } 277 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 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