// SPDX-License-Identifier: GPL-2.0 /* * Intel Vendor Specific Extended Capabilities auxiliary bus driver * * Copyright (c) 2021, Intel Corporation. * All Rights Reserved. * * Author: David E. Box <david.e.box@linux.intel.com> * * This driver discovers and creates auxiliary devices for Intel defined PCIe * "Vendor Specific" and "Designated Vendor Specific" Extended Capabilities, * VSEC and DVSEC respectively. The driver supports features on specific PCIe * endpoints that exist primarily to expose them.
*/
staticconststruct vsec_feature_dependency *
get_consumer_dependencies(struct vsec_priv *priv, int cap_id)
{ conststruct vsec_feature_dependency *deps = priv->info->deps; int consumer_id = priv->info->num_deps;
if (!deps) return NULL;
while (consumer_id--) if (deps[consumer_id].feature == BIT(cap_id)) return &deps[consumer_id];
return NULL;
}
staticbool vsec_driver_present(int cap_id)
{ unsignedlong bit = BIT(cap_id);
switch (bit) { case VSEC_CAP_TELEMETRY: return IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY); case VSEC_CAP_WATCHER: return IS_ENABLED(CONFIG_INTEL_PMT_WATCHER); case VSEC_CAP_CRASHLOG: return IS_ENABLED(CONFIG_INTEL_PMT_CRASHLOG); case VSEC_CAP_SDSI: return IS_ENABLED(CONFIG_INTEL_SDSI); case VSEC_CAP_TPMI: return IS_ENABLED(CONFIG_INTEL_TPMI); case VSEC_CAP_DISCOVERY: return IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY); default: returnfalse;
}
}
/* * Although pci_device_id table is available in the pdev, this prototype is * necessary because the code using it can be called by an exported API that * might pass a different pdev.
*/ staticconststruct pci_device_id intel_vsec_pci_ids[];
ret = auxiliary_device_init(auxdev); if (ret < 0) {
intel_vsec_dev_release(&auxdev->dev); return ret;
}
/* * Assign a name now to ensure that the device link doesn't contain * a null string for the consumer name. This is a problem when a supplier * supplies more than one consumer and can lead to a duplicate name error * when the link is created in sysfs.
*/
ret = dev_set_name(&auxdev->dev, "%s.%s.%d", KBUILD_MODNAME, auxdev->name,
auxdev->id); if (ret) goto cleanup_aux;
ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id); if (ret) goto cleanup_aux;
ret = auxiliary_device_add(auxdev); if (ret) goto cleanup_aux;
if (info->parent)
parent = info->parent; else
parent = &pdev->dev;
if (!intel_vsec_supported(header->id, info->caps)) return -EINVAL;
if (!header->num_entries) {
dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); return -EINVAL;
}
if (!header->entry_size) {
dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); return -EINVAL;
}
intel_vsec_dev = kzalloc(sizeof(*intel_vsec_dev), GFP_KERNEL); if (!intel_vsec_dev) return -ENOMEM;
res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM;
if (quirks & VSEC_QUIRK_TABLE_SHIFT)
header->offset >>= TABLE_OFFSET_SHIFT;
if (info->base_addr)
base_addr = info->base_addr; else
base_addr = pdev->resource[header->tbir].start;
/* * The DVSEC/VSEC contains the starting offset and count for a block of * discovery tables. Create a resource array of these tables to the * auxiliary device driver.
*/ for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) {
tmp->start = base_addr + header->offset + i * (header->entry_size * sizeof(u32));
tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1;
tmp->flags = IORESOURCE_MEM;
/* Check resource is not in use */ if (!request_mem_region(tmp->start, resource_size(tmp), "")) return -EBUSY;
/* * Pass the ownership of intel_vsec_dev and resource within it to * intel_vsec_add_aux()
*/ return intel_vsec_add_aux(pdev, parent, no_free_ptr(intel_vsec_dev),
intel_vsec_name(header->id));
}
staticbool suppliers_ready(struct vsec_priv *priv, conststruct vsec_feature_dependency *consumer_deps, int cap_id)
{ enum vsec_device_state *state = priv->state; int supplier_id;
if (WARN_ON_ONCE(consumer_deps->feature != BIT(cap_id))) returnfalse;
/* * Verify that all required suppliers have been found. Return false * immediately if any are still missing.
*/
for_each_set_bit(supplier_id, &consumer_deps->supplier_bitmap, VSEC_FEATURE_COUNT) { if (state[supplier_id] == STATE_SKIP) continue;
if (state[supplier_id] == STATE_NOT_FOUND) returnfalse;
}
/* * All suppliers have been found and the consumer is ready to be * registered.
*/ returntrue;
}
staticint get_cap_id(u32 header_id, unsignedlong *cap_id)
{ switch (header_id) { case VSEC_ID_TELEMETRY:
*cap_id = ilog2(VSEC_CAP_TELEMETRY); break; case VSEC_ID_WATCHER:
*cap_id = ilog2(VSEC_CAP_WATCHER); break; case VSEC_ID_CRASHLOG:
*cap_id = ilog2(VSEC_CAP_CRASHLOG); break; case VSEC_ID_SDSI:
*cap_id = ilog2(VSEC_CAP_SDSI); break; case VSEC_ID_TPMI:
*cap_id = ilog2(VSEC_CAP_TPMI); break; case VSEC_ID_DISCOVERY:
*cap_id = ilog2(VSEC_CAP_DISCOVERY); break; default: return -EINVAL;
}
ret = get_cap_id(header->id, &cap_id); if (ret) return ret;
/* * Only track dependencies for devices probed by the VSEC driver. * For others using the exported APIs, add the device directly.
*/ if (!pci_match_id(intel_vsec_pci_ids, pdev)) return intel_vsec_add_dev(pdev, header, info, cap_id);
/* * Both DVSEC and VSEC capabilities can exist on the same device, * so both intel_vsec_walk_dvsec() and intel_vsec_walk_vsec() must be * called independently. Additionally, intel_vsec_walk_header() is * needed for devices that do not have VSEC/DVSEC but provide the * information via device_data.
*/ if (intel_vsec_walk_dvsec(pdev, info))
found = true;
if (intel_vsec_walk_vsec(pdev, info))
found = true;
if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) &&
intel_vsec_walk_header(pdev, info))
found = true;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.