FILE
processor_thermal_wt_hint.c
File preview with syntax highlighting.
linux-6.19.14/drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * processor thermal device interface for reading workload type hints 4 * from the user space. The hints are provided by the firmware. 5 * 6 * Operation: 7 * When user space enables workload type prediction: 8 * - Use mailbox to: 9 * Configure notification delay 10 * Enable processor thermal device interrupt 11 * 12 * - The predicted workload type can be read from MMIO: 13 * Offset 0x5B18 shows if there was an interrupt 14 * active for change in workload type and also 15 * predicted workload type. 16 * 17 * Two interface functions are provided to call when there is a 18 * thermal device interrupt: 19 * - proc_thermal_check_wt_intr(): 20 * Check if the interrupt is for change in workload type. Called from 21 * interrupt context. 22 * 23 * - proc_thermal_wt_intr_callback(): 24 * Callback for interrupt processing in thread context. This involves 25 * sending notification to user space that there is a change in the 26 * workload type. 27 * 28 * Copyright (c) 2023, Intel Corporation. 29 */ 30 31#include <linux/bitfield.h> 32#include <linux/pci.h> 33#include "processor_thermal_device.h" 34 35#define SOC_WT GENMASK_ULL(47, 40) 36 37#define SOC_WT_PREDICTION_INT_ENABLE_BIT 23 38 39#define SOC_WT_PREDICTION_INT_ACTIVE BIT(2) 40 41/* 42 * Closest possible to 1 Second is 1024 ms with programmed time delay 43 * of 0x0A. 44 */ 45static u8 notify_delay = 0x0A; 46static u16 notify_delay_ms = 1024; 47 48static DEFINE_MUTEX(wt_lock); 49static u8 wt_enable; 50 51/* Show current predicted workload type index */ 52static ssize_t workload_type_index_show(struct device *dev, 53 struct device_attribute *attr, 54 char *buf) 55{ 56 struct proc_thermal_device *proc_priv; 57 struct pci_dev *pdev = to_pci_dev(dev); 58 u64 status = 0; 59 int wt; 60 61 mutex_lock(&wt_lock); 62 if (!wt_enable) { 63 mutex_unlock(&wt_lock); 64 return -ENODATA; 65 } 66 67 proc_priv = pci_get_drvdata(pdev); 68 69 status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET); 70 71 mutex_unlock(&wt_lock); 72 73 wt = FIELD_GET(SOC_WT, status); 74 75 return sysfs_emit(buf, "%d\n", wt); 76} 77 78static DEVICE_ATTR_RO(workload_type_index); 79 80static ssize_t workload_hint_enable_show(struct device *dev, 81 struct device_attribute *attr, 82 char *buf) 83{ 84 return sysfs_emit(buf, "%d\n", wt_enable); 85} 86 87static ssize_t workload_hint_enable_store(struct device *dev, 88 struct device_attribute *attr, 89 const char *buf, size_t size) 90{ 91 struct pci_dev *pdev = to_pci_dev(dev); 92 u8 mode; 93 int ret; 94 95 if (kstrtou8(buf, 10, &mode) || mode > 1) 96 return -EINVAL; 97 98 mutex_lock(&wt_lock); 99 100 if (mode) 101 ret = processor_thermal_mbox_interrupt_config(pdev, true, 102 SOC_WT_PREDICTION_INT_ENABLE_BIT, 103 notify_delay); 104 else 105 ret = processor_thermal_mbox_interrupt_config(pdev, false, 106 SOC_WT_PREDICTION_INT_ENABLE_BIT, 0); 107 108 if (ret) 109 goto ret_enable_store; 110 111 ret = size; 112 wt_enable = mode; 113 114ret_enable_store: 115 mutex_unlock(&wt_lock); 116 117 return ret; 118} 119 120static DEVICE_ATTR_RW(workload_hint_enable); 121 122static ssize_t notification_delay_ms_show(struct device *dev, 123 struct device_attribute *attr, 124 char *buf) 125{ 126 return sysfs_emit(buf, "%u\n", notify_delay_ms); 127} 128 129static ssize_t notification_delay_ms_store(struct device *dev, 130 struct device_attribute *attr, 131 const char *buf, size_t size) 132{ 133 struct pci_dev *pdev = to_pci_dev(dev); 134 u16 new_tw; 135 int ret; 136 u8 tm; 137 138 /* 139 * Time window register value: 140 * Formula: (1 + x/4) * power(2,y) 141 * x = 2 msbs, that is [30:29] y = 5 [28:24] 142 * in INTR_CONFIG register. 143 * The result will be in milli seconds. 144 * Here, just keep x = 0, and just change y. 145 * First round up the user value to power of 2 and 146 * then take log2, to get "y" value to program. 147 */ 148 ret = kstrtou16(buf, 10, &new_tw); 149 if (ret) 150 return ret; 151 152 if (!new_tw) 153 return -EINVAL; 154 155 new_tw = roundup_pow_of_two(new_tw); 156 tm = ilog2(new_tw); 157 if (tm > 31) 158 return -EINVAL; 159 160 mutex_lock(&wt_lock); 161 162 /* If the workload hint was already enabled, then update with the new delay */ 163 if (wt_enable) 164 ret = processor_thermal_mbox_interrupt_config(pdev, true, 165 SOC_WT_PREDICTION_INT_ENABLE_BIT, 166 tm); 167 168 if (!ret) { 169 ret = size; 170 notify_delay = tm; 171 notify_delay_ms = new_tw; 172 } 173 174 mutex_unlock(&wt_lock); 175 176 return ret; 177} 178 179static DEVICE_ATTR_RW(notification_delay_ms); 180 181static struct attribute *workload_hint_attrs[] = { 182 &dev_attr_workload_type_index.attr, 183 &dev_attr_workload_hint_enable.attr, 184 &dev_attr_notification_delay_ms.attr, 185 NULL 186}; 187 188static const struct attribute_group workload_hint_attribute_group = { 189 .attrs = workload_hint_attrs, 190 .name = "workload_hint" 191}; 192 193/* 194 * Callback to check if the interrupt for prediction is active. 195 * Caution: Called from the interrupt context. 196 */ 197bool proc_thermal_check_wt_intr(struct proc_thermal_device *proc_priv) 198{ 199 u64 int_status; 200 201 int_status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET); 202 if (int_status & SOC_WT_PREDICTION_INT_ACTIVE) 203 return true; 204 205 return false; 206} 207EXPORT_SYMBOL_NS_GPL(proc_thermal_check_wt_intr, "INT340X_THERMAL"); 208 209/* Callback to notify user space */ 210void proc_thermal_wt_intr_callback(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) 211{ 212 u64 status; 213 214 status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET); 215 if (!(status & SOC_WT_PREDICTION_INT_ACTIVE)) 216 return; 217 218 sysfs_notify(&pdev->dev.kobj, "workload_hint", "workload_type_index"); 219} 220EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_intr_callback, "INT340X_THERMAL"); 221 222static bool workload_hint_created; 223 224int proc_thermal_wt_hint_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) 225{ 226 int ret; 227 228 ret = sysfs_create_group(&pdev->dev.kobj, &workload_hint_attribute_group); 229 if (ret) 230 return ret; 231 232 workload_hint_created = true; 233 234 return 0; 235} 236EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_add, "INT340X_THERMAL"); 237 238void proc_thermal_wt_hint_remove(struct pci_dev *pdev) 239{ 240 mutex_lock(&wt_lock); 241 if (wt_enable) 242 processor_thermal_mbox_interrupt_config(pdev, false, 243 SOC_WT_PREDICTION_INT_ENABLE_BIT, 244 0); 245 mutex_unlock(&wt_lock); 246 247 if (workload_hint_created) 248 sysfs_remove_group(&pdev->dev.kobj, &workload_hint_attribute_group); 249 250 workload_hint_created = false; 251} 252EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, "INT340X_THERMAL"); 253 254MODULE_IMPORT_NS("INT340X_THERMAL"); 255MODULE_LICENSE("GPL"); 256MODULE_DESCRIPTION("Processor Thermal Work Load type hint Interface");
~/
Read-only file preview.