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