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