Skip to content

Commit 473be51

Browse files
spandruvadadlezcano
authored andcommitted
thermal: int340x: processor_thermal: Add RFIM driver
Add support for RFIM (Radio Frequency Interference Mitigation) support via processor thermal PCI device. This drivers allows adjustment of FIVR (Fully Integrated Voltage Regulator) and DDR (Double Data Rate) frequencies to avoid RF interference with WiFi and 5G. Switching voltage regulators (VR) generate radiated EMI or RFI at the fundamental frequency and its harmonics. Some harmonics may interfere with very sensitive wireless receivers such as Wi-Fi and cellular that are integrated into host systems like notebook PCs. One of mitigation methods is requesting SOC integrated VR (IVR) switching frequency to a small % and shift away the switching noise harmonic interference from radio channels. OEM or ODMs can use the driver to control SOC IVR operation within the range where it does not impact IVR performance. DRAM devices of DDR IO interface and their power plane can generate EMI at the data rates. Similar to IVR control mechanism, Intel offers a mechanism by which DDR data rates can be changed if several conditions are met: there is strong RFI interference because of DDR; CPU power management has no other restriction in changing DDR data rates; PC ODMs enable this feature (real time DDR RFI Mitigation referred to as DDR-RFIM) for Wi-Fi from BIOS. This change exports two folders under /sys/bus/pci/devices/0000:00:04.0. One folder "fivr" contains all attributes exposed for controling FIVR features. The other folder "dvfs" contains all attributes for DDR features. Changes done to implement: - New module for rfim interfaces - Two new per processor features for DDR and FIVR - Enable feature for Tiger Lake (FIVR only) and Alder Lake The attributes exposed and explanation: FIVR attributes vco_ref_code_lo (RW): The VCO reference code is an 11-bit field and controls the FIVR switching frequency. This is the 3-bit LSB field. vco_ref_code_hi (RW): The VCO reference code is an 11-bit field and controls the FIVR switching frequency. This is the 8-bit MSB field. spread_spectrum_pct (RW): Set the FIVR spread spectrum clocking percentage spread_spectrum_clk_enable (RW): Enable/disable of the FIVR spread spectrum clocking feature rfi_vco_ref_code (RW): This field is a read only status register which reflects the current FIVR switching frequency fivr_fffc_rev (RW): This field indicated the revision of the FIVR HW. DVFS attributes rfi_restriction_run_busy (RW): Request the restriction of specific DDR data rate and set this value 1. Self reset to 0 after operation. rfi_restriction_err_code (RW): Values: 0 :Request is accepted, 1:Feature disabled, 2: the request restricts more points than it is allowed rfi_restriction_data_rate_Delta (RW): Restricted DDR data rate for RFI protection: Lower Limit rfi_restriction_data_rate_Base (RW): Restricted DDR data rate for RFI protection: Upper Limit ddr_data_rate_point_0 (RO): DDR data rate selection 1st point ddr_data_rate_point_1 (RO): DDR data rate selection 2nd point ddr_data_rate_point_2 (RO): DDR data rate selection 3rd point ddr_data_rate_point_3 (RO): DDR data rate selection 4th point rfi_disable (RW): Disable DDR rate change feature Signed-off-by: Srinivas Pandruvada <[email protected]> Signed-off-by: Daniel Lezcano <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent df2537f commit 473be51

4 files changed

Lines changed: 270 additions & 3 deletions

File tree

drivers/thermal/intel/int340x_thermal/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
55
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
66
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
77
obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o
8+
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_rfim.o
89
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
910
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

drivers/thermal/intel/int340x_thermal/processor_thermal_device.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,8 @@ static int proc_thermal_mmio_add(struct pci_dev *pdev,
429429
{
430430
int ret;
431431

432+
proc_priv->mmio_feature_mask = feature_mask;
433+
432434
if (feature_mask) {
433435
ret = proc_thermal_set_mmio_base(pdev, proc_priv);
434436
if (ret)
@@ -443,9 +445,21 @@ static int proc_thermal_mmio_add(struct pci_dev *pdev,
443445
}
444446
}
445447

446-
proc_priv->mmio_feature_mask = feature_mask;
448+
if (feature_mask & PROC_THERMAL_FEATURE_FIVR ||
449+
feature_mask & PROC_THERMAL_FEATURE_DVFS) {
450+
ret = proc_thermal_rfim_add(pdev, proc_priv);
451+
if (ret) {
452+
dev_err(&pdev->dev, "failed to add RFIM interface\n");
453+
goto err_rem_rapl;
454+
}
455+
}
447456

448457
return 0;
458+
459+
err_rem_rapl:
460+
proc_thermal_rapl_remove();
461+
462+
return ret;
449463
}
450464

451465
static void proc_thermal_mmio_remove(struct pci_dev *pdev)
@@ -455,6 +469,9 @@ static void proc_thermal_mmio_remove(struct pci_dev *pdev)
455469
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_RAPL)
456470
proc_thermal_rapl_remove();
457471

