Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: Intel Platform Monitoring Technology support

Intel Platform Monitoring Technology (PMT) is an architecture for
enumerating and accessing hardware monitoring facilities. PMT supports
multiple types of monitoring capabilities. This driver creates platform
devices for each type so that they may be managed by capability specific
drivers (to be introduced). Capabilities are discovered using PCIe DVSEC
ids. Support is included for the 3 current capability types, Telemetry,
Watcher, and Crashlog. The features are available on new Intel platforms
starting from Tiger Lake for which support is added. This patch adds
support for Tiger Lake (TGL), Alder Lake (ADL), and Out-of-Band Management
Services Module (OOBMSM).

Also add a quirk mechanism for several early hardware differences and bugs.
For Tiger Lake and Alder Lake, do not support Watcher and Crashlog
capabilities since they will not be compatible with future product. Also,
fix use a quirk to fix the discovery table offset.

Co-developed-by: Alexander Duyck <alexander.h.duyck@linux.intel.com>
Signed-off-by: Alexander Duyck <alexander.h.duyck@linux.intel.com>
Signed-off-by: David E. Box <david.e.box@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

David E. Box and committed by
Lee Jones
4f8217d5 1dc2da5c

+239
+5
MAINTAINERS
··· 9021 9021 F: include/linux/mfd/intel_msic.h 9022 9022 F: include/linux/mfd/intel_soc_pmic* 9023 9023 9024 + INTEL PMT DRIVER 9025 + M: "David E. Box" <david.e.box@linux.intel.com> 9026 + S: Maintained 9027 + F: drivers/mfd/intel_pmt.c 9028 + 9024 9029 INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT 9025 9030 M: Stanislav Yakovlev <stas.yakovlev@gmail.com> 9026 9031 L: linux-wireless@vger.kernel.org
+10
drivers/mfd/Kconfig
··· 682 682 Register and P-unit access. In addition this creates devices 683 683 for iTCO watchdog and telemetry that are part of the PMC. 684 684 685 + config MFD_INTEL_PMT 686 + tristate "Intel Platform Monitoring Technology (PMT) support" 687 + depends on PCI 688 + select MFD_CORE 689 + help 690 + The Intel Platform Monitoring Technology (PMT) is an interface that 691 + provides access to hardware monitor registers. This driver supports 692 + Telemetry, Watcher, and Crashlog PMT capabilities/devices for 693 + platforms starting from Tiger Lake. 694 + 685 695 config MFD_IPAQ_MICRO 686 696 bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" 687 697 depends on SA1100_H3100 || SA1100_H3600
+1
drivers/mfd/Makefile
··· 216 216 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o 217 217 obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o 218 218 obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o 219 + obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o 219 220 obj-$(CONFIG_MFD_PALMAS) += palmas.o 220 221 obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o 221 222 obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
+223
drivers/mfd/intel_pmt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Intel Platform Monitoring Technology PMT driver 4 + * 5 + * Copyright (c) 2020, Intel Corporation. 6 + * All Rights Reserved. 7 + * 8 + * Author: David E. Box <david.e.box@linux.intel.com> 9 + */ 10 + 11 + #include <linux/bits.h> 12 + #include <linux/kernel.h> 13 + #include <linux/mfd/core.h> 14 + #include <linux/module.h> 15 + #include <linux/pci.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/pm.h> 18 + #include <linux/pm_runtime.h> 19 + #include <linux/types.h> 20 + 21 + /* Intel DVSEC capability vendor space offsets */ 22 + #define INTEL_DVSEC_ENTRIES 0xA 23 + #define INTEL_DVSEC_SIZE 0xB 24 + #define INTEL_DVSEC_TABLE 0xC 25 + #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) 26 + #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) 27 + #define INTEL_DVSEC_ENTRY_SIZE 4 28 + 29 + /* PMT capabilities */ 30 + #define DVSEC_INTEL_ID_TELEMETRY 2 31 + #define DVSEC_INTEL_ID_WATCHER 3 32 + #define DVSEC_INTEL_ID_CRASHLOG 4 33 + 34 + struct intel_dvsec_header { 35 + u16 length; 36 + u16 id; 37 + u8 num_entries; 38 + u8 entry_size; 39 + u8 tbir; 40 + u32 offset; 41 + }; 42 + 43 + enum pmt_quirks { 44 + /* Watcher capability not supported */ 45 + PMT_QUIRK_NO_WATCHER = BIT(0), 46 + 47 + /* Crashlog capability not supported */ 48 + PMT_QUIRK_NO_CRASHLOG = BIT(1), 49 + 50 + /* Use shift instead of mask to read discovery table offset */ 51 + PMT_QUIRK_TABLE_SHIFT = BIT(2), 52 + }; 53 + 54 + struct pmt_platform_info { 55 + unsigned long quirks; 56 + }; 57 + 58 + static const struct pmt_platform_info tgl_info = { 59 + .quirks = PMT_QUIRK_NO_WATCHER | PMT_QUIRK_NO_CRASHLOG | 60 + PMT_QUIRK_TABLE_SHIFT, 61 + }; 62 + 63 + static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header, 64 + unsigned long quirks) 65 + { 66 + struct device *dev = &pdev->dev; 67 + struct resource *res, *tmp; 68 + struct mfd_cell *cell; 69 + const char *name; 70 + int count = header->num_entries; 71 + int size = header->entry_size; 72 + int id = header->id; 73 + int i; 74 + 75 + switch (id) { 76 + case DVSEC_INTEL_ID_TELEMETRY: 77 + name = "pmt_telemetry"; 78 + break; 79 + case DVSEC_INTEL_ID_WATCHER: 80 + if (quirks & PMT_QUIRK_NO_WATCHER) { 81 + dev_info(dev, "Watcher not supported\n"); 82 + return 0; 83 + } 84 + name = "pmt_watcher"; 85 + break; 86 + case DVSEC_INTEL_ID_CRASHLOG: 87 + if (quirks & PMT_QUIRK_NO_CRASHLOG) { 88 + dev_info(dev, "Crashlog not supported\n"); 89 + return 0; 90 + } 91 + name = "pmt_crashlog"; 92 + break; 93 + default: 94 + dev_err(dev, "Unrecognized PMT capability: %d\n", id); 95 + return -EINVAL; 96 + } 97 + 98 + if (!header->num_entries || !header->entry_size) { 99 + dev_err(dev, "Invalid count or size for %s header\n", name); 100 + return -EINVAL; 101 + } 102 + 103 + cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL); 104 + if (!cell) 105 + return -ENOMEM; 106 + 107 + res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL); 108 + if (!res) 109 + return -ENOMEM; 110 + 111 + if (quirks & PMT_QUIRK_TABLE_SHIFT) 112 + header->offset >>= 3; 113 + 114 + /* 115 + * The PMT DVSEC contains the starting offset and count for a block of 116 + * discovery tables, each providing access to monitoring facilities for 117 + * a section of the device. Create a resource list of these tables to 118 + * provide to the driver. 119 + */ 120 + for (i = 0, tmp = res; i < count; i++, tmp++) { 121 + tmp->start = pdev->resource[header->tbir].start + 122 + header->offset + i * (size << 2); 123 + tmp->end = tmp->start + (size << 2) - 1; 124 + tmp->flags = IORESOURCE_MEM; 125 + } 126 + 127 + cell->resources = res; 128 + cell->num_resources = count; 129 + cell->name = name; 130 + 131 + return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, 132 + NULL); 133 + } 134 + 135 + static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 136 + { 137 + struct pmt_platform_info *info; 138 + unsigned long quirks = 0; 139 + bool found_devices = false; 140 + int ret, pos = 0; 141 + 142 + ret = pcim_enable_device(pdev); 143 + if (ret) 144 + return ret; 145 + 146 + info = (struct pmt_platform_info *)id->driver_data; 147 + 148 + if (info) 149 + quirks = info->quirks; 150 + 151 + do { 152 + struct intel_dvsec_header header; 153 + u32 table; 154 + u16 vid; 155 + 156 + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); 157 + if (!pos) 158 + break; 159 + 160 + pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid); 161 + if (vid != PCI_VENDOR_ID_INTEL) 162 + continue; 163 + 164 + pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, 165 + &header.id); 166 + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, 167 + &header.num_entries); 168 + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, 169 + &header.entry_size); 170 + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, 171 + &table); 172 + 173 + header.tbir = INTEL_DVSEC_TABLE_BAR(table); 174 + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 175 + 176 + ret = pmt_add_dev(pdev, &header, quirks); 177 + if (ret) { 178 + dev_warn(&pdev->dev, 179 + "Failed to add device for DVSEC id %d\n", 180 + header.id); 181 + continue; 182 + } 183 + 184 + found_devices = true; 185 + } while (true); 186 + 187 + if (!found_devices) 188 + return -ENODEV; 189 + 190 + pm_runtime_put(&pdev->dev); 191 + pm_runtime_allow(&pdev->dev); 192 + 193 + return 0; 194 + } 195 + 196 + static void pmt_pci_remove(struct pci_dev *pdev) 197 + { 198 + pm_runtime_forbid(&pdev->dev); 199 + pm_runtime_get_sync(&pdev->dev); 200 + } 201 + 202 + #define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d 203 + #define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7 204 + #define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d 205 + static const struct pci_device_id pmt_pci_ids[] = { 206 + { PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) }, 207 + { PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) }, 208 + { PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) }, 209 + { } 210 + }; 211 + MODULE_DEVICE_TABLE(pci, pmt_pci_ids); 212 + 213 + static struct pci_driver pmt_pci_driver = { 214 + .name = "intel-pmt", 215 + .id_table = pmt_pci_ids, 216 + .probe = pmt_pci_probe, 217 + .remove = pmt_pci_remove, 218 + }; 219 + module_pci_driver(pmt_pci_driver); 220 + 221 + MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 222 + MODULE_DESCRIPTION("Intel Platform Monitoring Technology PMT driver"); 223 + MODULE_LICENSE("GPL v2");