1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * processor thermal device RFIM control
4 * Copyright (c) 2020, Intel Corporation.
5 */
6
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/pci.h>
10 #include <linux/sysfs.h>
11 #include "processor_thermal_device.h"
12
13 MODULE_IMPORT_NS("INT340X_THERMAL");
14
15 struct mmio_reg {
16 int read_only;
17 u32 offset;
18 int bits;
19 u16 mask;
20 u16 shift;
21 };
22
23 struct mapping_table {
24 const char *attr_name;
25 const u32 value;
26 const char *mapped_str;
27 };
28
29 /* These will represent sysfs attribute names */
30 static const char * const fivr_strings[] = {
31 "vco_ref_code_lo",
32 "vco_ref_code_hi",
33 "spread_spectrum_pct",
34 "spread_spectrum_clk_enable",
35 "rfi_vco_ref_code",
36 "fivr_fffc_rev",
37 NULL
38 };
39
40 static const struct mmio_reg tgl_fivr_mmio_regs[] = {
41 { 0, 0x5A18, 3, 0x7, 11}, /* vco_ref_code_lo */
42 { 0, 0x5A18, 8, 0xFF, 16}, /* vco_ref_code_hi */
43 { 0, 0x5A08, 8, 0xFF, 0}, /* spread_spectrum_pct */
44 { 0, 0x5A08, 1, 0x1, 8}, /* spread_spectrum_clk_enable */
45 { 1, 0x5A10, 12, 0xFFF, 0}, /* rfi_vco_ref_code */
46 { 1, 0x5A14, 2, 0x3, 1}, /* fivr_fffc_rev */
47 };
48
49 static const char * const dlvr_strings[] = {
50 "dlvr_spread_spectrum_pct",
51 "dlvr_control_mode",
52 "dlvr_control_lock",
53 "dlvr_rfim_enable",
54 "dlvr_freq_select",
55 "dlvr_hardware_rev",
56 "dlvr_freq_mhz",
57 "dlvr_pll_busy",
58 NULL
59 };
60
61 static const struct mmio_reg dlvr_mmio_regs[] = {
62 { 0, 0x15A08, 5, 0x1F, 0}, /* dlvr_spread_spectrum_pct */
63 { 0, 0x15A08, 1, 0x1, 5}, /* dlvr_control_mode */
64 { 0, 0x15A08, 1, 0x1, 6}, /* dlvr_control_lock */
65 { 0, 0x15A08, 1, 0x1, 7}, /* dlvr_rfim_enable */
66 { 0, 0x15A08, 12, 0xFFF, 8}, /* dlvr_freq_select */
67 { 1, 0x15A10, 2, 0x3, 30}, /* dlvr_hardware_rev */
68 { 1, 0x15A10, 16, 0xFFFF, 0}, /* dlvr_freq_mhz */
69 { 1, 0x15A10, 1, 0x1, 16}, /* dlvr_pll_busy */
70 };
71
72 static const struct mmio_reg lnl_dlvr_mmio_regs[] = {
73 { 0, 0x5A08, 5, 0x1F, 0}, /* dlvr_spread_spectrum_pct */
74 { 0, 0x5A08, 1, 0x1, 5}, /* dlvr_control_mode */
75 { 0, 0x5A08, 1, 0x1, 6}, /* dlvr_control_lock */
76 { 0, 0x5A08, 1, 0x1, 7}, /* dlvr_rfim_enable */
77 { 0, 0x5A08, 2, 0x3, 8}, /* dlvr_freq_select */
78 { 1, 0x5A10, 2, 0x3, 30}, /* dlvr_hardware_rev */
79 { 1, 0x5A10, 2, 0x3, 0}, /* dlvr_freq_mhz */
80 { 1, 0x5A10, 1, 0x1, 23}, /* dlvr_pll_busy */
81 };
82
83 static const struct mapping_table lnl_dlvr_mapping[] = {
84 {"dlvr_freq_select", 0, "2227.2"},
85 {"dlvr_freq_select", 1, "2140"},
86 {"dlvr_freq_mhz", 0, "2227.2"},
87 {"dlvr_freq_mhz", 1, "2140"},
88 {NULL, 0, NULL},
89 };
90
91 static const struct mmio_reg nvl_dlvr_mmio_regs[] = {
92 { 0, 0x19208, 5, 0x1F, 0}, /* dlvr_spread_spectrum_pct */
93 { 0, 0x19208, 1, 0x1, 5}, /* dlvr_control_mode */
94 { 0, 0x19208, 1, 0x1, 6}, /* dlvr_control_lock */
95 { 0, 0x19208, 1, 0x1, 7}, /* dlvr_rfim_enable */
96 { 0, 0x19208, 12, 0xFFF, 8}, /* dlvr_freq_select */
97 { 1, 0x19210, 2, 0x3, 30}, /* dlvr_hardware_rev */
98 { 1, 0x19210, 16, 0xFFFF, 0}, /* dlvr_freq_mhz */
99 { 1, 0x19210, 1, 0x1, 16}, /* dlvr_pll_busy */
100 };
101
match_mapping_table(const struct mapping_table * table,const char * attr_name,bool match_int_value,const u32 value,const char * value_str,char ** result_str,u32 * result_int)102 static int match_mapping_table(const struct mapping_table *table, const char *attr_name,
103 bool match_int_value, const u32 value, const char *value_str,
104 char **result_str, u32 *result_int)
105 {
106 bool attr_matched = false;
107 int i = 0;
108
109 if (!table)
110 return -EOPNOTSUPP;
111
112 while (table[i].attr_name) {
113 if (strncmp(table[i].attr_name, attr_name, strlen(attr_name)))
114 goto match_next;
115
116 attr_matched = true;
117
118 if (match_int_value) {
119 if (table[i].value != value)
120 goto match_next;
121
122 *result_str = (char *)table[i].mapped_str;
123 return 0;
124 }
125
126 if (strncmp(table[i].mapped_str, value_str, strlen(table[i].mapped_str)))
127 goto match_next;
128
129 *result_int = table[i].value;
130
131 return 0;
132 match_next:
133 i++;
134 }
135
136 /* If attribute name is matched, then the user space value is invalid */
137 if (attr_matched)
138 return -EINVAL;
139
140 return -EOPNOTSUPP;
141 }
142
get_mapped_string(const struct mapping_table * table,const char * attr_name,u32 value,char ** result)143 static int get_mapped_string(const struct mapping_table *table, const char *attr_name,
144 u32 value, char **result)
145 {
146 return match_mapping_table(table, attr_name, true, value, NULL, result, NULL);
147 }
148
get_mapped_value(const struct mapping_table * table,const char * attr_name,const char * value,unsigned int * result)149 static int get_mapped_value(const struct mapping_table *table, const char *attr_name,
150 const char *value, unsigned int *result)
151 {
152 return match_mapping_table(table, attr_name, false, 0, value, NULL, result);
153 }
154
155 /* These will represent sysfs attribute names */
156 static const char * const dvfs_strings[] = {
157 "rfi_restriction_run_busy",
158 "rfi_restriction_err_code",
159 "rfi_restriction_data_rate",
160 "rfi_restriction_data_rate_base",
161 "ddr_data_rate_point_0",
162 "ddr_data_rate_point_1",
163 "ddr_data_rate_point_2",
164 "ddr_data_rate_point_3",
165 "rfi_disable",
166 NULL
167 };
168
169 static const struct mmio_reg adl_dvfs_mmio_regs[] = {
170 { 0, 0x5A38, 1, 0x1, 31}, /* rfi_restriction_run_busy */
171 { 0, 0x5A38, 7, 0x7F, 24}, /* rfi_restriction_err_code */
172 { 0, 0x5A38, 8, 0xFF, 16}, /* rfi_restriction_data_rate */
173 { 0, 0x5A38, 16, 0xFFFF, 0}, /* rfi_restriction_data_rate_base */
174 { 0, 0x5A30, 10, 0x3FF, 0}, /* ddr_data_rate_point_0 */
175 { 0, 0x5A30, 10, 0x3FF, 10}, /* ddr_data_rate_point_1 */
176 { 0, 0x5A30, 10, 0x3FF, 20}, /* ddr_data_rate_point_2 */
177 { 0, 0x5A30, 10, 0x3FF, 30}, /* ddr_data_rate_point_3 */
178 { 0, 0x5A40, 1, 0x1, 0}, /* rfi_disable */
179 };
180
181 static const struct mapping_table *dlvr_mapping;
182 static const struct mmio_reg *dlvr_mmio_regs_table;
183
184 #define RFIM_SHOW(suffix, table)\
185 static ssize_t suffix##_show(struct device *dev,\
186 struct device_attribute *attr,\
187 char *buf)\
188 {\
189 const struct mmio_reg *mmio_regs = dlvr_mmio_regs_table;\
190 const struct mapping_table *mapping = dlvr_mapping;\
191 struct proc_thermal_device *proc_priv;\
192 struct pci_dev *pdev = to_pci_dev(dev);\
193 const char **match_strs;\
194 int ret, err;\
195 u32 reg_val;\
196 char *str;\
197 \
198 proc_priv = pci_get_drvdata(pdev);\
199 if (table == 1) {\
200 match_strs = (const char **)dvfs_strings;\
201 mmio_regs = adl_dvfs_mmio_regs;\
202 } else if (table == 2) { \
203 match_strs = (const char **)dlvr_strings;\
204 } else {\
205 match_strs = (const char **)fivr_strings;\
206 mmio_regs = tgl_fivr_mmio_regs;\
207 } \
208 ret = match_string(match_strs, -1, attr->attr.name);\
209 if (ret < 0)\
210 return ret;\
211 reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
212 ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;\
213 err = get_mapped_string(mapping, attr->attr.name, ret, &str);\
214 if (!err)\
215 return sysfs_emit(buf, "%s\n", str);\
216 if (err == -EOPNOTSUPP)\
217 return sysfs_emit(buf, "%u\n", ret);\
218 return err;\
219 }
220
221 #define RFIM_STORE(suffix, table)\
222 static ssize_t suffix##_store(struct device *dev,\
223 struct device_attribute *attr,\
224 const char *buf, size_t count)\
225 {\
226 const struct mmio_reg *mmio_regs = dlvr_mmio_regs_table;\
227 const struct mapping_table *mapping = dlvr_mapping;\
228 struct proc_thermal_device *proc_priv;\
229 struct pci_dev *pdev = to_pci_dev(dev);\
230 unsigned int input;\
231 const char **match_strs;\
232 int ret, err;\
233 u32 reg_val;\
234 u32 mask;\
235 \
236 proc_priv = pci_get_drvdata(pdev);\
237 if (table == 1) {\
238 match_strs = (const char **)dvfs_strings;\
239 mmio_regs = adl_dvfs_mmio_regs;\
240 } else if (table == 2) { \
241 match_strs = (const char **)dlvr_strings;\
242 } else {\
243 match_strs = (const char **)fivr_strings;\
244 mmio_regs = tgl_fivr_mmio_regs;\
245 } \
246 \
247 ret = match_string(match_strs, -1, attr->attr.name);\
248 if (ret < 0)\
249 return ret;\
250 if (mmio_regs[ret].read_only)\
251 return -EPERM;\
252 err = get_mapped_value(mapping, attr->attr.name, buf, &input);\
253 if (err == -EINVAL)\
254 return err;\
255 if (err == -EOPNOTSUPP) {\
256 err = kstrtouint(buf, 10, &input);\
257 if (err)\
258 return err;\
259 } \
260 mask = GENMASK(mmio_regs[ret].shift + mmio_regs[ret].bits - 1, mmio_regs[ret].shift);\
261 reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
262 reg_val &= ~mask;\
263 reg_val |= (input << mmio_regs[ret].shift);\
264 writel(reg_val, (void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
265 return count;\
266 }
267
268 RFIM_SHOW(vco_ref_code_lo, 0)
269 RFIM_SHOW(vco_ref_code_hi, 0)
270 RFIM_SHOW(spread_spectrum_pct, 0)
271 RFIM_SHOW(spread_spectrum_clk_enable, 0)
272 RFIM_SHOW(rfi_vco_ref_code, 0)
273 RFIM_SHOW(fivr_fffc_rev, 0)
274
275 RFIM_STORE(vco_ref_code_lo, 0)
276 RFIM_STORE(vco_ref_code_hi, 0)
277 RFIM_STORE(spread_spectrum_pct, 0)
278 RFIM_STORE(spread_spectrum_clk_enable, 0)
279 RFIM_STORE(rfi_vco_ref_code, 0)
280 RFIM_STORE(fivr_fffc_rev, 0)
281
282 RFIM_SHOW(dlvr_spread_spectrum_pct, 2)
283 RFIM_SHOW(dlvr_control_mode, 2)
284 RFIM_SHOW(dlvr_control_lock, 2)
285 RFIM_SHOW(dlvr_hardware_rev, 2)
286 RFIM_SHOW(dlvr_freq_mhz, 2)
287 RFIM_SHOW(dlvr_pll_busy, 2)
288 RFIM_SHOW(dlvr_freq_select, 2)
289 RFIM_SHOW(dlvr_rfim_enable, 2)
290
291 RFIM_STORE(dlvr_spread_spectrum_pct, 2)
292 RFIM_STORE(dlvr_rfim_enable, 2)
293 RFIM_STORE(dlvr_freq_select, 2)
294 RFIM_STORE(dlvr_control_mode, 2)
295 RFIM_STORE(dlvr_control_lock, 2)
296
297 static DEVICE_ATTR_RW(dlvr_spread_spectrum_pct);
298 static DEVICE_ATTR_RW(dlvr_control_mode);
299 static DEVICE_ATTR_RW(dlvr_control_lock);
300 static DEVICE_ATTR_RW(dlvr_freq_select);
301 static DEVICE_ATTR_RO(dlvr_hardware_rev);
302 static DEVICE_ATTR_RO(dlvr_freq_mhz);
303 static DEVICE_ATTR_RO(dlvr_pll_busy);
304 static DEVICE_ATTR_RW(dlvr_rfim_enable);
305
306 static struct attribute *dlvr_attrs[] = {
307 &dev_attr_dlvr_spread_spectrum_pct.attr,
308 &dev_attr_dlvr_control_mode.attr,
309 &dev_attr_dlvr_control_lock.attr,
310 &dev_attr_dlvr_freq_select.attr,
311 &dev_attr_dlvr_hardware_rev.attr,
312 &dev_attr_dlvr_freq_mhz.attr,
313 &dev_attr_dlvr_pll_busy.attr,
314 &dev_attr_dlvr_rfim_enable.attr,
315 NULL
316 };
317
318 static const struct attribute_group dlvr_attribute_group = {
319 .attrs = dlvr_attrs,
320 .name = "dlvr"
321 };
322
323 static DEVICE_ATTR_RW(vco_ref_code_lo);
324 static DEVICE_ATTR_RW(vco_ref_code_hi);
325 static DEVICE_ATTR_RW(spread_spectrum_pct);
326 static DEVICE_ATTR_RW(spread_spectrum_clk_enable);
327 static DEVICE_ATTR_RW(rfi_vco_ref_code);
328 static DEVICE_ATTR_RW(fivr_fffc_rev);
329
330 static struct attribute *fivr_attrs[] = {
331 &dev_attr_vco_ref_code_lo.attr,
332 &dev_attr_vco_ref_code_hi.attr,
333 &dev_attr_spread_spectrum_pct.attr,
334 &dev_attr_spread_spectrum_clk_enable.attr,
335 &dev_attr_rfi_vco_ref_code.attr,
336 &dev_attr_fivr_fffc_rev.attr,
337 NULL
338 };
339
340 static const struct attribute_group fivr_attribute_group = {
341 .attrs = fivr_attrs,
342 .name = "fivr"
343 };
344
345 RFIM_SHOW(rfi_restriction_run_busy, 1)
346 RFIM_SHOW(rfi_restriction_err_code, 1)
347 RFIM_SHOW(rfi_restriction_data_rate, 1)
348 RFIM_SHOW(rfi_restriction_data_rate_base, 1)
349 RFIM_SHOW(ddr_data_rate_point_0, 1)
350 RFIM_SHOW(ddr_data_rate_point_1, 1)
351 RFIM_SHOW(ddr_data_rate_point_2, 1)
352 RFIM_SHOW(ddr_data_rate_point_3, 1)
353 RFIM_SHOW(rfi_disable, 1)
354
355 RFIM_STORE(rfi_restriction_run_busy, 1)
356 RFIM_STORE(rfi_restriction_err_code, 1)
357 RFIM_STORE(rfi_restriction_data_rate, 1)
358 RFIM_STORE(rfi_restriction_data_rate_base, 1)
359 RFIM_STORE(rfi_disable, 1)
360
361 static DEVICE_ATTR_RW(rfi_restriction_run_busy);
362 static DEVICE_ATTR_RW(rfi_restriction_err_code);
363 static DEVICE_ATTR_RW(rfi_restriction_data_rate);
364 static DEVICE_ATTR_RW(rfi_restriction_data_rate_base);
365 static DEVICE_ATTR_RO(ddr_data_rate_point_0);
366 static DEVICE_ATTR_RO(ddr_data_rate_point_1);
367 static DEVICE_ATTR_RO(ddr_data_rate_point_2);
368 static DEVICE_ATTR_RO(ddr_data_rate_point_3);
369 static DEVICE_ATTR_RW(rfi_disable);
370
rfi_restriction_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)371 static ssize_t rfi_restriction_store(struct device *dev,
372 struct device_attribute *attr,
373 const char *buf, size_t count)
374 {
375 u16 id = 0x0008;
376 u32 input;
377 int ret;
378
379 ret = kstrtou32(buf, 10, &input);
380 if (ret)
381 return ret;
382
383 ret = processor_thermal_send_mbox_write_cmd(to_pci_dev(dev), id, input);
384 if (ret)
385 return ret;
386
387 return count;
388 }
389
rfi_restriction_show(struct device * dev,struct device_attribute * attr,char * buf)390 static ssize_t rfi_restriction_show(struct device *dev,
391 struct device_attribute *attr,
392 char *buf)
393 {
394 u16 id = 0x0007;
395 u64 resp;
396 int ret;
397
398 ret = processor_thermal_send_mbox_read_cmd(to_pci_dev(dev), id, &resp);
399 if (ret)
400 return ret;
401
402 return sysfs_emit(buf, "%llu\n", resp);
403 }
404
ddr_data_rate_show(struct device * dev,struct device_attribute * attr,char * buf)405 static ssize_t ddr_data_rate_show(struct device *dev,
406 struct device_attribute *attr,
407 char *buf)
408 {
409 u16 id = 0x0107;
410 u64 resp;
411 int ret;
412
413 ret = processor_thermal_send_mbox_read_cmd(to_pci_dev(dev), id, &resp);
414 if (ret)
415 return ret;
416
417 return sysfs_emit(buf, "%llu\n", resp);
418 }
419
420 static DEVICE_ATTR_RW(rfi_restriction);
421 static DEVICE_ATTR_RO(ddr_data_rate);
422
423 static struct attribute *dvfs_attrs[] = {
424 &dev_attr_rfi_restriction_run_busy.attr,
425 &dev_attr_rfi_restriction_err_code.attr,
426 &dev_attr_rfi_restriction_data_rate.attr,
427 &dev_attr_rfi_restriction_data_rate_base.attr,
428 &dev_attr_ddr_data_rate_point_0.attr,
429 &dev_attr_ddr_data_rate_point_1.attr,
430 &dev_attr_ddr_data_rate_point_2.attr,
431 &dev_attr_ddr_data_rate_point_3.attr,
432 &dev_attr_rfi_disable.attr,
433 &dev_attr_ddr_data_rate.attr,
434 &dev_attr_rfi_restriction.attr,
435 NULL
436 };
437
438 static const struct attribute_group dvfs_attribute_group = {
439 .attrs = dvfs_attrs,
440 .name = "dvfs"
441 };
442
proc_thermal_rfim_add(struct pci_dev * pdev,struct proc_thermal_device * proc_priv)443 int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
444 {
445 int ret;
446
447 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
448 ret = sysfs_create_group(&pdev->dev.kobj, &fivr_attribute_group);
449 if (ret)
450 return ret;
451 }
452
453 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) {
454 switch (pdev->device) {
455 case PCI_DEVICE_ID_INTEL_LNLM_THERMAL:
456 case PCI_DEVICE_ID_INTEL_PTL_THERMAL:
457 case PCI_DEVICE_ID_INTEL_WCL_THERMAL:
458 dlvr_mmio_regs_table = lnl_dlvr_mmio_regs;
459 dlvr_mapping = lnl_dlvr_mapping;
460 break;
461 case PCI_DEVICE_ID_INTEL_NVL_H_THERMAL:
462 case PCI_DEVICE_ID_INTEL_NVL_S_THERMAL:
463 dlvr_mmio_regs_table = nvl_dlvr_mmio_regs;
464 break;
465 default:
466 dlvr_mmio_regs_table = dlvr_mmio_regs;
467 break;
468 }
469 ret = sysfs_create_group(&pdev->dev.kobj, &dlvr_attribute_group);
470 if (ret) {
471 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
472 sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
473 return ret;
474 }
475 }
476
477 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
478 ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group);
479 if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
480 sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
481 return ret;
482 }
483 if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR) {
484 sysfs_remove_group(&pdev->dev.kobj, &dlvr_attribute_group);
485 return ret;
486 }
487 }
488
489 return 0;
490 }
491 EXPORT_SYMBOL_GPL(proc_thermal_rfim_add);
492
proc_thermal_rfim_remove(struct pci_dev * pdev)493 void proc_thermal_rfim_remove(struct pci_dev *pdev)
494 {
495 struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
496
497 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
498 sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
499
500 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DLVR)
501 sysfs_remove_group(&pdev->dev.kobj, &dlvr_attribute_group);
502
503 if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS)
504 sysfs_remove_group(&pdev->dev.kobj, &dvfs_attribute_group);
505 }
506 EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove);
507
508 MODULE_LICENSE("GPL v2");
509 MODULE_DESCRIPTION("Processor Thermal RFIM Interface");
510