305 lines
8.3 KiB
C
305 lines
8.3 KiB
C
// 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"
|
|
|
|
MODULE_IMPORT_NS(INT340X_THERMAL);
|
|
|
|
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, 11}, /* 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(rfi_restriction_data_rate_base, 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_restriction_data_rate_base, 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_RW(rfi_restriction_data_rate_base);
|
|
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 ssize_t rfi_restriction_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
u16 id = 0x0008;
|
|
u32 input;
|
|
int ret;
|
|
|
|
ret = kstrtou32(buf, 10, &input);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = processor_thermal_send_mbox_write_cmd(to_pci_dev(dev), id, input);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t rfi_restriction_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
u16 id = 0x0007;
|
|
u64 resp;
|
|
int ret;
|
|
|
|
ret = processor_thermal_send_mbox_read_cmd(to_pci_dev(dev), id, &resp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "%llu\n", resp);
|
|
}
|
|
|
|
static ssize_t ddr_data_rate_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
u16 id = 0x0107;
|
|
u64 resp;
|
|
int ret;
|
|
|
|
ret = processor_thermal_send_mbox_read_cmd(to_pci_dev(dev), id, &resp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return sprintf(buf, "%llu\n", resp);
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(rfi_restriction);
|
|
static DEVICE_ATTR_RO(ddr_data_rate);
|
|
|
|
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_rfi_restriction_data_rate_base.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,
|
|
&dev_attr_ddr_data_rate.attr,
|
|
&dev_attr_rfi_restriction.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");
|