Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / thermal / intel / int340x_thermal / processor_thermal_wt_hint.c
blob68e8391af8f4e1ac34dedb1b9ee0fb1434cebdcf
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.
6 * Operation:
7 * When user space enables workload type prediction:
8 * - Use mailbox to:
9 * Configure notification delay
10 * Enable processor thermal device interrupt
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.
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.
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.
28 * Copyright (c) 2023, Intel Corporation.
31 #include <linux/bitfield.h>
32 #include <linux/pci.h>
33 #include "processor_thermal_device.h"
35 #define SOC_WT GENMASK_ULL(47, 40)
37 #define SOC_WT_PREDICTION_INT_ENABLE_BIT 23
39 #define SOC_WT_PREDICTION_INT_ACTIVE BIT(2)
42 * Closest possible to 1 Second is 1024 ms with programmed time delay
43 * of 0x0A.
45 static u8 notify_delay = 0x0A;
46 static u16 notify_delay_ms = 1024;
48 static DEFINE_MUTEX(wt_lock);
49 static u8 wt_enable;
51 /* Show current predicted workload type index */
52 static ssize_t workload_type_index_show(struct device *dev,
53 struct device_attribute *attr,
54 char *buf)
56 struct proc_thermal_device *proc_priv;
57 struct pci_dev *pdev = to_pci_dev(dev);
58 u64 status = 0;
59 int wt;
61 mutex_lock(&wt_lock);
62 if (!wt_enable) {
63 mutex_unlock(&wt_lock);
64 return -ENODATA;
67 proc_priv = pci_get_drvdata(pdev);
69 status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
71 mutex_unlock(&wt_lock);
73 wt = FIELD_GET(SOC_WT, status);
75 return sysfs_emit(buf, "%d\n", wt);
78 static DEVICE_ATTR_RO(workload_type_index);
80 static ssize_t workload_hint_enable_show(struct device *dev,
81 struct device_attribute *attr,
82 char *buf)
84 return sysfs_emit(buf, "%d\n", wt_enable);
87 static ssize_t workload_hint_enable_store(struct device *dev,
88 struct device_attribute *attr,
89 const char *buf, size_t size)
91 struct pci_dev *pdev = to_pci_dev(dev);
92 u8 mode;
93 int ret;
95 if (kstrtou8(buf, 10, &mode) || mode > 1)
96 return -EINVAL;
98 mutex_lock(&wt_lock);
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);
108 if (ret)
109 goto ret_enable_store;
111 ret = size;
112 wt_enable = mode;
114 ret_enable_store:
115 mutex_unlock(&wt_lock);
117 return ret;
120 static DEVICE_ATTR_RW(workload_hint_enable);
122 static ssize_t notification_delay_ms_show(struct device *dev,
123 struct device_attribute *attr,
124 char *buf)
126 return sysfs_emit(buf, "%u\n", notify_delay_ms);
129 static ssize_t notification_delay_ms_store(struct device *dev,
130 struct device_attribute *attr,
131 const char *buf, size_t size)
133 struct pci_dev *pdev = to_pci_dev(dev);
134 u16 new_tw;
135 int ret;
136 u8 tm;
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.
148 ret = kstrtou16(buf, 10, &new_tw);
149 if (ret)
150 return ret;
152 if (!new_tw)
153 return -EINVAL;
155 new_tw = roundup_pow_of_two(new_tw);
156 tm = ilog2(new_tw);
157 if (tm > 31)
158 return -EINVAL;
160 mutex_lock(&wt_lock);
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);
168 if (!ret) {
169 ret = size;
170 notify_delay = tm;
171 notify_delay_ms = new_tw;
174 mutex_unlock(&wt_lock);
176 return ret;
179 static DEVICE_ATTR_RW(notification_delay_ms);
181 static 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
188 static const struct attribute_group workload_hint_attribute_group = {
189 .attrs = workload_hint_attrs,
190 .name = "workload_hint"
194 * Callback to check if the interrupt for prediction is active.
195 * Caution: Called from the interrupt context.
197 bool proc_thermal_check_wt_intr(struct proc_thermal_device *proc_priv)
199 u64 int_status;
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;
205 return false;
207 EXPORT_SYMBOL_NS_GPL(proc_thermal_check_wt_intr, "INT340X_THERMAL");
209 /* Callback to notify user space */
210 void proc_thermal_wt_intr_callback(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
212 u64 status;
214 status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
215 if (!(status & SOC_WT_PREDICTION_INT_ACTIVE))
216 return;
218 sysfs_notify(&pdev->dev.kobj, "workload_hint", "workload_type_index");
220 EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_intr_callback, "INT340X_THERMAL");
222 static bool workload_hint_created;
224 int proc_thermal_wt_hint_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
226 int ret;
228 ret = sysfs_create_group(&pdev->dev.kobj, &workload_hint_attribute_group);
229 if (ret)
230 return ret;
232 workload_hint_created = true;
234 return 0;
236 EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_add, "INT340X_THERMAL");
238 void proc_thermal_wt_hint_remove(struct pci_dev *pdev)
240 mutex_lock(&wt_lock);
241 if (wt_enable)
242 processor_thermal_mbox_interrupt_config(pdev, false,
243 SOC_WT_PREDICTION_INT_ENABLE_BIT,
245 mutex_unlock(&wt_lock);
247 if (workload_hint_created)
248 sysfs_remove_group(&pdev->dev.kobj, &workload_hint_attribute_group);
250 workload_hint_created = false;
252 EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, "INT340X_THERMAL");
254 MODULE_IMPORT_NS("INT340X_THERMAL");
255 MODULE_LICENSE("GPL");
256 MODULE_DESCRIPTION("Processor Thermal Work Load type hint Interface");