1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel PMC SSRAM TELEMETRY PCI Driver
 *
 * Copyright (c) 2023, Intel Corporation.
 */

#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/intel_vsec.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/io-64-nonatomic-lo-hi.h>

#include "core.h"
#include "ssram_telemetry.h"

#define SSRAM_HDR_SIZE		0x100
#define SSRAM_PWRM_OFFSET	0x14
#define SSRAM_DVSEC_OFFSET	0x1C
#define SSRAM_DVSEC_SIZE	0x10
#define SSRAM_PCH_OFFSET	0x60
#define SSRAM_IOE_OFFSET	0x68
#define SSRAM_DEVID_OFFSET	0x70
#define SSRAM_BASE_ADDR_MASK	GENMASK_ULL(63, 3)
#define SSRAM_PCI_PMC_MASK	(BIT(PMC_IDX_MAIN) | BIT(PMC_IDX_IOE) | BIT(PMC_IDX_PCH))

DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T))

enum resource_method {
	RES_METHOD_PCI,
	RES_METHOD_ACPI,
};

struct ssram_type {
	enum resource_method method;
	enum pmc_index p_index;
};

static const struct ssram_type pci_main = {
	.method = RES_METHOD_PCI,
	.p_index = PMC_IDX_MAIN,
};

static const struct ssram_type acpi_main = {
	.method = RES_METHOD_ACPI,
	.p_index = PMC_IDX_MAIN,
};

static const struct ssram_type acpi_pch = {
	.method = RES_METHOD_ACPI,
	.p_index = PMC_IDX_PCH,
};

enum pmc_ssram_state {
	PMC_SSRAM_UNPROBED = 0,
	PMC_SSRAM_PROBING,
	PMC_SSRAM_PRESENT,
	PMC_SSRAM_ABSENT,
};

static enum pmc_ssram_state pmc_ssram_state[MAX_NUM_PMC];
static struct pmc_ssram_telemetry pmc_ssram_telems[MAX_NUM_PMC];

struct pmc_ssram_probe_cache {
	/* Per-index values staged by probe before publishing globally. */
	struct pmc_ssram_telemetry telems[MAX_NUM_PMC];
	/* PMCs this probe instance is responsible for publishing. */
	unsigned long owned_mask;
	/* Subset of owned_mask that was discovered successfully. */
	unsigned long valid_mask;
};

struct pmc_ssram_drvdata {
	/* PMCs published by this bound device; used to unpublish on remove. */
	unsigned long owned_mask;
};

static inline u64 get_base(void __iomem *addr, u32 offset)
{
	return lo_hi_readq(addr + offset) & SSRAM_BASE_ADDR_MASK;
}

static void pmc_ssram_get_devid_pwrmbase(struct pmc_ssram_probe_cache *probe_cache,
					 void __iomem *ssram, unsigned int pmc_idx)
{
	u64 pwrm_base;
	u16 devid;

	pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET);
	devid = readw(ssram + SSRAM_DEVID_OFFSET);

	probe_cache->telems[pmc_idx].base_addr = pwrm_base;
	probe_cache->telems[pmc_idx].devid = devid;
}

static void pmc_ssram_publish_absent(unsigned int pmc_idx)
{
	/*
	 * Publish only the state without modifying base_addr and devid. This
	 * lets a reader that already observed PRESENT finish copying the
	 * previous values even if unbind concurrently publishes ABSENT. Readers
	 * that observe ABSENT return -ENODEV without accessing data.
	 */
	smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_ABSENT);
}

static void pmc_ssram_publish_present(struct pmc_ssram_probe_cache *probe_cache,
				      unsigned int pmc_idx)
{
	/*
	 * The devid and base_addr fields are read from hardware MMIO registers
	 * whose values are stable for a given PMC index. A reader that observed
	 * PRESENT from an earlier probe can safely copy them while a concurrent
	 * rebind republishes those fields because both probes read the same
	 * hardware values.
	 */
	pmc_ssram_telems[pmc_idx] = probe_cache->telems[pmc_idx];
	/*
	 * Publish base_addr and devid before publishing PRESENT. Pairs with the
	 * acquire load in the reader that consumes them after observing PRESENT.
	 */
	smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_PRESENT);
}

