162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel Vendor Specific Extended Capabilities auxiliary bus driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2021, Intel Corporation. 662306a36Sopenharmony_ci * All Rights Reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: David E. Box <david.e.box@linux.intel.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This driver discovers and creates auxiliary devices for Intel defined PCIe 1162306a36Sopenharmony_ci * "Vendor Specific" and "Designated Vendor Specific" Extended Capabilities, 1262306a36Sopenharmony_ci * VSEC and DVSEC respectively. The driver supports features on specific PCIe 1362306a36Sopenharmony_ci * endpoints that exist primarily to expose them. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/auxiliary_bus.h> 1762306a36Sopenharmony_ci#include <linux/bits.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/idr.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "vsec.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Intel DVSEC offsets */ 2862306a36Sopenharmony_ci#define INTEL_DVSEC_ENTRIES 0xA 2962306a36Sopenharmony_ci#define INTEL_DVSEC_SIZE 0xB 3062306a36Sopenharmony_ci#define INTEL_DVSEC_TABLE 0xC 3162306a36Sopenharmony_ci#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) 3262306a36Sopenharmony_ci#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) 3362306a36Sopenharmony_ci#define TABLE_OFFSET_SHIFT 3 3462306a36Sopenharmony_ci#define PMT_XA_START 0 3562306a36Sopenharmony_ci#define PMT_XA_MAX INT_MAX 3662306a36Sopenharmony_ci#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic DEFINE_IDA(intel_vsec_ida); 3962306a36Sopenharmony_cistatic DEFINE_IDA(intel_vsec_sdsi_ida); 4062306a36Sopenharmony_cistatic DEFINE_XARRAY_ALLOC(auxdev_array); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/** 4362306a36Sopenharmony_ci * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. 4462306a36Sopenharmony_ci * @rev: Revision ID of the VSEC/DVSEC register space 4562306a36Sopenharmony_ci * @length: Length of the VSEC/DVSEC register space 4662306a36Sopenharmony_ci * @id: ID of the feature 4762306a36Sopenharmony_ci * @num_entries: Number of instances of the feature 4862306a36Sopenharmony_ci * @entry_size: Size of the discovery table for each feature 4962306a36Sopenharmony_ci * @tbir: BAR containing the discovery tables 5062306a36Sopenharmony_ci * @offset: BAR offset of start of the first discovery table 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistruct intel_vsec_header { 5362306a36Sopenharmony_ci u8 rev; 5462306a36Sopenharmony_ci u16 length; 5562306a36Sopenharmony_ci u16 id; 5662306a36Sopenharmony_ci u8 num_entries; 5762306a36Sopenharmony_ci u8 entry_size; 5862306a36Sopenharmony_ci u8 tbir; 5962306a36Sopenharmony_ci u32 offset; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cienum intel_vsec_id { 6362306a36Sopenharmony_ci VSEC_ID_TELEMETRY = 2, 6462306a36Sopenharmony_ci VSEC_ID_WATCHER = 3, 6562306a36Sopenharmony_ci VSEC_ID_CRASHLOG = 4, 6662306a36Sopenharmony_ci VSEC_ID_SDSI = 65, 6762306a36Sopenharmony_ci VSEC_ID_TPMI = 66, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const char *intel_vsec_name(enum intel_vsec_id id) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci switch (id) { 7362306a36Sopenharmony_ci case VSEC_ID_TELEMETRY: 7462306a36Sopenharmony_ci return "telemetry"; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci case VSEC_ID_WATCHER: 7762306a36Sopenharmony_ci return "watcher"; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci case VSEC_ID_CRASHLOG: 8062306a36Sopenharmony_ci return "crashlog"; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci case VSEC_ID_SDSI: 8362306a36Sopenharmony_ci return "sdsi"; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci case VSEC_ID_TPMI: 8662306a36Sopenharmony_ci return "tpmi"; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci default: 8962306a36Sopenharmony_ci return NULL; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic bool intel_vsec_supported(u16 id, unsigned long caps) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci switch (id) { 9662306a36Sopenharmony_ci case VSEC_ID_TELEMETRY: 9762306a36Sopenharmony_ci return !!(caps & VSEC_CAP_TELEMETRY); 9862306a36Sopenharmony_ci case VSEC_ID_WATCHER: 9962306a36Sopenharmony_ci return !!(caps & VSEC_CAP_WATCHER); 10062306a36Sopenharmony_ci case VSEC_ID_CRASHLOG: 10162306a36Sopenharmony_ci return !!(caps & VSEC_CAP_CRASHLOG); 10262306a36Sopenharmony_ci case VSEC_ID_SDSI: 10362306a36Sopenharmony_ci return !!(caps & VSEC_CAP_SDSI); 10462306a36Sopenharmony_ci case VSEC_ID_TPMI: 10562306a36Sopenharmony_ci return !!(caps & VSEC_CAP_TPMI); 10662306a36Sopenharmony_ci default: 10762306a36Sopenharmony_ci return false; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void intel_vsec_remove_aux(void *data) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci auxiliary_device_delete(data); 11462306a36Sopenharmony_ci auxiliary_device_uninit(data); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic DEFINE_MUTEX(vsec_ida_lock); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void intel_vsec_dev_release(struct device *dev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci xa_erase(&auxdev_array, intel_vsec_dev->id); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci mutex_lock(&vsec_ida_lock); 12662306a36Sopenharmony_ci ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); 12762306a36Sopenharmony_ci mutex_unlock(&vsec_ida_lock); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci kfree(intel_vsec_dev->resource); 13062306a36Sopenharmony_ci kfree(intel_vsec_dev); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciint intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, 13462306a36Sopenharmony_ci struct intel_vsec_device *intel_vsec_dev, 13562306a36Sopenharmony_ci const char *name) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; 13862306a36Sopenharmony_ci int ret, id; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, 14162306a36Sopenharmony_ci PMT_XA_LIMIT, GFP_KERNEL); 14262306a36Sopenharmony_ci if (ret < 0) { 14362306a36Sopenharmony_ci kfree(intel_vsec_dev->resource); 14462306a36Sopenharmony_ci kfree(intel_vsec_dev); 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci mutex_lock(&vsec_ida_lock); 14962306a36Sopenharmony_ci id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); 15062306a36Sopenharmony_ci mutex_unlock(&vsec_ida_lock); 15162306a36Sopenharmony_ci if (id < 0) { 15262306a36Sopenharmony_ci xa_erase(&auxdev_array, intel_vsec_dev->id); 15362306a36Sopenharmony_ci kfree(intel_vsec_dev->resource); 15462306a36Sopenharmony_ci kfree(intel_vsec_dev); 15562306a36Sopenharmony_ci return id; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!parent) 15962306a36Sopenharmony_ci parent = &pdev->dev; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci auxdev->id = id; 16262306a36Sopenharmony_ci auxdev->name = name; 16362306a36Sopenharmony_ci auxdev->dev.parent = parent; 16462306a36Sopenharmony_ci auxdev->dev.release = intel_vsec_dev_release; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ret = auxiliary_device_init(auxdev); 16762306a36Sopenharmony_ci if (ret < 0) { 16862306a36Sopenharmony_ci intel_vsec_dev_release(&auxdev->dev); 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = auxiliary_device_add(auxdev); 17362306a36Sopenharmony_ci if (ret < 0) { 17462306a36Sopenharmony_ci auxiliary_device_uninit(auxdev); 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = devm_add_action_or_reset(parent, intel_vsec_remove_aux, 17962306a36Sopenharmony_ci auxdev); 18062306a36Sopenharmony_ci if (ret < 0) 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, 18862306a36Sopenharmony_ci struct intel_vsec_platform_info *info) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct intel_vsec_device *intel_vsec_dev; 19162306a36Sopenharmony_ci struct resource *res, *tmp; 19262306a36Sopenharmony_ci unsigned long quirks = info->quirks; 19362306a36Sopenharmony_ci int i; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!intel_vsec_supported(header->id, info->caps)) 19662306a36Sopenharmony_ci return -EINVAL; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!header->num_entries) { 19962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); 20062306a36Sopenharmony_ci return -EINVAL; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!header->entry_size) { 20462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); 20562306a36Sopenharmony_ci return -EINVAL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci intel_vsec_dev = kzalloc(sizeof(*intel_vsec_dev), GFP_KERNEL); 20962306a36Sopenharmony_ci if (!intel_vsec_dev) 21062306a36Sopenharmony_ci return -ENOMEM; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); 21362306a36Sopenharmony_ci if (!res) { 21462306a36Sopenharmony_ci kfree(intel_vsec_dev); 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (quirks & VSEC_QUIRK_TABLE_SHIFT) 21962306a36Sopenharmony_ci header->offset >>= TABLE_OFFSET_SHIFT; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * The DVSEC/VSEC contains the starting offset and count for a block of 22362306a36Sopenharmony_ci * discovery tables. Create a resource array of these tables to the 22462306a36Sopenharmony_ci * auxiliary device driver. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { 22762306a36Sopenharmony_ci tmp->start = pdev->resource[header->tbir].start + 22862306a36Sopenharmony_ci header->offset + i * (header->entry_size * sizeof(u32)); 22962306a36Sopenharmony_ci tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; 23062306a36Sopenharmony_ci tmp->flags = IORESOURCE_MEM; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci intel_vsec_dev->pcidev = pdev; 23462306a36Sopenharmony_ci intel_vsec_dev->resource = res; 23562306a36Sopenharmony_ci intel_vsec_dev->num_resources = header->num_entries; 23662306a36Sopenharmony_ci intel_vsec_dev->info = info; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (header->id == VSEC_ID_SDSI) 23962306a36Sopenharmony_ci intel_vsec_dev->ida = &intel_vsec_sdsi_ida; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci intel_vsec_dev->ida = &intel_vsec_ida; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return intel_vsec_add_aux(pdev, NULL, intel_vsec_dev, 24462306a36Sopenharmony_ci intel_vsec_name(header->id)); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic bool intel_vsec_walk_header(struct pci_dev *pdev, 24862306a36Sopenharmony_ci struct intel_vsec_platform_info *info) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct intel_vsec_header **header = info->headers; 25162306a36Sopenharmony_ci bool have_devices = false; 25262306a36Sopenharmony_ci int ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci for ( ; *header; header++) { 25562306a36Sopenharmony_ci ret = intel_vsec_add_dev(pdev, *header, info); 25662306a36Sopenharmony_ci if (ret) 25762306a36Sopenharmony_ci dev_info(&pdev->dev, "Could not add device for VSEC id %d\n", 25862306a36Sopenharmony_ci (*header)->id); 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci have_devices = true; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return have_devices; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic bool intel_vsec_walk_dvsec(struct pci_dev *pdev, 26762306a36Sopenharmony_ci struct intel_vsec_platform_info *info) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci bool have_devices = false; 27062306a36Sopenharmony_ci int pos = 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci do { 27362306a36Sopenharmony_ci struct intel_vsec_header header; 27462306a36Sopenharmony_ci u32 table, hdr; 27562306a36Sopenharmony_ci u16 vid; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); 27962306a36Sopenharmony_ci if (!pos) 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER1, &hdr); 28362306a36Sopenharmony_ci vid = PCI_DVSEC_HEADER1_VID(hdr); 28462306a36Sopenharmony_ci if (vid != PCI_VENDOR_ID_INTEL) 28562306a36Sopenharmony_ci continue; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Support only revision 1 */ 28862306a36Sopenharmony_ci header.rev = PCI_DVSEC_HEADER1_REV(hdr); 28962306a36Sopenharmony_ci if (header.rev != 1) { 29062306a36Sopenharmony_ci dev_info(&pdev->dev, "Unsupported DVSEC revision %d\n", header.rev); 29162306a36Sopenharmony_ci continue; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci header.length = PCI_DVSEC_HEADER1_LEN(hdr); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); 29762306a36Sopenharmony_ci pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); 29862306a36Sopenharmony_ci pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci header.tbir = INTEL_DVSEC_TABLE_BAR(table); 30162306a36Sopenharmony_ci header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); 30462306a36Sopenharmony_ci header.id = PCI_DVSEC_HEADER2_ID(hdr); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ret = intel_vsec_add_dev(pdev, &header, info); 30762306a36Sopenharmony_ci if (ret) 30862306a36Sopenharmony_ci continue; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci have_devices = true; 31162306a36Sopenharmony_ci } while (true); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return have_devices; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic bool intel_vsec_walk_vsec(struct pci_dev *pdev, 31762306a36Sopenharmony_ci struct intel_vsec_platform_info *info) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci bool have_devices = false; 32062306a36Sopenharmony_ci int pos = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci do { 32362306a36Sopenharmony_ci struct intel_vsec_header header; 32462306a36Sopenharmony_ci u32 table, hdr; 32562306a36Sopenharmony_ci int ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_VNDR); 32862306a36Sopenharmony_ci if (!pos) 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci pci_read_config_dword(pdev, pos + PCI_VNDR_HEADER, &hdr); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Support only revision 1 */ 33462306a36Sopenharmony_ci header.rev = PCI_VNDR_HEADER_REV(hdr); 33562306a36Sopenharmony_ci if (header.rev != 1) { 33662306a36Sopenharmony_ci dev_info(&pdev->dev, "Unsupported VSEC revision %d\n", header.rev); 33762306a36Sopenharmony_ci continue; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci header.id = PCI_VNDR_HEADER_ID(hdr); 34162306a36Sopenharmony_ci header.length = PCI_VNDR_HEADER_LEN(hdr); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* entry, size, and table offset are the same as DVSEC */ 34462306a36Sopenharmony_ci pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); 34562306a36Sopenharmony_ci pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); 34662306a36Sopenharmony_ci pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci header.tbir = INTEL_DVSEC_TABLE_BAR(table); 34962306a36Sopenharmony_ci header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ret = intel_vsec_add_dev(pdev, &header, info); 35262306a36Sopenharmony_ci if (ret) 35362306a36Sopenharmony_ci continue; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci have_devices = true; 35662306a36Sopenharmony_ci } while (true); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return have_devices; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct intel_vsec_platform_info *info; 36462306a36Sopenharmony_ci bool have_devices = false; 36562306a36Sopenharmony_ci int ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci pci_save_state(pdev); 37262306a36Sopenharmony_ci info = (struct intel_vsec_platform_info *)id->driver_data; 37362306a36Sopenharmony_ci if (!info) 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (intel_vsec_walk_dvsec(pdev, info)) 37762306a36Sopenharmony_ci have_devices = true; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (intel_vsec_walk_vsec(pdev, info)) 38062306a36Sopenharmony_ci have_devices = true; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && 38362306a36Sopenharmony_ci intel_vsec_walk_header(pdev, info)) 38462306a36Sopenharmony_ci have_devices = true; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!have_devices) 38762306a36Sopenharmony_ci return -ENODEV; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* DG1 info */ 39362306a36Sopenharmony_cistatic struct intel_vsec_header dg1_header = { 39462306a36Sopenharmony_ci .length = 0x10, 39562306a36Sopenharmony_ci .id = 2, 39662306a36Sopenharmony_ci .num_entries = 1, 39762306a36Sopenharmony_ci .entry_size = 3, 39862306a36Sopenharmony_ci .tbir = 0, 39962306a36Sopenharmony_ci .offset = 0x466000, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic struct intel_vsec_header *dg1_headers[] = { 40362306a36Sopenharmony_ci &dg1_header, 40462306a36Sopenharmony_ci NULL 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic const struct intel_vsec_platform_info dg1_info = { 40862306a36Sopenharmony_ci .caps = VSEC_CAP_TELEMETRY, 40962306a36Sopenharmony_ci .headers = dg1_headers, 41062306a36Sopenharmony_ci .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, 41162306a36Sopenharmony_ci}; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* MTL info */ 41462306a36Sopenharmony_cistatic const struct intel_vsec_platform_info mtl_info = { 41562306a36Sopenharmony_ci .caps = VSEC_CAP_TELEMETRY, 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* OOBMSM info */ 41962306a36Sopenharmony_cistatic const struct intel_vsec_platform_info oobmsm_info = { 42062306a36Sopenharmony_ci .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* TGL info */ 42462306a36Sopenharmony_cistatic const struct intel_vsec_platform_info tgl_info = { 42562306a36Sopenharmony_ci .caps = VSEC_CAP_TELEMETRY, 42662306a36Sopenharmony_ci .quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d 43062306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e 43162306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_MTL_M 0x7d0d 43262306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_MTL_S 0xad0d 43362306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 43462306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d 43562306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d 43662306a36Sopenharmony_cistatic const struct pci_device_id intel_vsec_pci_ids[] = { 43762306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, 43862306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, 43962306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, 44062306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, 44162306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, 44262306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, 44362306a36Sopenharmony_ci { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, 44462306a36Sopenharmony_ci { } 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev, 44962306a36Sopenharmony_ci pci_channel_state_t state) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci pci_ers_result_t status = PCI_ERS_RESULT_NEED_RESET; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dev_info(&pdev->dev, "PCI error detected, state %d", state); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (state == pci_channel_io_perm_failure) 45662306a36Sopenharmony_ci status = PCI_ERS_RESULT_DISCONNECT; 45762306a36Sopenharmony_ci else 45862306a36Sopenharmony_ci pci_disable_device(pdev); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return status; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct intel_vsec_device *intel_vsec_dev; 46662306a36Sopenharmony_ci pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT; 46762306a36Sopenharmony_ci const struct pci_device_id *pci_dev_id; 46862306a36Sopenharmony_ci unsigned long index; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci dev_info(&pdev->dev, "Resetting PCI slot\n"); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci msleep(2000); 47362306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 47462306a36Sopenharmony_ci dev_info(&pdev->dev, 47562306a36Sopenharmony_ci "Failed to re-enable PCI device after reset.\n"); 47662306a36Sopenharmony_ci goto out; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci status = PCI_ERS_RESULT_RECOVERED; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci xa_for_each(&auxdev_array, index, intel_vsec_dev) { 48262306a36Sopenharmony_ci /* check if pdev doesn't match */ 48362306a36Sopenharmony_ci if (pdev != intel_vsec_dev->pcidev) 48462306a36Sopenharmony_ci continue; 48562306a36Sopenharmony_ci devm_release_action(&pdev->dev, intel_vsec_remove_aux, 48662306a36Sopenharmony_ci &intel_vsec_dev->auxdev); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci pci_disable_device(pdev); 48962306a36Sopenharmony_ci pci_restore_state(pdev); 49062306a36Sopenharmony_ci pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev); 49162306a36Sopenharmony_ci intel_vsec_pci_probe(pdev, pci_dev_id); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ciout: 49462306a36Sopenharmony_ci return status; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void intel_vsec_pci_resume(struct pci_dev *pdev) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci dev_info(&pdev->dev, "Done resuming PCI device\n"); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic const struct pci_error_handlers intel_vsec_pci_err_handlers = { 50362306a36Sopenharmony_ci .error_detected = intel_vsec_pci_error_detected, 50462306a36Sopenharmony_ci .slot_reset = intel_vsec_pci_slot_reset, 50562306a36Sopenharmony_ci .resume = intel_vsec_pci_resume, 50662306a36Sopenharmony_ci}; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct pci_driver intel_vsec_pci_driver = { 50962306a36Sopenharmony_ci .name = "intel_vsec", 51062306a36Sopenharmony_ci .id_table = intel_vsec_pci_ids, 51162306a36Sopenharmony_ci .probe = intel_vsec_pci_probe, 51262306a36Sopenharmony_ci .err_handler = &intel_vsec_pci_err_handlers, 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_cimodule_pci_driver(intel_vsec_pci_driver); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciMODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 51762306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Extended Capabilities auxiliary bus driver"); 51862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 519