xref: /linux/drivers/platform/x86/intel/pmc/pwrm_telemetry.c (revision f31c00c377ccf07c85442712f7c940a855cb3371)
1*adc5d98dSDavid E. Box // SPDX-License-Identifier: GPL-2.0-only
2*adc5d98dSDavid E. Box /*
3*adc5d98dSDavid E. Box  * Intel PMC PWRM ACPI driver
4*adc5d98dSDavid E. Box  *
5*adc5d98dSDavid E. Box  * Copyright (C) 2025, Intel Corporation
6*adc5d98dSDavid E. Box  */
7*adc5d98dSDavid E. Box 
8*adc5d98dSDavid E. Box #include <linux/acpi.h>
9*adc5d98dSDavid E. Box #include <linux/bits.h>
10*adc5d98dSDavid E. Box #include <linux/bitfield.h>
11*adc5d98dSDavid E. Box #include <linux/cleanup.h>
12*adc5d98dSDavid E. Box #include <linux/device.h>
13*adc5d98dSDavid E. Box #include <linux/err.h>
14*adc5d98dSDavid E. Box #include <linux/errno.h>
15*adc5d98dSDavid E. Box #include <linux/intel_vsec.h>
16*adc5d98dSDavid E. Box #include <linux/limits.h>
17*adc5d98dSDavid E. Box #include <linux/mod_devicetable.h>
18*adc5d98dSDavid E. Box #include <linux/module.h>
19*adc5d98dSDavid E. Box #include <linux/platform_device.h>
20*adc5d98dSDavid E. Box #include <linux/resource.h>
21*adc5d98dSDavid E. Box #include <linux/slab.h>
22*adc5d98dSDavid E. Box #include <linux/types.h>
23*adc5d98dSDavid E. Box #include <linux/uuid.h>
24*adc5d98dSDavid E. Box 
25*adc5d98dSDavid E. Box #include "core.h"
26*adc5d98dSDavid E. Box 
27*adc5d98dSDavid E. Box #define ENTRY_LEN		5
28*adc5d98dSDavid E. Box 
29*adc5d98dSDavid E. Box /* DWORD2 */
30*adc5d98dSDavid E. Box #define DVSEC_ID_MASK		GENMASK(15, 0)
31*adc5d98dSDavid E. Box #define NUM_ENTRIES_MASK	GENMASK(23, 16)
32*adc5d98dSDavid E. Box #define ENTRY_SIZE_MASK		GENMASK(31, 24)
33*adc5d98dSDavid E. Box 
34*adc5d98dSDavid E. Box /* DWORD3 */
35*adc5d98dSDavid E. Box #define TBIR_MASK		GENMASK(2, 0)
36*adc5d98dSDavid E. Box #define DISC_TBL_OFF_MASK	GENMASK(31, 3)
37*adc5d98dSDavid E. Box 
38*adc5d98dSDavid E. Box const guid_t intel_vsec_guid =
39*adc5d98dSDavid E. Box 	GUID_INIT(0x294903fb, 0x634d, 0x4fc7, 0xaf, 0x1f, 0x0f, 0xb9,
40*adc5d98dSDavid E. Box 		  0x56, 0xb0, 0x4f, 0xc1);
41*adc5d98dSDavid E. Box 
42*adc5d98dSDavid E. Box static bool is_valid_entry(union acpi_object *pkg)
43*adc5d98dSDavid E. Box {
44*adc5d98dSDavid E. Box 	int i;
45*adc5d98dSDavid E. Box 
46*adc5d98dSDavid E. Box 	if (!pkg || pkg->type != ACPI_TYPE_PACKAGE || pkg->package.count != ENTRY_LEN)
47*adc5d98dSDavid E. Box 		return false;
48*adc5d98dSDavid E. Box 
49*adc5d98dSDavid E. Box 	if (pkg->package.elements[0].type != ACPI_TYPE_STRING)
50*adc5d98dSDavid E. Box 		return false;
51*adc5d98dSDavid E. Box 
52*adc5d98dSDavid E. Box 	for (i = 1; i < ENTRY_LEN; i++)
53*adc5d98dSDavid E. Box 		if (pkg->package.elements[i].type != ACPI_TYPE_INTEGER)
54*adc5d98dSDavid E. Box 			return false;
55*adc5d98dSDavid E. Box 
56*adc5d98dSDavid E. Box 	return true;
57*adc5d98dSDavid E. Box }
58*adc5d98dSDavid E. Box 
59*adc5d98dSDavid E. Box acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj,
60*adc5d98dSDavid E. Box 			struct intel_vsec_header *header)
61*adc5d98dSDavid E. Box {
62*adc5d98dSDavid E. Box 	union acpi_object *vsec_pkg;
63*adc5d98dSDavid E. Box 	union acpi_object *disc_pkg;
64*adc5d98dSDavid E. Box 	u64 hdr0, hdr1;
65*adc5d98dSDavid E. Box 	int num_regions;
66*adc5d98dSDavid E. Box 	int i;
67*adc5d98dSDavid E. Box 
68*adc5d98dSDavid E. Box 	if (!header)
69*adc5d98dSDavid E. Box 		return ERR_PTR(-EINVAL);
70*adc5d98dSDavid E. Box 
71*adc5d98dSDavid E. Box 	if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2)
72*adc5d98dSDavid E. Box 		return ERR_PTR(-EINVAL);
73*adc5d98dSDavid E. Box 
74*adc5d98dSDavid E. Box 	/* First Package is DVSEC info */
75*adc5d98dSDavid E. Box 	vsec_pkg = &obj->package.elements[0];
76*adc5d98dSDavid E. Box 	if (!is_valid_entry(vsec_pkg))
77*adc5d98dSDavid E. Box 		return ERR_PTR(-EINVAL);
78*adc5d98dSDavid E. Box 
79*adc5d98dSDavid E. Box 	hdr0 = vsec_pkg->package.elements[3].integer.value;
80*adc5d98dSDavid E. Box 	hdr1 = vsec_pkg->package.elements[4].integer.value;
81*adc5d98dSDavid E. Box 
82*adc5d98dSDavid E. Box 	header->id          = FIELD_GET(DVSEC_ID_MASK, hdr0);
83*adc5d98dSDavid E. Box 	header->num_entries = FIELD_GET(NUM_ENTRIES_MASK, hdr0);
84*adc5d98dSDavid E. Box 	header->entry_size  = FIELD_GET(ENTRY_SIZE_MASK, hdr0);
85*adc5d98dSDavid E. Box 	header->tbir        = FIELD_GET(TBIR_MASK, hdr1);
86*adc5d98dSDavid E. Box 	header->offset      = hdr1 & DISC_TBL_OFF_MASK;
87*adc5d98dSDavid E. Box 
88*adc5d98dSDavid E. Box 	/* Second Package contains the discovery tables */
89*adc5d98dSDavid E. Box 	disc_pkg = &obj->package.elements[1];
90*adc5d98dSDavid E. Box 	if (disc_pkg->type != ACPI_TYPE_PACKAGE || disc_pkg->package.count < 1)
91*adc5d98dSDavid E. Box 		return ERR_PTR(-EINVAL);
92*adc5d98dSDavid E. Box 
93*adc5d98dSDavid E. Box 	num_regions = disc_pkg->package.count;
94*adc5d98dSDavid E. Box 	if (header->num_entries != num_regions)
95*adc5d98dSDavid E. Box 		return ERR_PTR(-EINVAL);
96*adc5d98dSDavid E. Box 
97*adc5d98dSDavid E. Box 	acpi_disc_t disc __free(kfree) = kmalloc_array(num_regions, sizeof(*disc),
98*adc5d98dSDavid E. Box 						      GFP_KERNEL);
99*adc5d98dSDavid E. Box 	if (!disc)
100*adc5d98dSDavid E. Box 		return ERR_PTR(-ENOMEM);
101*adc5d98dSDavid E. Box 
102*adc5d98dSDavid E. Box 	for (i = 0; i < num_regions; i++) {
103*adc5d98dSDavid E. Box 		union acpi_object *pkg;
104*adc5d98dSDavid E. Box 		u64 value;
105*adc5d98dSDavid E. Box 		int j;
106*adc5d98dSDavid E. Box 
107*adc5d98dSDavid E. Box 		pkg = &disc_pkg->package.elements[i];
108*adc5d98dSDavid E. Box 		if (!is_valid_entry(pkg))
109*adc5d98dSDavid E. Box 			return ERR_PTR(-EINVAL);
110*adc5d98dSDavid E. Box 
111*adc5d98dSDavid E. Box 		/* Element 0 is a descriptive string; DWORD values start at index 1. */
112*adc5d98dSDavid E. Box 		for (j = 1; j < ENTRY_LEN; j++) {
113*adc5d98dSDavid E. Box 			value = pkg->package.elements[j].integer.value;
114*adc5d98dSDavid E. Box 			if (value > U32_MAX)
115*adc5d98dSDavid E. Box 				return ERR_PTR(-ERANGE);
116*adc5d98dSDavid E. Box 
117*adc5d98dSDavid E. Box 			disc[i][j - 1] = value;
118*adc5d98dSDavid E. Box 		}
119*adc5d98dSDavid E. Box 	}
120*adc5d98dSDavid E. Box 
121*adc5d98dSDavid E. Box 	return no_free_ptr(disc);
122*adc5d98dSDavid E. Box }
123*adc5d98dSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmc_parse_telem_dsd, "INTEL_PMC_CORE");
124*adc5d98dSDavid E. Box 
125*adc5d98dSDavid E. Box union acpi_object *pmc_find_telem_guid(union acpi_object *dsd)
126*adc5d98dSDavid E. Box {
127*adc5d98dSDavid E. Box 	int i;
128*adc5d98dSDavid E. Box 
129*adc5d98dSDavid E. Box 	if (!dsd || dsd->type != ACPI_TYPE_PACKAGE)
130*adc5d98dSDavid E. Box 		return NULL;
131*adc5d98dSDavid E. Box 
132*adc5d98dSDavid E. Box 	for (i = 0; i + 1 < dsd->package.count; i += 2) {
133*adc5d98dSDavid E. Box 		union acpi_object *uuid_obj, *data_obj;
134*adc5d98dSDavid E. Box 		guid_t uuid;
135*adc5d98dSDavid E. Box 
136*adc5d98dSDavid E. Box 		uuid_obj = &dsd->package.elements[i];
137*adc5d98dSDavid E. Box 		data_obj = &dsd->package.elements[i + 1];
138*adc5d98dSDavid E. Box 
139*adc5d98dSDavid E. Box 		if (uuid_obj->type != ACPI_TYPE_BUFFER ||
140*adc5d98dSDavid E. Box 		    uuid_obj->buffer.length != 16)
141*adc5d98dSDavid E. Box 			continue;
142*adc5d98dSDavid E. Box 
143*adc5d98dSDavid E. Box 		memcpy(&uuid, uuid_obj->buffer.pointer, 16);
144*adc5d98dSDavid E. Box 		if (guid_equal(&uuid, &intel_vsec_guid))
145*adc5d98dSDavid E. Box 			return data_obj;
146*adc5d98dSDavid E. Box 	}
147*adc5d98dSDavid E. Box 
148*adc5d98dSDavid E. Box 	return NULL;
149*adc5d98dSDavid E. Box }
150*adc5d98dSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmc_find_telem_guid, "INTEL_PMC_CORE");
151*adc5d98dSDavid E. Box 
152*adc5d98dSDavid E. Box static int pmc_pwrm_acpi_probe(struct platform_device *pdev)
153*adc5d98dSDavid E. Box {
154*adc5d98dSDavid E. Box 	struct intel_vsec_header header;
155*adc5d98dSDavid E. Box 	struct intel_vsec_header *headers[2] = { &header, NULL };
156*adc5d98dSDavid E. Box 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
157*adc5d98dSDavid E. Box 	struct intel_vsec_platform_info info = { };
158*adc5d98dSDavid E. Box 	struct device *dev = &pdev->dev;
159*adc5d98dSDavid E. Box 	union acpi_object *dsd;
160*adc5d98dSDavid E. Box 	struct resource *res;
161*adc5d98dSDavid E. Box 	acpi_handle handle;
162*adc5d98dSDavid E. Box 	acpi_status status;
163*adc5d98dSDavid E. Box 
164*adc5d98dSDavid E. Box 	handle = ACPI_HANDLE(&pdev->dev);
165*adc5d98dSDavid E. Box 	if (!handle)
166*adc5d98dSDavid E. Box 		return -ENODEV;
167*adc5d98dSDavid E. Box 
168*adc5d98dSDavid E. Box 	status = acpi_evaluate_object(handle, "_DSD", NULL, &buf);
169*adc5d98dSDavid E. Box 	if (ACPI_FAILURE(status)) {
170*adc5d98dSDavid E. Box 		return dev_err_probe(dev, -ENODEV, "Could not evaluate _DSD: %s\n",
171*adc5d98dSDavid E. Box 				     acpi_format_exception(status));
172*adc5d98dSDavid E. Box 	}
173*adc5d98dSDavid E. Box 
174*adc5d98dSDavid E. Box 	void *dsd_buf __free(pmc_acpi_free) = buf.pointer;
175*adc5d98dSDavid E. Box 
176*adc5d98dSDavid E. Box 	dsd = pmc_find_telem_guid(dsd_buf);
177*adc5d98dSDavid E. Box 	if (!dsd)
178*adc5d98dSDavid E. Box 		return -ENODEV;
179*adc5d98dSDavid E. Box 
180*adc5d98dSDavid E. Box 	acpi_disc_t acpi_disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header);
181*adc5d98dSDavid E. Box 	if (IS_ERR(acpi_disc))
182*adc5d98dSDavid E. Box 		return PTR_ERR(acpi_disc);
183*adc5d98dSDavid E. Box 
184*adc5d98dSDavid E. Box 	res = platform_get_resource(pdev, IORESOURCE_MEM, header.tbir);
185*adc5d98dSDavid E. Box 	if (!res)
186*adc5d98dSDavid E. Box 		return -EINVAL;
187*adc5d98dSDavid E. Box 
188*adc5d98dSDavid E. Box 	info.headers = headers;
189*adc5d98dSDavid E. Box 	info.caps = VSEC_CAP_TELEMETRY;
190*adc5d98dSDavid E. Box 	info.acpi_disc = acpi_disc;
191*adc5d98dSDavid E. Box 	info.src = INTEL_VSEC_DISC_ACPI;
192*adc5d98dSDavid E. Box 	info.base_addr = res->start;
193*adc5d98dSDavid E. Box 
194*adc5d98dSDavid E. Box 	return intel_vsec_register(&pdev->dev, &info);
195*adc5d98dSDavid E. Box }
196*adc5d98dSDavid E. Box 
197*adc5d98dSDavid E. Box static const struct acpi_device_id pmc_pwrm_acpi_ids[] = {
198*adc5d98dSDavid E. Box 	{ "INTC1122", 0 }, /* Nova Lake */
199*adc5d98dSDavid E. Box 	{ "INTC1129", 0 }, /* Nova Lake */
200*adc5d98dSDavid E. Box 	{ }
201*adc5d98dSDavid E. Box };
202*adc5d98dSDavid E. Box MODULE_DEVICE_TABLE(acpi, pmc_pwrm_acpi_ids);
203*adc5d98dSDavid E. Box 
204*adc5d98dSDavid E. Box static struct platform_driver pmc_pwrm_acpi_driver = {
205*adc5d98dSDavid E. Box 	.probe = pmc_pwrm_acpi_probe,
206*adc5d98dSDavid E. Box 	.driver = {
207*adc5d98dSDavid E. Box 		.name = "intel_pmc_pwrm_acpi",
208*adc5d98dSDavid E. Box 		.acpi_match_table = ACPI_PTR(pmc_pwrm_acpi_ids),
209*adc5d98dSDavid E. Box 	},
210*adc5d98dSDavid E. Box };
211*adc5d98dSDavid E. Box module_platform_driver(pmc_pwrm_acpi_driver);
212*adc5d98dSDavid E. Box 
213*adc5d98dSDavid E. Box MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
214*adc5d98dSDavid E. Box MODULE_DESCRIPTION("Intel PMC PWRM ACPI driver");
215*adc5d98dSDavid E. Box MODULE_LICENSE("GPL");
216*adc5d98dSDavid E. Box MODULE_IMPORT_NS("INTEL_VSEC");
217