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