The existing mail mechanism only supports writing of workload types.
However, mailbox command for RFIM (cmd = 0x08) also requires write
operation which is ignored. This results in failing to store RFI
restriction.
Fixint this requires enhancing mailbox writes for non workload
commands too, so remove the check for MBOX_CMD_WORKLOAD_TYPE_WRITE
in mailbox write to allow this other write commands to be supoorted.
At the same time, however, we have to make sure that there is no
impact on read commands, by avoiding to write anything into the
mailbox data register.
To properly implement that, add two separate functions for mbox read
and write commands for the processor thermal workload command type.
This helps to distinguish the read and write workload command types
from each other while sending mbox commands.
Fixes: 5d6fbc96bd
("thermal/drivers/int340x: processor_thermal: Export additional attributes")
Signed-off-by: Sumeet Pawnikar <sumeet.r.pawnikar@intel.com>
Cc: 5.14+ <stable@vger.kernel.org> # 5.14+
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
[ rjw: Changelog edits ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
300 lines
8.1 KiB
C
300 lines
8.1 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(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 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_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");
|