// SPDX-License-Identifier: GPL-2.0-only /* * processor thermal device RFIM control * Copyright (c) 2020, Intel Corporation. */ #include #include #include #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");