]> Git Repo - J-linux.git/blob - drivers/thermal/intel/int340x_thermal/processor_thermal_wt_hint.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / 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  */
45 static u8 notify_delay = 0x0A;
46 static u16 notify_delay_ms = 1024;
47
48 static DEFINE_MUTEX(wt_lock);
49 static u8 wt_enable;
50
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)
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
78 static DEVICE_ATTR_RO(workload_type_index);
79
80 static 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
87 static 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
114 ret_enable_store:
115         mutex_unlock(&wt_lock);
116
117         return ret;
118 }
119
120 static DEVICE_ATTR_RW(workload_hint_enable);
121
122 static 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
129 static 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
179 static DEVICE_ATTR_RW(notification_delay_ms);
180
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
186 };
187
188 static 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  */
197 bool 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 }
207 EXPORT_SYMBOL_NS_GPL(proc_thermal_check_wt_intr, "INT340X_THERMAL");
208
209 /* Callback to notify user space */
210 void 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 }
220 EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_intr_callback, "INT340X_THERMAL");
221
222 static bool workload_hint_created;
223
224 int 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 }
236 EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_add, "INT340X_THERMAL");
237
238 void 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 }
252 EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, "INT340X_THERMAL");
253
254 MODULE_IMPORT_NS("INT340X_THERMAL");
255 MODULE_LICENSE("GPL");
256 MODULE_DESCRIPTION("Processor Thermal Work Load type hint Interface");
This page took 0.042655 seconds and 4 git commands to generate.