diff options
Diffstat (limited to 'drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c')
| -rw-r--r-- | drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c | 244 | 
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c new file mode 100644 index 000000000000..aef993a813e2 --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * processor thermal device RFIM control + * Copyright (c) 2020, Intel Corporation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include "processor_thermal_device.h" + +struct mmio_reg { +	int read_only; +	u32 offset; +	int bits; +	u16 mask; +	u16 shift; +}; + +/* These will represent sysfs attribute names */ +static const char * const fivr_strings[] = { +	"vco_ref_code_lo", +	"vco_ref_code_hi", +	"spread_spectrum_pct", +	"spread_spectrum_clk_enable", +	"rfi_vco_ref_code", +	"fivr_fffc_rev", +	NULL +}; + +static const struct mmio_reg tgl_fivr_mmio_regs[] = { +	{ 0, 0x5A18, 3, 0x7, 12}, /* vco_ref_code_lo */ +	{ 0, 0x5A18, 8, 0xFF, 16}, /* vco_ref_code_hi */ +	{ 0, 0x5A08, 8, 0xFF, 0}, /* spread_spectrum_pct */ +	{ 0, 0x5A08, 1, 0x1, 8}, /* spread_spectrum_clk_enable */ +	{ 1, 0x5A10, 12, 0xFFF, 0}, /* rfi_vco_ref_code */ +	{ 1, 0x5A14, 2, 0x3, 1}, /* fivr_fffc_rev */ +}; + +/* These will represent sysfs attribute names */ +static const char * const dvfs_strings[] = { +	"rfi_restriction_run_busy", +	"rfi_restriction_err_code", +	"rfi_restriction_data_rate", +	"rfi_restriction_data_rate_base", +	"ddr_data_rate_point_0", +	"ddr_data_rate_point_1", +	"ddr_data_rate_point_2", +	"ddr_data_rate_point_3", +	"rfi_disable", +	NULL +}; + +static const struct mmio_reg adl_dvfs_mmio_regs[] = { +	{ 0, 0x5A38, 1, 0x1, 31}, /* rfi_restriction_run_busy */ +	{ 0, 0x5A38, 7, 0x7F, 24}, /* rfi_restriction_err_code */ +	{ 0, 0x5A38, 8, 0xFF, 16}, /* rfi_restriction_data_rate */ +	{ 0, 0x5A38, 16, 0xFFFF, 0}, /* rfi_restriction_data_rate_base */ +	{ 0, 0x5A30, 10, 0x3FF, 0}, /* ddr_data_rate_point_0 */ +	{ 0, 0x5A30, 10, 0x3FF, 10}, /* ddr_data_rate_point_1 */ +	{ 0, 0x5A30, 10, 0x3FF, 20}, /* ddr_data_rate_point_2 */ +	{ 0, 0x5A30, 10, 0x3FF, 30}, /* ddr_data_rate_point_3 */ +	{ 0, 0x5A40, 1, 0x1, 0}, /* rfi_disable */ +}; + +#define RFIM_SHOW(suffix, table)\ +static ssize_t suffix##_show(struct device *dev,\ +			      struct device_attribute *attr,\ +			      char *buf)\ +{\ +	struct proc_thermal_device *proc_priv;\ +	struct pci_dev *pdev = to_pci_dev(dev);\ +	const struct mmio_reg *mmio_regs;\ +	const char **match_strs;\ +	u32 reg_val;\ +	int ret;\ +\ +	proc_priv = pci_get_drvdata(pdev);\ +	if (table) {\ +		match_strs = (const char **)dvfs_strings;\ +		mmio_regs = adl_dvfs_mmio_regs;\ +	} else { \ +		match_strs = (const char **)fivr_strings;\ +		mmio_regs = tgl_fivr_mmio_regs;\ +	} \ +	\ +	ret = match_string(match_strs, -1, attr->attr.name);\ +	if (ret < 0)\ +		return ret;\ +	reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\ +	ret = (reg_val >> mmio_regs[ret].shift) & mmio_regs[ret].mask;\ +	return sprintf(buf, "%u\n", ret);\ +} + +#define RFIM_STORE(suffix, table)\ +static ssize_t suffix##_store(struct device *dev,\ +			       struct device_attribute *attr,\ +			       const char *buf, size_t count)\ +{\ +	struct proc_thermal_device *proc_priv;\ +	struct pci_dev *pdev = to_pci_dev(dev);\ +	unsigned int input;\ +	const char **match_strs;\ +	const struct mmio_reg *mmio_regs;\ +	int ret, err;\ +	u32 reg_val;\ +	u32 mask;\ +\ +	proc_priv = pci_get_drvdata(pdev);\ +	if (table) {\ +		match_strs = (const char **)dvfs_strings;\ +		mmio_regs = adl_dvfs_mmio_regs;\ +	} else { \ +		match_strs = (const char **)fivr_strings;\ +		mmio_regs = tgl_fivr_mmio_regs;\ +	} \ +	\ +	ret = match_string(match_strs, -1, attr->attr.name);\ +	if (ret < 0)\ +		return ret;\ +	if (mmio_regs[ret].read_only)\ +		return -EPERM;\ +	err = kstrtouint(buf, 10, &input);\ +	if (err)\ +		return err;\ +	mask = GENMASK(mmio_regs[ret].shift + mmio_regs[ret].bits - 1, mmio_regs[ret].shift);\ +	reg_val = readl((void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\ +	reg_val &= ~mask;\ +	reg_val |= (input << mmio_regs[ret].shift);\ +	writel(reg_val, (void __iomem *) (proc_priv->mmio_base + mmio_regs[ret].offset));\ +	return count;\ +} + +RFIM_SHOW(vco_ref_code_lo, 0) +RFIM_SHOW(vco_ref_code_hi, 0) +RFIM_SHOW(spread_spectrum_pct, 0) +RFIM_SHOW(spread_spectrum_clk_enable, 0) +RFIM_SHOW(rfi_vco_ref_code, 0) +RFIM_SHOW(fivr_fffc_rev, 0) + +RFIM_STORE(vco_ref_code_lo, 0) +RFIM_STORE(vco_ref_code_hi, 0) +RFIM_STORE(spread_spectrum_pct, 0) +RFIM_STORE(spread_spectrum_clk_enable, 0) +RFIM_STORE(rfi_vco_ref_code, 0) +RFIM_STORE(fivr_fffc_rev, 0) + +static DEVICE_ATTR_RW(vco_ref_code_lo); +static DEVICE_ATTR_RW(vco_ref_code_hi); +static DEVICE_ATTR_RW(spread_spectrum_pct); +static DEVICE_ATTR_RW(spread_spectrum_clk_enable); +static DEVICE_ATTR_RW(rfi_vco_ref_code); +static DEVICE_ATTR_RW(fivr_fffc_rev); + +static struct attribute *fivr_attrs[] = { +	&dev_attr_vco_ref_code_lo.attr, +	&dev_attr_vco_ref_code_hi.attr, +	&dev_attr_spread_spectrum_pct.attr, +	&dev_attr_spread_spectrum_clk_enable.attr, +	&dev_attr_rfi_vco_ref_code.attr, +	&dev_attr_fivr_fffc_rev.attr, +	NULL +}; + +static const struct attribute_group fivr_attribute_group = { +	.attrs = fivr_attrs, +	.name = "fivr" +}; + +RFIM_SHOW(rfi_restriction_run_busy, 1) +RFIM_SHOW(rfi_restriction_err_code, 1) +RFIM_SHOW(rfi_restriction_data_rate, 1) +RFIM_SHOW(ddr_data_rate_point_0, 1) +RFIM_SHOW(ddr_data_rate_point_1, 1) +RFIM_SHOW(ddr_data_rate_point_2, 1) +RFIM_SHOW(ddr_data_rate_point_3, 1) +RFIM_SHOW(rfi_disable, 1) + +RFIM_STORE(rfi_restriction_run_busy, 1) +RFIM_STORE(rfi_restriction_err_code, 1) +RFIM_STORE(rfi_restriction_data_rate, 1) +RFIM_STORE(rfi_disable, 1) + +static DEVICE_ATTR_RW(rfi_restriction_run_busy); +static DEVICE_ATTR_RW(rfi_restriction_err_code); +static DEVICE_ATTR_RW(rfi_restriction_data_rate); +static DEVICE_ATTR_RO(ddr_data_rate_point_0); +static DEVICE_ATTR_RO(ddr_data_rate_point_1); +static DEVICE_ATTR_RO(ddr_data_rate_point_2); +static DEVICE_ATTR_RO(ddr_data_rate_point_3); +static DEVICE_ATTR_RW(rfi_disable); + +static struct attribute *dvfs_attrs[] = { +	&dev_attr_rfi_restriction_run_busy.attr, +	&dev_attr_rfi_restriction_err_code.attr, +	&dev_attr_rfi_restriction_data_rate.attr, +	&dev_attr_ddr_data_rate_point_0.attr, +	&dev_attr_ddr_data_rate_point_1.attr, +	&dev_attr_ddr_data_rate_point_2.attr, +	&dev_attr_ddr_data_rate_point_3.attr, +	&dev_attr_rfi_disable.attr, +	NULL +}; + +static const struct attribute_group dvfs_attribute_group = { +	.attrs = dvfs_attrs, +	.name = "dvfs" +}; + +int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) +{ +	int ret; + +	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) { +		ret = sysfs_create_group(&pdev->dev.kobj, &fivr_attribute_group); +		if (ret) +			return ret; +	} + +	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) { +		ret = sysfs_create_group(&pdev->dev.kobj, &dvfs_attribute_group); +		if (ret && proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) { +			sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group); +			return ret; +		} +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(proc_thermal_rfim_add); + +void proc_thermal_rfim_remove(struct pci_dev *pdev) +{ +	struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev); + +	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR) +		sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group); + +	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) +		sysfs_remove_group(&pdev->dev.kobj, &dvfs_attribute_group); +} +EXPORT_SYMBOL_GPL(proc_thermal_rfim_remove); + +MODULE_LICENSE("GPL v2");  | 