static void pmc_ssram_mark_probing(unsigned long mask)
{
	unsigned long bit;

	for_each_set_bit(bit, &mask, MAX_NUM_PMC)
		WRITE_ONCE(pmc_ssram_state[bit], PMC_SSRAM_PROBING);
}

static int
pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram)
{
	struct intel_vsec_platform_info info = {};
	struct intel_vsec_header *headers[2] = {};
	struct intel_vsec_header header;
	void __iomem *dvsec;
	u32 dvsec_offset;
	u32 table, hdr;

	dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET);
	dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE);
	if (!dvsec)
		return -ENOMEM;

	hdr = readl(dvsec + PCI_DVSEC_HEADER1);
	header.id = readw(dvsec + PCI_DVSEC_HEADER2);
	header.rev = PCI_DVSEC_HEADER1_REV(hdr);
	header.length = PCI_DVSEC_HEADER1_LEN(hdr);
	header.num_entries = readb(dvsec + INTEL_DVSEC_ENTRIES);
	header.entry_size = readb(dvsec + INTEL_DVSEC_SIZE);

	table = readl(dvsec + INTEL_DVSEC_TABLE);
	header.tbir = INTEL_DVSEC_TABLE_BAR(table);
	header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
	iounmap(dvsec);

	headers[0] = &header;
	info.caps = VSEC_CAP_TELEMETRY;
	info.headers = headers;
	info.base_addr = ssram_base;
	info.parent = &pcidev->dev;

	return intel_vsec_register(&pcidev->dev, &info);
}

static int
pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev,
				struct pmc_ssram_probe_cache *probe_cache,
				unsigned int pmc_idx, u32 offset)
{
	void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL;
	void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL;
	u64 ssram_base;
	int ret;

	ssram_base = pci_resource_start(pcidev, 0);
	tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
	if (!tmp_ssram)
		return -ENOMEM;

	if (pmc_idx != PMC_IDX_MAIN) {
		/*
		 * The secondary PMC BARS (which are behind hidden PCI devices)
		 * are read from fixed offsets in MMIO of the primary PMC BAR.
		 * If a device is not present, the value will be 0.
		 */
		ssram_base = get_base(tmp_ssram, offset);
		if (!ssram_base)
			return 0;

		ssram = ioremap(ssram_base, SSRAM_HDR_SIZE);
		if (!ssram)
			return -ENOMEM;

	} else {
		ssram = no_free_ptr(tmp_ssram);
	}

	pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx);

	/* Find and register and PMC telemetry entries */
	ret = pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram);
	if (ret)
		dev_warn(&pcidev->dev, "could not register PMT\n");

	probe_cache->valid_mask |= BIT(pmc_idx);

	return 0;
}

static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev,
					struct pmc_ssram_probe_cache *probe_cache)
{
	int ret;

	ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_MAIN, 0);
	if (ret)
		return ret;

	/*
	 * If MAIN PMC enumeration is successful but either IOE or PCH fail,
	 * don't fail probe as the MAIN PMC is still useful as it provides the
	 * global reset and slp_s0 counter access. Failed or missing secondary
	 * PMCs are left out of valid_mask and published as absent.
	 */
	pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_IOE,
					SSRAM_IOE_OFFSET);

	pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_PCH,
					SSRAM_PCH_OFFSET);

	return 0;
}

