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