1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Intel Vendor Specific Extended Capabilities auxiliary bus driver 4 * 5 * Copyright (c) 2021, Intel Corporation. 6 * All Rights Reserved. 7 * 8 * Author: David E. Box <david.e.box@linux.intel.com> 9 * 10 * This driver discovers and creates auxiliary devices for Intel defined PCIe 11 * "Vendor Specific" and "Designated Vendor Specific" Extended Capabilities, 12 * VSEC and DVSEC respectively. The driver supports features on specific PCIe 13 * endpoints that exist primarily to expose them. 14 */ 15 16 #include <linux/auxiliary_bus.h> 17 #include <linux/bits.h> 18 #include <linux/cleanup.h> 19 #include <linux/delay.h> 20 #include <linux/idr.h> 21 #include <linux/intel_vsec.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include <linux/pci.h> 25 #include <linux/types.h> 26 27 #define PMT_XA_START 0 28 #define PMT_XA_MAX INT_MAX 29 #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) 30 31 static DEFINE_IDA(intel_vsec_ida); 32 static DEFINE_IDA(intel_vsec_sdsi_ida); 33 static DEFINE_XARRAY_ALLOC(auxdev_array); 34 35 static const char *intel_vsec_name(enum intel_vsec_id id) 36 { 37 switch (id) { 38 case VSEC_ID_TELEMETRY: 39 return "telemetry"; 40 41 case VSEC_ID_WATCHER: 42 return "watcher"; 43 44 case VSEC_ID_CRASHLOG: 45 return "crashlog"; 46 47 case VSEC_ID_SDSI: 48 return "sdsi"; 49 50 case VSEC_ID_TPMI: 51 return "tpmi"; 52 53 default: 54 return NULL; 55 } 56 } 57 58 static bool intel_vsec_supported(u16 id, unsigned long caps) 59 { 60 switch (id) { 61 case VSEC_ID_TELEMETRY: 62 return !!(caps & VSEC_CAP_TELEMETRY); 63 case VSEC_ID_WATCHER: 64 return !!(caps & VSEC_CAP_WATCHER); 65 case VSEC_ID_CRASHLOG: 66 return !!(caps & VSEC_CAP_CRASHLOG); 67 case VSEC_ID_SDSI: 68 return !!(caps & VSEC_CAP_SDSI); 69 case VSEC_ID_TPMI: 70 return !!(caps & VSEC_CAP_TPMI); 71 default: 72 return false; 73 } 74 } 75 76 static void intel_vsec_remove_aux(void *data) 77 { 78 auxiliary_device_delete(data); 79 auxiliary_device_uninit(data); 80 } 81 82 static void intel_vsec_dev_release(struct device *dev) 83 { 84 struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); 85 86 xa_erase(&auxdev_array, intel_vsec_dev->id); 87 88 ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); 89 90 kfree(intel_vsec_dev->resource); 91 kfree(intel_vsec_dev); 92 } 93 94 int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, 95 struct intel_vsec_device *intel_vsec_dev, 96 const char *name) 97 { 98 struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; 99 int ret, id; 100 101 if (!parent) 102 return -EINVAL; 103 104 ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, 105 PMT_XA_LIMIT, GFP_KERNEL); 106 if (ret < 0) { 107 kfree(intel_vsec_dev->resource); 108 kfree(intel_vsec_dev); 109 return ret; 110 } 111 112 id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); 113 if (id < 0) { 114 xa_erase(&auxdev_array, intel_vsec_dev->id); 115 kfree(intel_vsec_dev->resource); 116 kfree(intel_vsec_dev); 117 return id; 118 } 119 120 auxdev->id = id; 121 auxdev->name = name; 122 auxdev->dev.parent = parent; 123 auxdev->dev.release = intel_vsec_dev_release; 124 125 ret = auxiliary_device_init(auxdev); 126 if (ret < 0) { 127 intel_vsec_dev_release(&auxdev->dev); 128 return ret; 129 } 130 131 ret = auxiliary_device_add(auxdev); 132 if (ret < 0) { 133 auxiliary_device_uninit(auxdev); 134 return ret; 135 } 136 137 return devm_add_action_or_reset(parent, intel_vsec_remove_aux, 138 auxdev); 139 } 140 EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); 141 142 static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, 143 struct intel_vsec_platform_info *info) 144 { 145 struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; 146 struct resource __free(kfree) *res = NULL; 147 struct resource *tmp; 148 struct device *parent; 149 unsigned long quirks = info->quirks; 150 u64 base_addr; 151 int i; 152 153 if (info->parent) 154 parent = info->parent; 155 else 156 parent = &pdev->dev; 157 158 if (!intel_vsec_supported(header->id, info->caps)) 159 return -EINVAL; 160 161 if (!header->num_entries) { 162 dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); 163 return -EINVAL; 164 } 165 166 if (!header->entry_size) { 167 dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); 168 return -EINVAL; 169 } 170 171 intel_vsec_dev = kzalloc(sizeof(*intel_vsec_dev), GFP_KERNEL); 172 if (!intel_vsec_dev) 173 return -ENOMEM; 174 175 res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); 176 if (!res) 177 return -ENOMEM; 178 179 if (quirks & VSEC_QUIRK_TABLE_SHIFT) 180 header->offset >>= TABLE_OFFSET_SHIFT; 181 182 if (info->base_addr) 183 base_addr = info->base_addr; 184 else 185 base_addr = pdev->resource[header->tbir].start; 186 187 /* 188 * The DVSEC/VSEC contains the starting offset and count for a block of 189 * discovery tables. Create a resource array of these tables to the 190 * auxiliary device driver. 191 */ 192 for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { 193 tmp->start = base_addr + header->offset + i * (header->entry_size * sizeof(u32)); 194 tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; 195 tmp->flags = IORESOURCE_MEM; 196 197 /* Check resource is not in use */ 198 if (!request_mem_region(tmp->start, resource_size(tmp), "")) 199 return -EBUSY; 200 201 release_mem_region(tmp->start, resource_size(tmp)); 202 } 203 204 intel_vsec_dev->pcidev = pdev; 205 intel_vsec_dev->resource = no_free_ptr(res); 206 intel_vsec_dev->num_resources = header->num_entries; 207 intel_vsec_dev->quirks = info->quirks; 208 intel_vsec_dev->base_addr = info->base_addr; 209 intel_vsec_dev->priv_data = info->priv_data; 210 211 if (header->id == VSEC_ID_SDSI) 212 intel_vsec_dev->ida = &intel_vsec_sdsi_ida; 213 else 214 intel_vsec_dev->ida = &intel_vsec_ida; 215 216 /* 217 * Pass the ownership of intel_vsec_dev and resource within it to 218 * intel_vsec_add_aux() 219 */ 220 return intel_vsec_add_aux(pdev, parent, no_free_ptr(intel_vsec_dev), 221 intel_vsec_name(header->id)); 222 } 223 224 static bool intel_vsec_walk_header(struct pci_dev *pdev, 225 struct intel_vsec_platform_info *info) 226 { 227 struct intel_vsec_header **header = info->headers; 228 bool have_devices = false; 229 int ret; 230 231 for ( ; *header; header++) { 232 ret = intel_vsec_add_dev(pdev, *header, info); 233 if (!ret) 234 have_devices = true; 235 } 236 237 return have_devices; 238 } 239 240 static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, 241 struct intel_vsec_platform_info *info) 242 { 243 bool have_devices = false; 244 int pos = 0; 245 246 do { 247 struct intel_vsec_header header; 248 u32 table, hdr; 249 u16 vid; 250 int ret; 251 252 pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); 253 if (!pos) 254 break; 255 256 pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER1, &hdr); 257 vid = PCI_DVSEC_HEADER1_VID(hdr); 258 if (vid != PCI_VENDOR_ID_INTEL) 259 continue; 260 261 /* Support only revision 1 */ 262 header.rev = PCI_DVSEC_HEADER1_REV(hdr); 263 if (header.rev != 1) { 264 dev_info(&pdev->dev, "Unsupported DVSEC revision %d\n", header.rev); 265 continue; 266 } 267 268 header.length = PCI_DVSEC_HEADER1_LEN(hdr); 269 270 pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); 271 pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); 272 pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); 273 274 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 275 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 276 277 pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); 278 header.id = PCI_DVSEC_HEADER2_ID(hdr); 279 280 ret = intel_vsec_add_dev(pdev, &header, info); 281 if (ret) 282 continue; 283 284 have_devices = true; 285 } while (true); 286 287 return have_devices; 288 } 289 290 static bool intel_vsec_walk_vsec(struct pci_dev *pdev, 291 struct intel_vsec_platform_info *info) 292 { 293 bool have_devices = false; 294 int pos = 0; 295 296 do { 297 struct intel_vsec_header header; 298 u32 table, hdr; 299 int ret; 300 301 pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_VNDR); 302 if (!pos) 303 break; 304 305 pci_read_config_dword(pdev, pos + PCI_VNDR_HEADER, &hdr); 306 307 /* Support only revision 1 */ 308 header.rev = PCI_VNDR_HEADER_REV(hdr); 309 if (header.rev != 1) { 310 dev_info(&pdev->dev, "Unsupported VSEC revision %d\n", header.rev); 311 continue; 312 } 313 314 header.id = PCI_VNDR_HEADER_ID(hdr); 315 header.length = PCI_VNDR_HEADER_LEN(hdr); 316 317 /* entry, size, and table offset are the same as DVSEC */ 318 pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); 319 pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); 320 pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); 321 322 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 323 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 324 325 ret = intel_vsec_add_dev(pdev, &header, info); 326 if (ret) 327 continue; 328 329 have_devices = true; 330 } while (true); 331 332 return have_devices; 333 } 334 335 void intel_vsec_register(struct pci_dev *pdev, 336 struct intel_vsec_platform_info *info) 337 { 338 if (!pdev || !info || !info->headers) 339 return; 340 341 intel_vsec_walk_header(pdev, info); 342 } 343 EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); 344 345 static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 346 { 347 struct intel_vsec_platform_info *info; 348 bool have_devices = false; 349 int ret; 350 351 ret = pcim_enable_device(pdev); 352 if (ret) 353 return ret; 354 355 pci_save_state(pdev); 356 info = (struct intel_vsec_platform_info *)id->driver_data; 357 if (!info) 358 return -EINVAL; 359 360 if (intel_vsec_walk_dvsec(pdev, info)) 361 have_devices = true; 362 363 if (intel_vsec_walk_vsec(pdev, info)) 364 have_devices = true; 365 366 if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && 367 intel_vsec_walk_header(pdev, info)) 368 have_devices = true; 369 370 if (!have_devices) 371 return -ENODEV; 372 373 return 0; 374 } 375 376 /* DG1 info */ 377 static struct intel_vsec_header dg1_header = { 378 .length = 0x10, 379 .id = 2, 380 .num_entries = 1, 381 .entry_size = 3, 382 .tbir = 0, 383 .offset = 0x466000, 384 }; 385 386 static struct intel_vsec_header *dg1_headers[] = { 387 &dg1_header, 388 NULL 389 }; 390 391 static const struct intel_vsec_platform_info dg1_info = { 392 .caps = VSEC_CAP_TELEMETRY, 393 .headers = dg1_headers, 394 .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, 395 }; 396 397 /* MTL info */ 398 static const struct intel_vsec_platform_info mtl_info = { 399 .caps = VSEC_CAP_TELEMETRY, 400 }; 401 402 /* OOBMSM info */ 403 static const struct intel_vsec_platform_info oobmsm_info = { 404 .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, 405 }; 406 407 /* TGL info */ 408 static const struct intel_vsec_platform_info tgl_info = { 409 .caps = VSEC_CAP_TELEMETRY, 410 .quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, 411 }; 412 413 /* LNL info */ 414 static const struct intel_vsec_platform_info lnl_info = { 415 .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_WATCHER, 416 }; 417 418 #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d 419 #define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e 420 #define PCI_DEVICE_ID_INTEL_VSEC_MTL_M 0x7d0d 421 #define PCI_DEVICE_ID_INTEL_VSEC_MTL_S 0xad0d 422 #define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 423 #define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d 424 #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d 425 #define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d 426 #define PCI_DEVICE_ID_INTEL_VSEC_PTL 0xb07d 427 static const struct pci_device_id intel_vsec_pci_ids[] = { 428 { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, 429 { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, 430 { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, 431 { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, 432 { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, 433 { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, 434 { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, 435 { PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) }, 436 { PCI_DEVICE_DATA(INTEL, VSEC_PTL, &mtl_info) }, 437 { } 438 }; 439 MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); 440 441 static pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev, 442 pci_channel_state_t state) 443 { 444 pci_ers_result_t status = PCI_ERS_RESULT_NEED_RESET; 445 446 dev_info(&pdev->dev, "PCI error detected, state %d", state); 447 448 if (state == pci_channel_io_perm_failure) 449 status = PCI_ERS_RESULT_DISCONNECT; 450 else 451 pci_disable_device(pdev); 452 453 return status; 454 } 455 456 static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) 457 { 458 struct intel_vsec_device *intel_vsec_dev; 459 pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT; 460 const struct pci_device_id *pci_dev_id; 461 unsigned long index; 462 463 dev_info(&pdev->dev, "Resetting PCI slot\n"); 464 465 msleep(2000); 466 if (pci_enable_device(pdev)) { 467 dev_info(&pdev->dev, 468 "Failed to re-enable PCI device after reset.\n"); 469 goto out; 470 } 471 472 status = PCI_ERS_RESULT_RECOVERED; 473 474 xa_for_each(&auxdev_array, index, intel_vsec_dev) { 475 /* check if pdev doesn't match */ 476 if (pdev != intel_vsec_dev->pcidev) 477 continue; 478 devm_release_action(&pdev->dev, intel_vsec_remove_aux, 479 &intel_vsec_dev->auxdev); 480 } 481 pci_disable_device(pdev); 482 pci_restore_state(pdev); 483 pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev); 484 intel_vsec_pci_probe(pdev, pci_dev_id); 485 486 out: 487 return status; 488 } 489 490 static void intel_vsec_pci_resume(struct pci_dev *pdev) 491 { 492 dev_info(&pdev->dev, "Done resuming PCI device\n"); 493 } 494 495 static const struct pci_error_handlers intel_vsec_pci_err_handlers = { 496 .error_detected = intel_vsec_pci_error_detected, 497 .slot_reset = intel_vsec_pci_slot_reset, 498 .resume = intel_vsec_pci_resume, 499 }; 500 501 static struct pci_driver intel_vsec_pci_driver = { 502 .name = "intel_vsec", 503 .id_table = intel_vsec_pci_ids, 504 .probe = intel_vsec_pci_probe, 505 .err_handler = &intel_vsec_pci_err_handlers, 506 }; 507 module_pci_driver(intel_vsec_pci_driver); 508 509 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 510 MODULE_DESCRIPTION("Intel Extended Capabilities auxiliary bus driver"); 511 MODULE_LICENSE("GPL v2"); 512