static int pmc_ssram_telemetry_get_pmc_acpi(struct pci_dev *pcidev,
					    struct pmc_ssram_probe_cache *probe_cache,
					    unsigned int pmc_idx)
{
	u64 ssram_base;

	ssram_base = pci_resource_start(pcidev, 0);
	if (!ssram_base)
		return -ENODEV;

	void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram =
		ioremap(ssram_base, SSRAM_HDR_SIZE);
	if (!ssram)
		return -ENOMEM;

	pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx);
	probe_cache->valid_mask |= BIT(pmc_idx);

	return 0;
}

static int pmc_ssram_telemetry_acpi_init(struct pci_dev *pcidev,
					 struct pmc_ssram_probe_cache *probe_cache,
					 enum pmc_index index)
{
	struct intel_vsec_header header;
	struct intel_vsec_header *headers[2] = { &header, NULL };
	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
	struct intel_vsec_platform_info info = { };
	union acpi_object *dsd;
	acpi_handle handle;
	acpi_status status;
	int ret;

	handle = ACPI_HANDLE(&pcidev->dev);
	if (!handle)
		return -ENODEV;

	status = acpi_evaluate_object(handle, "_DSD", NULL, &buf);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	void *dsd_buf __free(pmc_acpi_free) = buf.pointer;

	dsd = pmc_find_telem_guid(dsd_buf);
	if (!dsd)
		return -ENODEV;

	acpi_disc_t disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header);
	if (IS_ERR(disc))
		return PTR_ERR(disc);

	info.headers = headers;
	info.caps = VSEC_CAP_TELEMETRY;
	info.acpi_disc = disc;
	info.src = INTEL_VSEC_DISC_ACPI;

	/* This is an ACPI companion device. PCI BAR will be used for base addr. */
	info.base_addr = 0;

	ret = intel_vsec_register(&pcidev->dev, &info);
	if (ret)
		dev_warn(&pcidev->dev, "could not register PMT\n");

	return pmc_ssram_telemetry_get_pmc_acpi(pcidev, probe_cache, index);
}

/**
 * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information
 * @pmc_idx:               Index of the PMC
 * @pmc_ssram_telemetry:   pmc_ssram_telemetry structure to store the PMC information
 *
 * State flow per PMC index:
 * - PMC_SSRAM_UNPROBED: Probe has not started for this index.
 * - PMC_SSRAM_PROBING:  Probe is currently discovering data for this index.
 * - PMC_SSRAM_PRESENT:  base_addr/devid were published and can be read.
 * - PMC_SSRAM_ABSENT:   No data is available for this index.
 *
 * Readers use an acquire load of the state. If state is PRESENT, reads of
 * base_addr/devid are ordered after the state observation and pair with the
 * writer's release-store when publishing PRESENT.
 *
 * Return:
 * * 0           - Success
 * * -EAGAIN     - Probe function has not finished yet. Try again.
 * * -EINVAL     - Invalid pmc_idx
 * * -ENODEV     - PMC device is not available (hardware absent or driver failed to initialize)
 */
int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx,
				     struct pmc_ssram_telemetry *pmc_ssram_telemetry)
{
	enum pmc_ssram_state state;

	if (pmc_idx >= MAX_NUM_PMC)
		return -EINVAL;

	/*
	 * PMCs are discovered in probe function. If this function is called before
	 * probe function complete, the result would be invalid. Use per-PMC state
	 * to inform the consumer to call again later.
	 */
	state = smp_load_acquire(&pmc_ssram_state[pmc_idx]);
	if (state == PMC_SSRAM_UNPROBED || state == PMC_SSRAM_PROBING)
		return -EAGAIN;

	if (state == PMC_SSRAM_ABSENT)
		return -ENODEV;

	/*
	 * Acquire semantics order reads of devid and base_addr after observing
	 * PRESENT and pair with the writer's release-store.
	 */
	pmc_ssram_telemetry->devid = pmc_ssram_telems[pmc_idx].devid;
	pmc_ssram_telemetry->base_addr = pmc_ssram_telems[pmc_idx].base_addr;
	return 0;
}
EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info);