472+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR ||
473+
proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS)
474+
proc_thermal_rfim_remove(pdev);
458475
}
459476

460477
static int proc_thermal_pci_probe(struct pci_dev *pdev,
@@ -566,7 +583,7 @@ static int proc_thermal_resume(struct device *dev)
566583
static SIMPLE_DEV_PM_OPS(proc_thermal_pm, NULL, proc_thermal_resume);
567584

568585
static const struct pci_device_id proc_thermal_pci_ids[] = {
569-
{ PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
586+
{ PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS) },
570587
{ PCI_DEVICE_DATA(INTEL, BDW_THERMAL, 0) },
571588
{ PCI_DEVICE_DATA(INTEL, BSW_THERMAL, 0) },
572589
{ PCI_DEVICE_DATA(INTEL, BXT0_THERMAL, 0) },
@@ -580,7 +597,7 @@ static const struct pci_device_id proc_thermal_pci_ids[] = {
580597
{ PCI_DEVICE_DATA(INTEL, ICL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
581598
{ PCI_DEVICE_DATA(INTEL, JSL_THERMAL, 0) },
582599
{ PCI_DEVICE_DATA(INTEL, SKL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
583-
{ PCI_DEVICE_DATA(INTEL, TGL_THERMAL, PROC_THERMAL_FEATURE_RAPL) },
600+
{ PCI_DEVICE_DATA(INTEL, TGL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR) },
584601
{ },
585602
};
586603

drivers/thermal/intel/int340x_thermal/processor_thermal_device.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ struct rapl_mmio_regs {
5454

5555
#define PROC_THERMAL_FEATURE_NONE 0x00
5656
#define PROC_THERMAL_FEATURE_RAPL 0x01
57+
#define PROC_THERMAL_FEATURE_FIVR 0x02
58+
#define PROC_THERMAL_FEATURE_DVFS 0x04
5759

5860
#if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL)
5961
int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
@@ -70,4 +72,7 @@ static void __maybe_unused proc_thermal_rapl_remove(void)
7072
}
7173
#endif
7274

75+
int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv);
76+
void proc_thermal_rfim_remove(struct pci_dev *pdev);
77+
7378
#endif
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
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+
struct mmio_reg {
13+
int read_only;
14+
u32 offset;
15+
int bits;
16+
u16 mask;
17+
u16 shift;
18+
};
19+
20+
/* These will represent sysfs attribute names */
21+
static const char * const fivr_strings[] = {
22+
"vco_ref_code_lo",
23+
"vco_ref_code_hi",
24+
"spread_spectrum_pct",
25+
"spread_spectrum_clk_enable",
26+
"rfi_vco_ref_code",
27+
"fivr_fffc_rev",
28+
NULL
29+
};
30+
31+
static const struct mmio_reg tgl_fivr_mmio_regs[] = {
32+
{ 0, 0x5A18, 3, 0x7, 12}, /* vco_ref_code_lo */
33+
{ 0, 0x5A18, 8, 0xFF, 16}, /* vco_ref_code_hi */
34+
{ 0, 0x5A08, 8, 0xFF, 0}, /* spread_spectrum_pct */
35+
{ 0, 0x5A08, 1, 0x1, 8}, /* spread_spectrum_clk_enable */
36+
{ 1, 0x5A10, 12, 0xFFF, 0}, /* rfi_vco_ref_code */
37+
{ 1, 0x5A14, 2, 0x3, 1}, /* fivr_fffc_rev */
38+
};
39+
40+
/* These will represent sysfs attribute names */
41+
static const char * const dvfs_strings[] = {
42+
"rfi_restriction_run_busy",
43+
"rfi_restriction_err_code",
44+
"rfi_restriction_data_rate",
45+
"rfi_restriction_data_rate_base",
46+
"ddr_data_rate_point_0",
47+
"ddr_data_rate_point_1",
48+
"ddr_data_rate_point_2",
49+
"ddr_data_rate_point_3",
50+
"rfi_disable",
51+
NULL
52+
};
53+
54+
static const struct mmio_reg adl_dvfs_mmio_regs[] = {
55+
{ 0, 0x5A38, 1, 0x1, 31}, /* rfi_restriction_run_busy */
56+
{ 0, 0x5A38, 7, 0x7F, 24}, /* rfi_restriction_err_code */
57+
{ 0, 0x5A38, 8, 0xFF, 16}, /* rfi_restriction_data_rate */
58+
{ 0, 0x5A38, 16, 0xFFFF, 0}, /* rfi_restriction_data_rate_base */
59+
{ 0, 0x5A30, 10, 0x3FF, 0}, /* ddr_data_rate_point_0 */
60+
{ 0, 0x5A30, 10, 0x3FF, 10}, /* ddr_data_rate_point_1 */
61+
{ 0, 0x5A30, 10, 0x3FF, 20}, /* ddr_data_rate_point_2 */
62+
{ 0, 0x5A30, 10, 0x3FF, 30}, /* ddr_data_rate_point_3 */
63+
{ 0, 0x5A40, 1, 0x1, 0}, /* rfi_disable */
64+
};
65+
66+
#define RFIM_SHOW(suffix, table)\
67+
static ssize_t suffix##_show(struct device *dev,\
68+
struct device_attribute *attr,\
69+
char *buf)\
70+
{\
71+
struct proc_thermal_device *proc_priv;\
72+
struct pci_dev *pdev = to_pci_dev(dev);\
73+
const struct mmio_reg *mmio_regs;\
74+
const char **match_strs;\
75+
u32 reg_val;\
76+
int ret;\
77+
\
78+
proc_priv = pci_get_drvdata(pdev);\
79+
if (table) {\
80+
match_strs = (const char **)dvfs_strings;\
81+
mmio_regs = adl_dvfs_mmio_regs;\
82+
} else { \
83+
match_strs = (const char **)fivr_strings;\
84+
mmio_regs = tgl_fivr_mmio_regs;\
85+
} \
86+
\
87+
ret = match_string(match_strs, -1, attr->attr.name);\
88+
if (ret < 0)\
89+
return ret;\
90+
reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
91+
ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;\
92+
return sprintf(buf, "%u\n", ret);\
93+
}
94+
95+
#define RFIM_STORE(suffix, table)\
96+
static ssize_t suffix##_store(struct device *dev,\
97+
struct device_attribute *attr,\
98+
const char *buf, size_t count)\
99+
{\
100+
struct proc_thermal_device *proc_priv;\
101+
struct pci_dev *pdev = to_pci_dev(dev);\
102+
unsigned int input;\
103+
const char **match_strs;\
104+
const struct mmio_reg *mmio_regs;\
105+
int ret, err;\
106+
u32 reg_val;\
107+
u32 mask;\
108+
\
109+
proc_priv = pci_get_drvdata(pdev);\
110+
if (table) {\
111+
match_strs = (const char **)dvfs_strings;\
112+
mmio_regs = adl_dvfs_mmio_regs;\
113+
} else { \
114+
match_strs = (const char **)fivr_strings;\
115+
mmio_regs = tgl_fivr_mmio_regs;\
116+
} \
117+
\
118+
ret = match_string(match_strs, -1, attr->attr.name);\
119+
if (ret < 0)\
120+
return ret;\
121+
if (mmio_regs[ret].read_only)\
122+
return -EPERM;\
123+
err = kstrtouint(buf, 10, &input);\
124+
if (err)\
125+
return err;\
126+
mask = GENMASK(mmio_regs[ret].shift + mmio_regs[ret].bits - 1, mmio_regs[ret].shift);\
127+
reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
128+
reg_val &= ~mask;\
129+
reg_val |= (input << mmio_regs[ret].shift);\
130+
writel(reg_val, (void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
131+
return count;\
132+
}
133+
134+
RFIM_SHOW(vco_ref_code_lo, 0)
135+
RFIM_SHOW(vco_ref_code_hi, 0)
136+
RFIM_SHOW(spread_spectrum_pct, 0)
137+
RFIM_SHOW(spread_spectrum_clk_enable, 0)
138+
RFIM_SHOW(rfi_vco_ref_code, 0)
139+
RFIM_SHOW(fivr_fffc_rev, 0)
140+
141+
RFIM_STORE(vco_ref_code_lo, 0)
142+
RFIM_STORE(vco_ref_code_hi, 0)
143+
RFIM_STORE(spread_spectrum_pct, 0)
144+
RFIM_STORE(spread_spectrum_clk_enable, 0)
145+
RFIM_STORE(rfi_vco_ref_code, 0)
146+
RFIM_STORE(fivr_fffc_rev, 0)
147+
148+
static DEVICE_ATTR_RW(vco_ref_code_lo);
149+
static DEVICE_ATTR_RW(vco_ref_code_hi);
150+
static DEVICE_ATTR_RW(spread_spectrum_pct);
151+
static DEVICE_ATTR_RW(spread_spectrum_clk_enable);
152+
static DEVICE_ATTR_RW(rfi_vco_ref_code);
153+
static DEVICE_ATTR_RW(fivr_fffc_rev);
154+
155+
static struct attribute *fivr_attrs[] = {
156+
&dev_attr_vco_ref_code_lo.attr,
157+
&dev_attr_vco_ref_code_hi.attr,
158+
&dev_attr_spread_spectrum_pct.attr,
159+
&dev_attr_spread_spectrum_clk_enable.attr,
160+
&dev_attr_rfi_vco_ref_code.attr,
161+
&dev_attr_fivr_fffc_rev.attr,
162+
NULL
163+
};
164+
165+
static const struct attribute_group fivr_attribute_group = {
166+
.attrs = fivr_attrs,
167+
.name = "fivr"
168+
};
169+
170+
RFIM_SHOW(rfi_restriction_run_busy, 1)
171+
RFIM_SHOW(rfi_restriction_err_code, 1)
172+
RFIM_SHOW(rfi_restriction_data_rate, 1)
173+
RFIM_SHOW(ddr_data_rate_point_0, 1)
174+
RFIM_SHOW(ddr_data_rate_point_1, 1)
175+
RFIM_SHOW(ddr_data_rate_point_2, 1)
176+
RFIM_SHOW(ddr_data_rate_point_3, 1)
177+
RFIM_SHOW(rfi_disable, 1)
178+
179+
RFIM_STORE(rfi_restriction_run_busy, 1)
180+
RFIM_STORE(rfi_restriction_err_code, 1)
181+
RFIM_STORE(rfi_restriction_data_rate, 1)
182+
RFIM_STORE(rfi_disable, 1)
183+
184+
static DEVICE_ATTR_RW(rfi_restriction_run_busy);
185+
static DEVICE_ATTR_RW(rfi_restriction_err_code);
186+
static DEVICE_ATTR_RW(rfi_restriction_data_rate);
187+
static DEVICE_ATTR_RO(ddr_data_rate_point_0);
188+
static DEVICE_ATTR_RO(ddr_data_rate_point_1);
189+
static DEVICE_ATTR_RO(ddr_data_rate_point_2);
190+
static DEVICE_ATTR_RO(ddr_data_rate_point_3);
191+
static DEVICE_ATTR_RW(rfi_disable);
192+
193+
static struct attribute *dvfs_attrs[] = {
194+
&dev_attr_rfi_restriction_run_busy.attr,
195+
&dev_attr_rfi_restriction_err_code.attr,
196+
&dev_attr_rfi_restriction_data_rate.attr,
197+
&dev_attr_ddr_data_rate_point_0.attr,
198+
&dev_attr_ddr_data_rate_point_1.attr,
199+
&dev_attr_ddr_data_rate_point_2.attr,
200+
&dev_attr_ddr_data_rate_point_3.attr,
201+
&dev_attr_rfi_disable.attr,
202+
NULL
203+
};
204+
205+
static const struct attribute_group dvfs_attribute_group = {
206+
.attrs = dvfs_attrs,
207+
.name = "dvfs"
208+
};
209+
210+
int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
211+
{
212+
int ret;
213+
214+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
215+
ret = sysfs_create_group(&pdev->dev.kobj, &fivr_attribute_group);
216+
if (ret)
217+
return ret;
218+
}
219+
220+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
221+
ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group);
222+
if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
223+
sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
224+
return ret;
225+
}
226+
}
227+
228+
return 0;
229+
}
230+
EXPORT_SYMBOL_GPL(proc_thermal_rfim_add);
231+
232+
void proc_thermal_rfim_remove(struct pci_dev *pdev)
233+
{
234+
struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
235+
236+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
237+
sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
238+
239+
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS)
240+
sysfs_remove_group(&pdev->dev.kobj, &dvfs_attribute_group);
241+
}
242+
EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove);
243+
244+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)