355 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Performance Limit Reasons via TPMI4 *5 * Copyright (c) 2024, Intel Corporation.6 */7 8#include <linux/array_size.h>9#include <linux/auxiliary_bus.h>10#include <linux/bitfield.h>11#include <linux/bitmap.h>12#include <linux/debugfs.h>13#include <linux/device.h>14#include <linux/err.h>15#include <linux/gfp_types.h>16#include <linux/intel_tpmi.h>17#include <linux/io.h>18#include <linux/iopoll.h>19#include <linux/kstrtox.h>20#include <linux/lockdep.h>21#include <linux/module.h>22#include <linux/mod_devicetable.h>23#include <linux/mutex.h>24#include <linux/seq_file.h>25#include <linux/sprintf.h>26#include <linux/types.h>27 28#include "tpmi_power_domains.h"29 30#define PLR_HEADER 0x0031#define PLR_MAILBOX_INTERFACE 0x0832#define PLR_MAILBOX_DATA 0x1033#define PLR_DIE_LEVEL 0x1834 35#define PLR_MODULE_ID_MASK GENMASK_ULL(19, 12)36#define PLR_RUN_BUSY BIT_ULL(63)37 38#define PLR_COMMAND_WRITE 139 40#define PLR_INVALID GENMASK_ULL(63, 0)41 42#define PLR_TIMEOUT_US 543#define PLR_TIMEOUT_MAX_US 100044 45#define PLR_COARSE_REASON_BITS 3246 47struct tpmi_plr;48 49struct tpmi_plr_die {50 void __iomem *base;51 struct mutex lock; /* Protect access to PLR mailbox */52 int package_id;53 int die_id;54 struct tpmi_plr *plr;55};56 57struct tpmi_plr {58 struct dentry *dbgfs_dir;59 struct tpmi_plr_die *die_info;60 int num_dies;61 struct auxiliary_device *auxdev;62};63 64static const char * const plr_coarse_reasons[] = {65 "FREQUENCY",66 "CURRENT",67 "POWER",68 "THERMAL",69 "PLATFORM",70 "MCP",71 "RAS",72 "MISC",73 "QOS",74 "DFC",75};76 77static const char * const plr_fine_reasons[] = {78 "FREQUENCY_CDYN0",79 "FREQUENCY_CDYN1",80 "FREQUENCY_CDYN2",81 "FREQUENCY_CDYN3",82 "FREQUENCY_CDYN4",83 "FREQUENCY_CDYN5",84 "FREQUENCY_FCT",85 "FREQUENCY_PCS_TRL",86 "CURRENT_MTPMAX",87 "POWER_FAST_RAPL",88 "POWER_PKG_PL1_MSR_TPMI",89 "POWER_PKG_PL1_MMIO",90 "POWER_PKG_PL1_PCS",91 "POWER_PKG_PL2_MSR_TPMI",92 "POWER_PKG_PL2_MMIO",93 "POWER_PKG_PL2_PCS",94 "POWER_PLATFORM_PL1_MSR_TPMI",95 "POWER_PLATFORM_PL1_MMIO",96 "POWER_PLATFORM_PL1_PCS",97 "POWER_PLATFORM_PL2_MSR_TPMI",98 "POWER_PLATFORM_PL2_MMIO",99 "POWER_PLATFORM_PL2_PCS",100 "UNKNOWN(22)",101 "THERMAL_PER_CORE",102 "DFC_UFS",103 "PLATFORM_PROCHOT",104 "PLATFORM_HOT_VR",105 "UNKNOWN(27)",106 "UNKNOWN(28)",107 "MISC_PCS_PSTATE",108};109 110static u64 plr_read(struct tpmi_plr_die *plr_die, int offset)111{112 return readq(plr_die->base + offset);113}114 115static void plr_write(u64 val, struct tpmi_plr_die *plr_die, int offset)116{117 writeq(val, plr_die->base + offset);118}119 120static int plr_read_cpu_status(struct tpmi_plr_die *plr_die, int cpu,121 u64 *status)122{123 u64 regval;124 int ret;125 126 lockdep_assert_held(&plr_die->lock);127 128 regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu));129 regval |= PLR_RUN_BUSY;130 131 plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE);132 133 ret = readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval,134 !(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US,135 PLR_TIMEOUT_MAX_US);136 if (ret)137 return ret;138 139 *status = plr_read(plr_die, PLR_MAILBOX_DATA);140 141 return 0;142}143 144static int plr_clear_cpu_status(struct tpmi_plr_die *plr_die, int cpu)145{146 u64 regval;147 148 lockdep_assert_held(&plr_die->lock);149 150 regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu));151 regval |= PLR_RUN_BUSY | PLR_COMMAND_WRITE;152 153 plr_write(0, plr_die, PLR_MAILBOX_DATA);154 155 plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE);156 157 return readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval,158 !(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US,159 PLR_TIMEOUT_MAX_US);160}161 162static void plr_print_bits(struct seq_file *s, u64 val, int bits)163{164 const unsigned long mask[] = { BITMAP_FROM_U64(val) };165 int bit, index;166 167 for_each_set_bit(bit, mask, bits) {168 const char *str = NULL;169 170 if (bit < PLR_COARSE_REASON_BITS) {171 if (bit < ARRAY_SIZE(plr_coarse_reasons))172 str = plr_coarse_reasons[bit];173 } else {174 index = bit - PLR_COARSE_REASON_BITS;175 if (index < ARRAY_SIZE(plr_fine_reasons))176 str = plr_fine_reasons[index];177 }178 179 if (str)180 seq_printf(s, " %s", str);181 else182 seq_printf(s, " UNKNOWN(%d)", bit);183 }184 185 if (!val)186 seq_puts(s, " none");187 188 seq_putc(s, '\n');189}190 191static int plr_status_show(struct seq_file *s, void *unused)192{193 struct tpmi_plr_die *plr_die = s->private;194 int ret;195 u64 val;196 197 val = plr_read(plr_die, PLR_DIE_LEVEL);198 seq_puts(s, "cpus");199 plr_print_bits(s, val, 32);200 201 guard(mutex)(&plr_die->lock);202 203 for (int cpu = 0; cpu < nr_cpu_ids; cpu++) {204 if (plr_die->die_id != tpmi_get_power_domain_id(cpu))205 continue;206 207 if (plr_die->package_id != topology_physical_package_id(cpu))208 continue;209 210 seq_printf(s, "cpu%d", cpu);211 ret = plr_read_cpu_status(plr_die, cpu, &val);212 if (ret) {213 dev_err(&plr_die->plr->auxdev->dev, "Failed to read PLR for cpu %d, ret=%d\n",214 cpu, ret);215 return ret;216 }217 218 plr_print_bits(s, val, 64);219 }220 221 return 0;222}223 224static ssize_t plr_status_write(struct file *filp, const char __user *ubuf,225 size_t count, loff_t *ppos)226{227 struct seq_file *s = filp->private_data;228 struct tpmi_plr_die *plr_die = s->private;229 bool val;230 int ret;231 232 ret = kstrtobool_from_user(ubuf, count, &val);233 if (ret)234 return ret;235 236 if (val != 0)237 return -EINVAL;238 239 plr_write(0, plr_die, PLR_DIE_LEVEL);240 241 guard(mutex)(&plr_die->lock);242 243 for (int cpu = 0; cpu < nr_cpu_ids; cpu++) {244 if (plr_die->die_id != tpmi_get_power_domain_id(cpu))245 continue;246 247 if (plr_die->package_id != topology_physical_package_id(cpu))248 continue;249 250 plr_clear_cpu_status(plr_die, cpu);251 }252 253 return count;254}255DEFINE_SHOW_STORE_ATTRIBUTE(plr_status);256 257static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)258{259 struct intel_tpmi_plat_info *plat_info;260 struct dentry *dentry;261 int i, num_resources;262 struct resource *res;263 struct tpmi_plr *plr;264 void __iomem *base;265 char name[16];266 int err;267 268 plat_info = tpmi_get_platform_data(auxdev);269 if (!plat_info)270 return dev_err_probe(&auxdev->dev, -EINVAL, "No platform info\n");271 272 dentry = tpmi_get_debugfs_dir(auxdev);273 if (!dentry)274 return dev_err_probe(&auxdev->dev, -ENODEV, "No TPMI debugfs directory.\n");275 276 num_resources = tpmi_get_resource_count(auxdev);277 if (!num_resources)278 return -EINVAL;279 280 plr = devm_kzalloc(&auxdev->dev, sizeof(*plr), GFP_KERNEL);281 if (!plr)282 return -ENOMEM;283 284 plr->die_info = devm_kcalloc(&auxdev->dev, num_resources, sizeof(*plr->die_info),285 GFP_KERNEL);286 if (!plr->die_info)287 return -ENOMEM;288 289 plr->num_dies = num_resources;290 plr->dbgfs_dir = debugfs_create_dir("plr", dentry);291 plr->auxdev = auxdev;292 293 for (i = 0; i < num_resources; i++) {294 res = tpmi_get_resource_at_index(auxdev, i);295 if (!res) {296 err = dev_err_probe(&auxdev->dev, -EINVAL, "No resource\n");297 goto err;298 }299 300 base = devm_ioremap_resource(&auxdev->dev, res);301 if (IS_ERR(base)) {302 err = PTR_ERR(base);303 goto err;304 }305 306 plr->die_info[i].base = base;307 plr->die_info[i].package_id = plat_info->package_id;308 plr->die_info[i].die_id = i;309 plr->die_info[i].plr = plr;310 mutex_init(&plr->die_info[i].lock);311 312 if (plr_read(&plr->die_info[i], PLR_HEADER) == PLR_INVALID)313 continue;314 315 snprintf(name, sizeof(name), "domain%d", i);316 317 dentry = debugfs_create_dir(name, plr->dbgfs_dir);318 debugfs_create_file("status", 0444, dentry, &plr->die_info[i],319 &plr_status_fops);320 }321 322 auxiliary_set_drvdata(auxdev, plr);323 324 return 0;325 326err:327 debugfs_remove_recursive(plr->dbgfs_dir);328 return err;329}330 331static void intel_plr_remove(struct auxiliary_device *auxdev)332{333 struct tpmi_plr *plr = auxiliary_get_drvdata(auxdev);334 335 debugfs_remove_recursive(plr->dbgfs_dir);336}337 338static const struct auxiliary_device_id intel_plr_id_table[] = {339 { .name = "intel_vsec.tpmi-plr" },340 {}341};342MODULE_DEVICE_TABLE(auxiliary, intel_plr_id_table);343 344static struct auxiliary_driver intel_plr_aux_driver = {345 .id_table = intel_plr_id_table,346 .remove = intel_plr_remove,347 .probe = intel_plr_probe,348};349module_auxiliary_driver(intel_plr_aux_driver);350 351MODULE_IMPORT_NS(INTEL_TPMI);352MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN);353MODULE_DESCRIPTION("Intel TPMI PLR Driver");354MODULE_LICENSE("GPL");355