File Index
Files and directories
Sign in
FILE

plr_tpmi.c

File preview with syntax highlighting.

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