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