diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-12 20:11:34 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-12 20:11:34 +0100 |
commit | 6dc69d3d0d18d587ab9d809fe060ba4417cf0279 (patch) | |
tree | 9cb5936b21a6b4eab2224019abfe412d1b2e6050 /drivers/platform | |
parent | Merge tag 'pinctrl-v5.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/... (diff) | |
parent | kobject documentation: remove default_attrs information (diff) | |
download | linux-6dc69d3d0d18d587ab9d809fe060ba4417cf0279.tar.xz linux-6dc69d3d0d18d587ab9d809fe060ba4417cf0279.zip |
Merge tag 'driver-core-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH:
"Here is the set of changes for the driver core for 5.17-rc1.
Lots of little things here, including:
- kobj_type cleanups
- auxiliary_bus documentation updates
- auxiliary_device conversions for some drivers (relevant subsystems
all have provided acks for these)
- kernfs lock contention reduction for some workloads
- other tiny cleanups and changes.
All of these have been in linux-next for a while with no reported
issues"
* tag 'driver-core-5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (43 commits)
kobject documentation: remove default_attrs information
drivers/firmware: Add missing platform_device_put() in sysfb_create_simplefb
debugfs: lockdown: Allow reading debugfs files that are not world readable
driver core: Make bus notifiers in right order in really_probe()
driver core: Move driver_sysfs_remove() after driver_sysfs_add()
firmware: edd: remove empty default_attrs array
firmware: dmi-sysfs: use default_groups in kobj_type
qemu_fw_cfg: use default_groups in kobj_type
firmware: memmap: use default_groups in kobj_type
sh: sq: use default_groups in kobj_type
headers/uninline: Uninline single-use function: kobject_has_children()
devtmpfs: mount with noexec and nosuid
driver core: Simplify async probe test code by using ktime_ms_delta()
nilfs2: use default_groups in kobj_type
kobject: remove kset from struct kset_uevent_ops callbacks
driver core: make kobj_type constant.
driver core: platform: document registration-failure requirement
vdpa/mlx5: Use auxiliary_device driver data helpers
net/mlx5e: Use auxiliary_device driver data helpers
soundwire: intel: Use auxiliary_device driver data helpers
...
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/intel/Kconfig | 11 | ||||
-rw-r--r-- | drivers/platform/x86/intel/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmt/Kconfig | 4 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmt/class.c | 21 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmt/class.h | 5 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmt/crashlog.c | 47 | ||||
-rw-r--r-- | drivers/platform/x86/intel/pmt/telemetry.c | 46 | ||||
-rw-r--r-- | drivers/platform/x86/intel/vsec.c | 408 | ||||
-rw-r--r-- | drivers/platform/x86/intel/vsec.h | 43 |
9 files changed, 528 insertions, 59 deletions
diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 40096b25994a..8e65086bb6c8 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -170,3 +170,14 @@ config INTEL_UNCORE_FREQ_CONTROL To compile this driver as a module, choose M here: the module will be called intel-uncore-frequency. + +config INTEL_VSEC + tristate "Intel Vendor Specific Extended Capabilities Driver" + depends on PCI + select AUXILIARY_BUS + help + Adds support for feature drivers exposed using Intel PCIe VSEC and + DVSEC. + + To compile this driver as a module, choose M here: the module will + be called intel_vsec. diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 174a80a6c87c..35f2066578b2 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -26,6 +26,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o intel_oaktrail-y := oaktrail.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o +intel_vsec-y := vsec.o +obj-$(CONFIG_INTEL_VSEC) += intel_vsec.o # Intel PMIC / PMC / P-Unit drivers intel_bxtwc_tmu-y := bxtwc_tmu.o diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index d630f883a717..e916fc966221 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -17,7 +17,7 @@ config INTEL_PMT_CLASS config INTEL_PMT_TELEMETRY tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" - depends on MFD_INTEL_PMT + depends on INTEL_VSEC select INTEL_PMT_CLASS help The Intel Platform Monitory Technology (PMT) Telemetry driver provides @@ -29,7 +29,7 @@ config INTEL_PMT_TELEMETRY config INTEL_PMT_CRASHLOG tristate "Intel Platform Monitoring Technology (PMT) Crashlog driver" - depends on MFD_INTEL_PMT + depends on INTEL_VSEC select INTEL_PMT_CLASS help The Intel Platform Monitoring Technology (PMT) crashlog driver provides diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 659b1073033c..1c9e3f3ea41c 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -13,6 +13,7 @@ #include <linux/mm.h> #include <linux/pci.h> +#include "../vsec.h" #include "class.h" #define PMT_XA_START 0 @@ -281,31 +282,29 @@ fail_dev_create: return ret; } -int intel_pmt_dev_create(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx) +int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, + struct intel_vsec_device *intel_vsec_dev, int idx) { + struct device *dev = &intel_vsec_dev->auxdev.dev; struct intel_pmt_header header; struct resource *disc_res; - int ret = -ENODEV; + int ret; - disc_res = platform_get_resource(pdev, IORESOURCE_MEM, idx); - if (!disc_res) - return ret; + disc_res = &intel_vsec_dev->resource[idx]; - entry->disc_table = devm_platform_ioremap_resource(pdev, idx); + entry->disc_table = devm_ioremap_resource(dev, disc_res); if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); - ret = ns->pmt_header_decode(entry, &header, &pdev->dev); + ret = ns->pmt_header_decode(entry, &header, dev); if (ret) return ret; - ret = intel_pmt_populate_entry(entry, &header, &pdev->dev, disc_res); + ret = intel_pmt_populate_entry(entry, &header, dev, disc_res); if (ret) return ret; - return intel_pmt_dev_register(entry, ns, &pdev->dev); + return intel_pmt_dev_register(entry, ns, dev); } EXPORT_SYMBOL_GPL(intel_pmt_dev_create); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 1337019c2873..db11d58867ce 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -2,13 +2,14 @@ #ifndef _INTEL_PMT_CLASS_H #define _INTEL_PMT_CLASS_H -#include <linux/platform_device.h> #include <linux/xarray.h> #include <linux/types.h> #include <linux/bits.h> #include <linux/err.h> #include <linux/io.h> +#include "../vsec.h" + /* PMT access types */ #define ACCESS_BARID 2 #define ACCESS_LOCAL 3 @@ -47,7 +48,7 @@ struct intel_pmt_namespace { bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx); + struct intel_vsec_device *dev, int idx); void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns); #endif diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 1c1021f04d3c..34daf9df168b 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -8,6 +8,7 @@ * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -15,10 +16,9 @@ #include <linux/uaccess.h> #include <linux/overflow.h> +#include "../vsec.h" #include "class.h" -#define DRV_NAME "pmt_crashlog" - /* Crashlog discovery header types */ #define CRASH_TYPE_OOBMSM 1 @@ -257,34 +257,34 @@ static struct intel_pmt_namespace pmt_crashlog_ns = { /* * initialization */ -static int pmt_crashlog_remove(struct platform_device *pdev) +static void pmt_crashlog_remove(struct auxiliary_device *auxdev) { - struct pmt_crashlog_priv *priv = platform_get_drvdata(pdev); + struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); int i; for (i = 0; i < priv->num_entries; i++) intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); - - return 0; } -static int pmt_crashlog_probe(struct platform_device *pdev) +static int pmt_crashlog_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) { + struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); struct pmt_crashlog_priv *priv; size_t size; int i, ret; - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + size = struct_size(priv, entry, intel_vsec_dev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(auxdev, priv); - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[i].entry; - ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, pdev, i); + ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i); if (ret < 0) goto abort_probe; if (ret) @@ -295,26 +295,30 @@ static int pmt_crashlog_probe(struct platform_device *pdev) return 0; abort_probe: - pmt_crashlog_remove(pdev); + pmt_crashlog_remove(auxdev); return ret; } -static struct platform_driver pmt_crashlog_driver = { - .driver = { - .name = DRV_NAME, - }, - .remove = pmt_crashlog_remove, - .probe = pmt_crashlog_probe, +static const struct auxiliary_device_id pmt_crashlog_id_table[] = { + { .name = "intel_vsec.crashlog" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table); + +static struct auxiliary_driver pmt_crashlog_aux_driver = { + .id_table = pmt_crashlog_id_table, + .remove = pmt_crashlog_remove, + .probe = pmt_crashlog_probe, }; static int __init pmt_crashlog_init(void) { - return platform_driver_register(&pmt_crashlog_driver); + return auxiliary_driver_register(&pmt_crashlog_aux_driver); } static void __exit pmt_crashlog_exit(void) { - platform_driver_unregister(&pmt_crashlog_driver); + auxiliary_driver_unregister(&pmt_crashlog_aux_driver); xa_destroy(&crashlog_array); } @@ -323,5 +327,4 @@ module_exit(pmt_crashlog_exit); MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); MODULE_DESCRIPTION("Intel PMT Crashlog driver"); -MODULE_ALIAS("platform:" DRV_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 38d52651c572..6b6f3e2a617a 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -8,6 +8,7 @@ * Author: "David E. Box" <david.e.box@linux.intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -15,10 +16,9 @@ #include <linux/uaccess.h> #include <linux/overflow.h> +#include "../vsec.h" #include "class.h" -#define TELEM_DEV_NAME "pmt_telemetry" - #define TELEM_SIZE_OFFSET 0x0 #define TELEM_GUID_OFFSET 0x4 #define TELEM_BASE_OFFSET 0x8 @@ -79,34 +79,33 @@ static struct intel_pmt_namespace pmt_telem_ns = { .pmt_header_decode = pmt_telem_header_decode, }; -static int pmt_telem_remove(struct platform_device *pdev) +static void pmt_telem_remove(struct auxiliary_device *auxdev) { - struct pmt_telem_priv *priv = platform_get_drvdata(pdev); + struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); int i; for (i = 0; i < priv->num_entries; i++) intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); - - return 0; } -static int pmt_telem_probe(struct platform_device *pdev) +static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { + struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); struct pmt_telem_priv *priv; size_t size; int i, ret; - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + size = struct_size(priv, entry, intel_vsec_dev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(auxdev, priv); - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[i]; - ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i); + ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); if (ret < 0) goto abort_probe; if (ret) @@ -117,32 +116,35 @@ static int pmt_telem_probe(struct platform_device *pdev) return 0; abort_probe: - pmt_telem_remove(pdev); + pmt_telem_remove(auxdev); return ret; } -static struct platform_driver pmt_telem_driver = { - .driver = { - .name = TELEM_DEV_NAME, - }, - .remove = pmt_telem_remove, - .probe = pmt_telem_probe, +static const struct auxiliary_device_id pmt_telem_id_table[] = { + { .name = "intel_vsec.telemetry" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table); + +static struct auxiliary_driver pmt_telem_aux_driver = { + .id_table = pmt_telem_id_table, + .remove = pmt_telem_remove, + .probe = pmt_telem_probe, }; static int __init pmt_telem_init(void) { - return platform_driver_register(&pmt_telem_driver); + return auxiliary_driver_register(&pmt_telem_aux_driver); } module_init(pmt_telem_init); static void __exit pmt_telem_exit(void) { - platform_driver_unregister(&pmt_telem_driver); + auxiliary_driver_unregister(&pmt_telem_aux_driver); xa_destroy(&telem_array); } module_exit(pmt_telem_exit); MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); MODULE_DESCRIPTION("Intel PMT Telemetry driver"); -MODULE_ALIAS("platform:" TELEM_DEV_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c new file mode 100644 index 000000000000..c3bdd75ed690 --- /dev/null +++ b/drivers/platform/x86/intel/vsec.c @@ -0,0 +1,408 @@ +// 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. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> +#include <linux/kernel.h> +#include <linux/idr.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include "vsec.h" + +/* Intel DVSEC offsets */ +#define INTEL_DVSEC_ENTRIES 0xA +#define INTEL_DVSEC_SIZE 0xB +#define INTEL_DVSEC_TABLE 0xC +#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) +#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) +#define TABLE_OFFSET_SHIFT 3 + +static DEFINE_IDA(intel_vsec_ida); + +/** + * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. + * @rev: Revision ID of the VSEC/DVSEC register space + * @length: Length of the VSEC/DVSEC register space + * @id: ID of the feature + * @num_entries: Number of instances of the feature + * @entry_size: Size of the discovery table for each feature + * @tbir: BAR containing the discovery tables + * @offset: BAR offset of start of the first discovery table + */ +struct intel_vsec_header { + u8 rev; + u16 length; + u16 id; + u8 num_entries; + u8 entry_size; + u8 tbir; + u32 offset; +}; + +/* Platform specific data */ +struct intel_vsec_platform_info { + struct intel_vsec_header **capabilities; + unsigned long quirks; +}; + +enum intel_vsec_id { + VSEC_ID_TELEMETRY = 2, + VSEC_ID_WATCHER = 3, + VSEC_ID_CRASHLOG = 4, +}; + +static enum intel_vsec_id intel_vsec_allow_list[] = { + VSEC_ID_TELEMETRY, + VSEC_ID_WATCHER, + VSEC_ID_CRASHLOG, +}; + +static const char *intel_vsec_name(enum intel_vsec_id id) +{ + switch (id) { + case VSEC_ID_TELEMETRY: + return "telemetry"; + + case VSEC_ID_WATCHER: + return "watcher"; + + case VSEC_ID_CRASHLOG: + return "crashlog"; + + default: + return NULL; + } +} + +static bool intel_vsec_allowed(u16 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++) + if (intel_vsec_allow_list[i] == id) + return true; + + return false; +} + +static bool intel_vsec_disabled(u16 id, unsigned long quirks) +{ + switch (id) { + case VSEC_ID_WATCHER: + return !!(quirks & VSEC_QUIRK_NO_WATCHER); + + case VSEC_ID_CRASHLOG: + return !!(quirks & VSEC_QUIRK_NO_CRASHLOG); + + default: + return false; + } +} + +static void intel_vsec_remove_aux(void *data) +{ + auxiliary_device_delete(data); + auxiliary_device_uninit(data); +} + +static void intel_vsec_dev_release(struct device *dev) +{ + struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); + + ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); +} + +static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *intel_vsec_dev, + const char *name) +{ + struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; + int ret; + + ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); + if (ret < 0) { + kfree(intel_vsec_dev); + return ret; + } + + auxdev->id = ret; + auxdev->name = name; + auxdev->dev.parent = &pdev->dev; + auxdev->dev.release = intel_vsec_dev_release; + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + ida_free(intel_vsec_dev->ida, auxdev->id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); + return ret; + } + + ret = auxiliary_device_add(auxdev); + if (ret < 0) { + auxiliary_device_uninit(auxdev); + return ret; + } + + return devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, auxdev); +} + +static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, + unsigned long quirks) +{ + struct intel_vsec_device *intel_vsec_dev; + struct resource *res, *tmp; + int i; + + if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) + 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) { + kfree(intel_vsec_dev); + return -ENOMEM; + } + + if (quirks & VSEC_QUIRK_TABLE_SHIFT) + header->offset >>= TABLE_OFFSET_SHIFT; + + /* + * 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 = pdev->resource[header->tbir].start + + header->offset + i * (header->entry_size * sizeof(u32)); + tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; + tmp->flags = IORESOURCE_MEM; + } + + intel_vsec_dev->pcidev = pdev; + intel_vsec_dev->resource = res; + intel_vsec_dev->num_resources = header->num_entries; + intel_vsec_dev->quirks = quirks; + intel_vsec_dev->ida = &intel_vsec_ida; + + return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id)); +} + +static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, + struct intel_vsec_header **header) +{ + bool have_devices = false; + int ret; + + for ( ; *header; header++) { + ret = intel_vsec_add_dev(pdev, *header, quirks); + if (ret) + dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", + (*header)->id); + else + have_devices = true; + } + + return have_devices; +} + +static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) +{ + bool have_devices = false; + int pos = 0; + + do { + struct intel_vsec_header header; + u32 table, hdr; + u16 vid; + int ret; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); + if (!pos) + break; + + pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER1, &hdr); + vid = PCI_DVSEC_HEADER1_VID(hdr); + if (vid != PCI_VENDOR_ID_INTEL) + continue; + + /* Support only revision 1 */ + header.rev = PCI_DVSEC_HEADER1_REV(hdr); + if (header.rev != 1) { + dev_info(&pdev->dev, "Unsupported DVSEC revision %d\n", header.rev); + continue; + } + + header.length = PCI_DVSEC_HEADER1_LEN(hdr); + + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); + header.id = PCI_DVSEC_HEADER2_ID(hdr); + + ret = intel_vsec_add_dev(pdev, &header, quirks); + if (ret) + continue; + + have_devices = true; + } while (true); + + return have_devices; +} + +static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) +{ + bool have_devices = false; + int pos = 0; + + do { + struct intel_vsec_header header; + u32 table, hdr; + int ret; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_VNDR); + if (!pos) + break; + + pci_read_config_dword(pdev, pos + PCI_VNDR_HEADER, &hdr); + + /* Support only revision 1 */ + header.rev = PCI_VNDR_HEADER_REV(hdr); + if (header.rev != 1) { + dev_info(&pdev->dev, "Unsupported VSEC revision %d\n", header.rev); + continue; + } + + header.id = PCI_VNDR_HEADER_ID(hdr); + header.length = PCI_VNDR_HEADER_LEN(hdr); + + /* entry, size, and table offset are the same as DVSEC */ + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + ret = intel_vsec_add_dev(pdev, &header, quirks); + if (ret) + continue; + + have_devices = true; + } while (true); + + return have_devices; +} + +static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct intel_vsec_platform_info *info; + bool have_devices = false; + unsigned long quirks = 0; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + info = (struct intel_vsec_platform_info *)id->driver_data; + if (info) + quirks = info->quirks; + + if (intel_vsec_walk_dvsec(pdev, quirks)) + have_devices = true; + + if (intel_vsec_walk_vsec(pdev, quirks)) + have_devices = true; + + if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && + intel_vsec_walk_header(pdev, quirks, info->capabilities)) + have_devices = true; + + if (!have_devices) + return -ENODEV; + + return 0; +} + +/* TGL info */ +static const struct intel_vsec_platform_info tgl_info = { + .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | VSEC_QUIRK_TABLE_SHIFT, +}; + +/* DG1 info */ +static struct intel_vsec_header dg1_telemetry = { + .length = 0x10, + .id = 2, + .num_entries = 1, + .entry_size = 3, + .tbir = 0, + .offset = 0x466000, +}; + +static struct intel_vsec_header *dg1_capabilities[] = { + &dg1_telemetry, + NULL +}; + +static const struct intel_vsec_platform_info dg1_info = { + .capabilities = dg1_capabilities, + .quirks = VSEC_QUIRK_NO_DVSEC, +}; + +#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d +#define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e +#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 +#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d +static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, NULL) }, + { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, + { } +}; +MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); + +static struct pci_driver intel_vsec_pci_driver = { + .name = "intel_vsec", + .id_table = intel_vsec_pci_ids, + .probe = intel_vsec_pci_probe, +}; +module_pci_driver(intel_vsec_pci_driver); + +MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Extended Capabilities auxiliary bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h new file mode 100644 index 000000000000..4cc36678e8c5 --- /dev/null +++ b/drivers/platform/x86/intel/vsec.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _VSEC_H +#define _VSEC_H + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> + +struct pci_dev; +struct resource; + +enum intel_vsec_quirks { + /* Watcher feature not supported */ + VSEC_QUIRK_NO_WATCHER = BIT(0), + + /* Crashlog feature not supported */ + VSEC_QUIRK_NO_CRASHLOG = BIT(1), + + /* Use shift instead of mask to read discovery table offset */ + VSEC_QUIRK_TABLE_SHIFT = BIT(2), + + /* DVSEC not present (provided in driver data) */ + VSEC_QUIRK_NO_DVSEC = BIT(3), +}; + +struct intel_vsec_device { + struct auxiliary_device auxdev; + struct pci_dev *pcidev; + struct resource *resource; + struct ida *ida; + unsigned long quirks; + int num_resources; +}; + +static inline struct intel_vsec_device *dev_to_ivdev(struct device *dev) +{ + return container_of(dev, struct intel_vsec_device, auxdev.dev); +} + +static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device *auxdev) +{ + return container_of(auxdev, struct intel_vsec_device, auxdev); +} +#endif |