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