vsec.c 13 KB

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