static void pmc_ssram_publish_absent_mask(unsigned long mask)
{
	unsigned long bit;

	for_each_set_bit(bit, &mask, MAX_NUM_PMC)
		pmc_ssram_publish_absent(bit);
}

static void pmc_ssram_publish_telems(struct pmc_ssram_probe_cache *probe_cache, int ret)
{
	unsigned long bit;

	/* If probe failed, all owned indexes become absent for readers. */
	if (ret) {
		pmc_ssram_publish_absent_mask(probe_cache->owned_mask);
		return;
	}

	/* Publish each owned index independently based on discovery result. */
	for_each_set_bit(bit, &probe_cache->owned_mask, MAX_NUM_PMC) {
		if (probe_cache->valid_mask & BIT(bit))
			pmc_ssram_publish_present(probe_cache, bit);
		else
			pmc_ssram_publish_absent(bit);
	}
}

static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id)
{
	struct pmc_ssram_probe_cache probe_cache = {};
	struct pmc_ssram_drvdata *drvdata;
	const struct ssram_type *ssram_type;
	enum resource_method method;
	enum pmc_index index;
	int ret;

	ssram_type = (const struct ssram_type *)id->driver_data;
	if (!ssram_type) {
		dev_dbg(&pcidev->dev, "missing driver data\n");
		return -EINVAL;
	}

	index = ssram_type->p_index;
	method = ssram_type->method;
	if (method == RES_METHOD_PCI)
		probe_cache.owned_mask = SSRAM_PCI_PMC_MASK;
	else if (method == RES_METHOD_ACPI)
		probe_cache.owned_mask = BIT(index);
	else
		return -EINVAL;

	pmc_ssram_mark_probing(probe_cache.owned_mask);

	drvdata = devm_kzalloc(&pcidev->dev, sizeof(*drvdata), GFP_KERNEL);
	if (!drvdata) {
		ret = -ENOMEM;
		goto probe_finish;
	}

	ret = pcim_enable_device(pcidev);
	if (ret) {
		dev_dbg(&pcidev->dev, "failed to enable PMC SSRAM device\n");
		goto probe_finish;
	}

	if (method == RES_METHOD_PCI)
		ret = pmc_ssram_telemetry_pci_init(pcidev, &probe_cache);
	else if (method == RES_METHOD_ACPI)
		ret = pmc_ssram_telemetry_acpi_init(pcidev, &probe_cache, index);
	else
		ret = -EINVAL;

	if (!ret) {
		drvdata->owned_mask = probe_cache.owned_mask;
		pci_set_drvdata(pcidev, drvdata);
	}

probe_finish:
	pmc_ssram_publish_telems(&probe_cache, ret);

	return ret;
}

static void pmc_ssram_telemetry_remove(struct pci_dev *pcidev)
{
	struct pmc_ssram_drvdata *drvdata = pci_get_drvdata(pcidev);

	if (drvdata)
		pmc_ssram_publish_absent_mask(drvdata->owned_mask);
}

static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN),
		.driver_data = (kernel_ulong_t)&pci_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDH),
		.driver_data = (kernel_ulong_t)&acpi_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDS),
		.driver_data = (kernel_ulong_t)&acpi_main },
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCHS),
		.driver_data = (kernel_ulong_t)&acpi_pch },
	{ }
};
MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids);

static struct pci_driver pmc_ssram_telemetry_driver = {
	.name = "intel_pmc_ssram_telemetry",
	.id_table = pmc_ssram_telemetry_pci_ids,
	.probe = pmc_ssram_telemetry_probe,
	.remove = pmc_ssram_telemetry_remove,
};
module_pci_driver(pmc_ssram_telemetry_driver);

MODULE_IMPORT_NS("INTEL_VSEC");
MODULE_IMPORT_NS("INTEL_PMC_CORE");
MODULE_AUTHOR("Xi Pardee <xi.pardee@intel.com>");
MODULE_DESCRIPTION("Intel PMC SSRAM Telemetry driver");
MODULE_LICENSE("GPL");