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