1473be511SSrinivas Pandruvada // SPDX-License-Identifier: GPL-2.0-only
2473be511SSrinivas Pandruvada /*
3473be511SSrinivas Pandruvada  * processor thermal device RFIM control
4473be511SSrinivas Pandruvada  * Copyright (c) 2020, Intel Corporation.
5473be511SSrinivas Pandruvada  */
6473be511SSrinivas Pandruvada 
7473be511SSrinivas Pandruvada #include <linux/kernel.h>
8473be511SSrinivas Pandruvada #include <linux/module.h>
9473be511SSrinivas Pandruvada #include <linux/pci.h>
10473be511SSrinivas Pandruvada #include "processor_thermal_device.h"
11473be511SSrinivas Pandruvada 
12473be511SSrinivas Pandruvada struct mmio_reg {
13473be511SSrinivas Pandruvada 	int read_only;
14473be511SSrinivas Pandruvada 	u32 offset;
15473be511SSrinivas Pandruvada 	int bits;
16473be511SSrinivas Pandruvada 	u16 mask;
17473be511SSrinivas Pandruvada 	u16 shift;
18473be511SSrinivas Pandruvada };
19473be511SSrinivas Pandruvada 
20473be511SSrinivas Pandruvada /* These will represent sysfs attribute names */
21473be511SSrinivas Pandruvada static const char * const fivr_strings[] = {
22473be511SSrinivas Pandruvada 	"vco_ref_code_lo",
23473be511SSrinivas Pandruvada 	"vco_ref_code_hi",
24473be511SSrinivas Pandruvada 	"spread_spectrum_pct",
25473be511SSrinivas Pandruvada 	"spread_spectrum_clk_enable",
26473be511SSrinivas Pandruvada 	"rfi_vco_ref_code",
27473be511SSrinivas Pandruvada 	"fivr_fffc_rev",
28473be511SSrinivas Pandruvada 	NULL
29473be511SSrinivas Pandruvada };
30473be511SSrinivas Pandruvada 
31473be511SSrinivas Pandruvada static const struct mmio_reg tgl_fivr_mmio_regs[] = {
32*f872f736SSumeet Pawnikar 	{ 0, 0x5A18, 3, 0x7, 11}, /* vco_ref_code_lo */
33473be511SSrinivas Pandruvada 	{ 0, 0x5A18, 8, 0xFF, 16}, /* vco_ref_code_hi */
34473be511SSrinivas Pandruvada 	{ 0, 0x5A08, 8, 0xFF, 0}, /* spread_spectrum_pct */
35473be511SSrinivas Pandruvada 	{ 0, 0x5A08, 1, 0x1, 8}, /* spread_spectrum_clk_enable */
36473be511SSrinivas Pandruvada 	{ 1, 0x5A10, 12, 0xFFF, 0}, /* rfi_vco_ref_code */
37473be511SSrinivas Pandruvada 	{ 1, 0x5A14, 2, 0x3, 1}, /* fivr_fffc_rev */
38473be511SSrinivas Pandruvada };
39473be511SSrinivas Pandruvada 
40473be511SSrinivas Pandruvada /* These will represent sysfs attribute names */
41473be511SSrinivas Pandruvada static const char * const dvfs_strings[] = {
42473be511SSrinivas Pandruvada 	"rfi_restriction_run_busy",
43473be511SSrinivas Pandruvada 	"rfi_restriction_err_code",
44473be511SSrinivas Pandruvada 	"rfi_restriction_data_rate",
45473be511SSrinivas Pandruvada 	"rfi_restriction_data_rate_base",
46473be511SSrinivas Pandruvada 	"ddr_data_rate_point_0",
47473be511SSrinivas Pandruvada 	"ddr_data_rate_point_1",
48473be511SSrinivas Pandruvada 	"ddr_data_rate_point_2",
49473be511SSrinivas Pandruvada 	"ddr_data_rate_point_3",
50473be511SSrinivas Pandruvada 	"rfi_disable",
51473be511SSrinivas Pandruvada 	NULL
52473be511SSrinivas Pandruvada };
53473be511SSrinivas Pandruvada 
54473be511SSrinivas Pandruvada static const struct mmio_reg adl_dvfs_mmio_regs[] = {
55473be511SSrinivas Pandruvada 	{ 0, 0x5A38, 1, 0x1, 31}, /* rfi_restriction_run_busy */
56473be511SSrinivas Pandruvada 	{ 0, 0x5A38, 7, 0x7F, 24}, /* rfi_restriction_err_code */
57473be511SSrinivas Pandruvada 	{ 0, 0x5A38, 8, 0xFF, 16}, /* rfi_restriction_data_rate */
58473be511SSrinivas Pandruvada 	{ 0, 0x5A38, 16, 0xFFFF, 0}, /* rfi_restriction_data_rate_base */
59473be511SSrinivas Pandruvada 	{ 0, 0x5A30, 10, 0x3FF, 0}, /* ddr_data_rate_point_0 */
60473be511SSrinivas Pandruvada 	{ 0, 0x5A30, 10, 0x3FF, 10}, /* ddr_data_rate_point_1 */
61473be511SSrinivas Pandruvada 	{ 0, 0x5A30, 10, 0x3FF, 20}, /* ddr_data_rate_point_2 */
62473be511SSrinivas Pandruvada 	{ 0, 0x5A30, 10, 0x3FF, 30}, /* ddr_data_rate_point_3 */
63473be511SSrinivas Pandruvada 	{ 0, 0x5A40, 1, 0x1, 0}, /* rfi_disable */
64473be511SSrinivas Pandruvada };
65473be511SSrinivas Pandruvada 
66473be511SSrinivas Pandruvada #define RFIM_SHOW(suffix, table)\
67473be511SSrinivas Pandruvada static ssize_t suffix##_show(struct device *dev,\
68473be511SSrinivas Pandruvada 			      struct device_attribute *attr,\
69473be511SSrinivas Pandruvada 			      char *buf)\
70473be511SSrinivas Pandruvada {\
71473be511SSrinivas Pandruvada 	struct proc_thermal_device *proc_priv;\
72473be511SSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(dev);\
73473be511SSrinivas Pandruvada 	const struct mmio_reg *mmio_regs;\
74473be511SSrinivas Pandruvada 	const char **match_strs;\
75473be511SSrinivas Pandruvada 	u32 reg_val;\
76473be511SSrinivas Pandruvada 	int ret;\
77473be511SSrinivas Pandruvada \
78473be511SSrinivas Pandruvada 	proc_priv = pci_get_drvdata(pdev);\
79473be511SSrinivas Pandruvada 	if (table) {\
80473be511SSrinivas Pandruvada 		match_strs = (const char **)dvfs_strings;\
81473be511SSrinivas Pandruvada 		mmio_regs = adl_dvfs_mmio_regs;\
82473be511SSrinivas Pandruvada 	} else { \
83473be511SSrinivas Pandruvada 		match_strs = (const char **)fivr_strings;\
84473be511SSrinivas Pandruvada 		mmio_regs = tgl_fivr_mmio_regs;\
85473be511SSrinivas Pandruvada 	} \
86473be511SSrinivas Pandruvada 	\
87473be511SSrinivas Pandruvada 	ret = match_string(match_strs, -1, attr->attr.name);\
88473be511SSrinivas Pandruvada 	if (ret < 0)\
89473be511SSrinivas Pandruvada 		return ret;\
90473be511SSrinivas Pandruvada 	reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
91473be511SSrinivas Pandruvada 	ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;\
92473be511SSrinivas Pandruvada 	return sprintf(buf, "%u\n", ret);\
93473be511SSrinivas Pandruvada }
94473be511SSrinivas Pandruvada 
95473be511SSrinivas Pandruvada #define RFIM_STORE(suffix, table)\
96473be511SSrinivas Pandruvada static ssize_t suffix##_store(struct device *dev,\
97473be511SSrinivas Pandruvada 			       struct device_attribute *attr,\
98473be511SSrinivas Pandruvada 			       const char *buf, size_t count)\
99473be511SSrinivas Pandruvada {\
100473be511SSrinivas Pandruvada 	struct proc_thermal_device *proc_priv;\
101473be511SSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(dev);\
102473be511SSrinivas Pandruvada 	unsigned int input;\
103473be511SSrinivas Pandruvada 	const char **match_strs;\
104473be511SSrinivas Pandruvada 	const struct mmio_reg *mmio_regs;\
105473be511SSrinivas Pandruvada 	int ret, err;\
106473be511SSrinivas Pandruvada 	u32 reg_val;\
107473be511SSrinivas Pandruvada 	u32 mask;\
108473be511SSrinivas Pandruvada \
109473be511SSrinivas Pandruvada 	proc_priv = pci_get_drvdata(pdev);\
110473be511SSrinivas Pandruvada 	if (table) {\
111473be511SSrinivas Pandruvada 		match_strs = (const char **)dvfs_strings;\
112473be511SSrinivas Pandruvada 		mmio_regs = adl_dvfs_mmio_regs;\
113473be511SSrinivas Pandruvada 	} else { \
114473be511SSrinivas Pandruvada 		match_strs = (const char **)fivr_strings;\
115473be511SSrinivas Pandruvada 		mmio_regs = tgl_fivr_mmio_regs;\
116473be511SSrinivas Pandruvada 	} \
117473be511SSrinivas Pandruvada 	\
118473be511SSrinivas Pandruvada 	ret = match_string(match_strs, -1, attr->attr.name);\
119473be511SSrinivas Pandruvada 	if (ret < 0)\
120473be511SSrinivas Pandruvada 		return ret;\
121473be511SSrinivas Pandruvada 	if (mmio_regs[ret].read_only)\
122473be511SSrinivas Pandruvada 		return -EPERM;\
123473be511SSrinivas Pandruvada 	err = kstrtouint(buf, 10, &input);\
124473be511SSrinivas Pandruvada 	if (err)\
125473be511SSrinivas Pandruvada 		return err;\
126473be511SSrinivas Pandruvada 	mask = GENMASK(mmio_regs[ret].shift + mmio_regs[ret].bits - 1, mmio_regs[ret].shift);\
127473be511SSrinivas Pandruvada 	reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
128473be511SSrinivas Pandruvada 	reg_val &= ~mask;\
129473be511SSrinivas Pandruvada 	reg_val |= (input << mmio_regs[ret].shift);\
130473be511SSrinivas Pandruvada 	writel(reg_val, (void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\
131473be511SSrinivas Pandruvada 	return count;\
132473be511SSrinivas Pandruvada }
133473be511SSrinivas Pandruvada 
134473be511SSrinivas Pandruvada RFIM_SHOW(vco_ref_code_lo, 0)
135473be511SSrinivas Pandruvada RFIM_SHOW(vco_ref_code_hi, 0)
136473be511SSrinivas Pandruvada RFIM_SHOW(spread_spectrum_pct, 0)
137473be511SSrinivas Pandruvada RFIM_SHOW(spread_spectrum_clk_enable, 0)
138473be511SSrinivas Pandruvada RFIM_SHOW(rfi_vco_ref_code, 0)
139473be511SSrinivas Pandruvada RFIM_SHOW(fivr_fffc_rev, 0)
140473be511SSrinivas Pandruvada 
141473be511SSrinivas Pandruvada RFIM_STORE(vco_ref_code_lo, 0)
142473be511SSrinivas Pandruvada RFIM_STORE(vco_ref_code_hi, 0)
143473be511SSrinivas Pandruvada RFIM_STORE(spread_spectrum_pct, 0)
144473be511SSrinivas Pandruvada RFIM_STORE(spread_spectrum_clk_enable, 0)
145473be511SSrinivas Pandruvada RFIM_STORE(rfi_vco_ref_code, 0)
146473be511SSrinivas Pandruvada RFIM_STORE(fivr_fffc_rev, 0)
147473be511SSrinivas Pandruvada 
148473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(vco_ref_code_lo);
149473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(vco_ref_code_hi);
150473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(spread_spectrum_pct);
151473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(spread_spectrum_clk_enable);
152473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(rfi_vco_ref_code);
153473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(fivr_fffc_rev);
154473be511SSrinivas Pandruvada 
155473be511SSrinivas Pandruvada static struct attribute *fivr_attrs[] = {
156473be511SSrinivas Pandruvada 	&dev_attr_vco_ref_code_lo.attr,
157473be511SSrinivas Pandruvada 	&dev_attr_vco_ref_code_hi.attr,
158473be511SSrinivas Pandruvada 	&dev_attr_spread_spectrum_pct.attr,
159473be511SSrinivas Pandruvada 	&dev_attr_spread_spectrum_clk_enable.attr,
160473be511SSrinivas Pandruvada 	&dev_attr_rfi_vco_ref_code.attr,
161473be511SSrinivas Pandruvada 	&dev_attr_fivr_fffc_rev.attr,
162473be511SSrinivas Pandruvada 	NULL
163473be511SSrinivas Pandruvada };
164473be511SSrinivas Pandruvada 
165473be511SSrinivas Pandruvada static const struct attribute_group fivr_attribute_group = {
166473be511SSrinivas Pandruvada 	.attrs = fivr_attrs,
167473be511SSrinivas Pandruvada 	.name = "fivr"
168473be511SSrinivas Pandruvada };
169473be511SSrinivas Pandruvada 
170473be511SSrinivas Pandruvada RFIM_SHOW(rfi_restriction_run_busy, 1)
171473be511SSrinivas Pandruvada RFIM_SHOW(rfi_restriction_err_code, 1)
172473be511SSrinivas Pandruvada RFIM_SHOW(rfi_restriction_data_rate, 1)
173473be511SSrinivas Pandruvada RFIM_SHOW(ddr_data_rate_point_0, 1)
174473be511SSrinivas Pandruvada RFIM_SHOW(ddr_data_rate_point_1, 1)
175473be511SSrinivas Pandruvada RFIM_SHOW(ddr_data_rate_point_2, 1)
176473be511SSrinivas Pandruvada RFIM_SHOW(ddr_data_rate_point_3, 1)
177473be511SSrinivas Pandruvada RFIM_SHOW(rfi_disable, 1)
178473be511SSrinivas Pandruvada 
179473be511SSrinivas Pandruvada RFIM_STORE(rfi_restriction_run_busy, 1)
180473be511SSrinivas Pandruvada RFIM_STORE(rfi_restriction_err_code, 1)
181473be511SSrinivas Pandruvada RFIM_STORE(rfi_restriction_data_rate, 1)
182473be511SSrinivas Pandruvada RFIM_STORE(rfi_disable, 1)
183473be511SSrinivas Pandruvada 
184473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(rfi_restriction_run_busy);
185473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(rfi_restriction_err_code);
186473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(rfi_restriction_data_rate);
187473be511SSrinivas Pandruvada static DEVICE_ATTR_RO(ddr_data_rate_point_0);
188473be511SSrinivas Pandruvada static DEVICE_ATTR_RO(ddr_data_rate_point_1);
189473be511SSrinivas Pandruvada static DEVICE_ATTR_RO(ddr_data_rate_point_2);
190473be511SSrinivas Pandruvada static DEVICE_ATTR_RO(ddr_data_rate_point_3);
191473be511SSrinivas Pandruvada static DEVICE_ATTR_RW(rfi_disable);
192473be511SSrinivas Pandruvada 
1935d6fbc96SSrinivas Pandruvada static ssize_t rfi_restriction_store(struct device *dev,
1945d6fbc96SSrinivas Pandruvada 				     struct device_attribute *attr,
1955d6fbc96SSrinivas Pandruvada 				     const char *buf, size_t count)
1965d6fbc96SSrinivas Pandruvada {
1975d6fbc96SSrinivas Pandruvada 	u16 cmd_id = 0x0008;
198aeb58c86SSrinivas Pandruvada 	u64 cmd_resp;
1995d6fbc96SSrinivas Pandruvada 	u32 input;
2005d6fbc96SSrinivas Pandruvada 	int ret;
2015d6fbc96SSrinivas Pandruvada 
2025d6fbc96SSrinivas Pandruvada 	ret = kstrtou32(buf, 10, &input);
2035d6fbc96SSrinivas Pandruvada 	if (ret)
2045d6fbc96SSrinivas Pandruvada 		return ret;
2055d6fbc96SSrinivas Pandruvada 
2065d6fbc96SSrinivas Pandruvada 	ret = processor_thermal_send_mbox_cmd(to_pci_dev(dev), cmd_id, input, &cmd_resp);
2075d6fbc96SSrinivas Pandruvada 	if (ret)
2085d6fbc96SSrinivas Pandruvada 		return ret;
2095d6fbc96SSrinivas Pandruvada 
2105d6fbc96SSrinivas Pandruvada 	return count;
2115d6fbc96SSrinivas Pandruvada }
2125d6fbc96SSrinivas Pandruvada 
2135d6fbc96SSrinivas Pandruvada static ssize_t rfi_restriction_show(struct device *dev,
2145d6fbc96SSrinivas Pandruvada 				    struct device_attribute *attr,
2155d6fbc96SSrinivas Pandruvada 				    char *buf)
2165d6fbc96SSrinivas Pandruvada {
2175d6fbc96SSrinivas Pandruvada 	u16 cmd_id = 0x0007;
218aeb58c86SSrinivas Pandruvada 	u64 cmd_resp;
2195d6fbc96SSrinivas Pandruvada 	int ret;
2205d6fbc96SSrinivas Pandruvada 
2215d6fbc96SSrinivas Pandruvada 	ret = processor_thermal_send_mbox_cmd(to_pci_dev(dev), cmd_id, 0, &cmd_resp);
2225d6fbc96SSrinivas Pandruvada 	if (ret)
2235d6fbc96SSrinivas Pandruvada 		return ret;
2245d6fbc96SSrinivas Pandruvada 
225aeb58c86SSrinivas Pandruvada 	return sprintf(buf, "%llu\n", cmd_resp);
2265d6fbc96SSrinivas Pandruvada }
2275d6fbc96SSrinivas Pandruvada 
2285d6fbc96SSrinivas Pandruvada static ssize_t ddr_data_rate_show(struct device *dev,
2295d6fbc96SSrinivas Pandruvada 				  struct device_attribute *attr,
2305d6fbc96SSrinivas Pandruvada 				  char *buf)
2315d6fbc96SSrinivas Pandruvada {
2325d6fbc96SSrinivas Pandruvada 	u16 cmd_id = 0x0107;
233aeb58c86SSrinivas Pandruvada 	u64 cmd_resp;
2345d6fbc96SSrinivas Pandruvada 	int ret;
2355d6fbc96SSrinivas Pandruvada 
2365d6fbc96SSrinivas Pandruvada 	ret = processor_thermal_send_mbox_cmd(to_pci_dev(dev), cmd_id, 0, &cmd_resp);
2375d6fbc96SSrinivas Pandruvada 	if (ret)
2385d6fbc96SSrinivas Pandruvada 		return ret;
2395d6fbc96SSrinivas Pandruvada 
240aeb58c86SSrinivas Pandruvada 	return sprintf(buf, "%llu\n", cmd_resp);
2415d6fbc96SSrinivas Pandruvada }
2425d6fbc96SSrinivas Pandruvada 
2435d6fbc96SSrinivas Pandruvada static DEVICE_ATTR_RW(rfi_restriction);
2445d6fbc96SSrinivas Pandruvada static DEVICE_ATTR_RO(ddr_data_rate);
2455d6fbc96SSrinivas Pandruvada 
246473be511SSrinivas Pandruvada static struct attribute *dvfs_attrs[] = {
247473be511SSrinivas Pandruvada 	&dev_attr_rfi_restriction_run_busy.attr,
248473be511SSrinivas Pandruvada 	&dev_attr_rfi_restriction_err_code.attr,
249473be511SSrinivas Pandruvada 	&dev_attr_rfi_restriction_data_rate.attr,
250473be511SSrinivas Pandruvada 	&dev_attr_ddr_data_rate_point_0.attr,
251473be511SSrinivas Pandruvada 	&dev_attr_ddr_data_rate_point_1.attr,
252473be511SSrinivas Pandruvada 	&dev_attr_ddr_data_rate_point_2.attr,
253473be511SSrinivas Pandruvada 	&dev_attr_ddr_data_rate_point_3.attr,
254473be511SSrinivas Pandruvada 	&dev_attr_rfi_disable.attr,
2555d6fbc96SSrinivas Pandruvada 	&dev_attr_ddr_data_rate.attr,
2565d6fbc96SSrinivas Pandruvada 	&dev_attr_rfi_restriction.attr,
257473be511SSrinivas Pandruvada 	NULL
258473be511SSrinivas Pandruvada };
259473be511SSrinivas Pandruvada 
260473be511SSrinivas Pandruvada static const struct attribute_group dvfs_attribute_group = {
261473be511SSrinivas Pandruvada 	.attrs = dvfs_attrs,
262473be511SSrinivas Pandruvada 	.name = "dvfs"
263473be511SSrinivas Pandruvada };
264473be511SSrinivas Pandruvada 
265473be511SSrinivas Pandruvada int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
266473be511SSrinivas Pandruvada {
267473be511SSrinivas Pandruvada 	int ret;
268473be511SSrinivas Pandruvada 
269473be511SSrinivas Pandruvada 	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
270473be511SSrinivas Pandruvada 		ret = sysfs_create_group(&pdev->dev.kobj, &fivr_attribute_group);
271473be511SSrinivas Pandruvada 		if (ret)
272473be511SSrinivas Pandruvada 			return ret;
273473be511SSrinivas Pandruvada 	}
274473be511SSrinivas Pandruvada 
275473be511SSrinivas Pandruvada 	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
276473be511SSrinivas Pandruvada 		ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group);
277473be511SSrinivas Pandruvada 		if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) {
278473be511SSrinivas Pandruvada 			sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
279473be511SSrinivas Pandruvada 			return ret;
280473be511SSrinivas Pandruvada 		}
281473be511SSrinivas Pandruvada 	}
282473be511SSrinivas Pandruvada 
283473be511SSrinivas Pandruvada 	return 0;
284473be511SSrinivas Pandruvada }
285473be511SSrinivas Pandruvada EXPORT_SYMBOL_GPL(proc_thermal_rfim_add);
286473be511SSrinivas Pandruvada 
287473be511SSrinivas Pandruvada void proc_thermal_rfim_remove(struct pci_dev *pdev)
288473be511SSrinivas Pandruvada {
289473be511SSrinivas Pandruvada 	struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
290473be511SSrinivas Pandruvada 
291473be511SSrinivas Pandruvada 	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
292473be511SSrinivas Pandruvada 		sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
293473be511SSrinivas Pandruvada 
294473be511SSrinivas Pandruvada 	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS)
295473be511SSrinivas Pandruvada 		sysfs_remove_group(&pdev->dev.kobj, &dvfs_attribute_group);
296473be511SSrinivas Pandruvada }
297473be511SSrinivas Pandruvada EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove);
298473be511SSrinivas Pandruvada 
299473be511SSrinivas Pandruvada MODULE_LICENSE("GPL v2");
300