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

Configure Feed

Select the types of activity you want to include in your feed.

at v5.13-rc2 261 lines 6.3 kB view raw
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 34struct 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 43enum 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 /* DVSEC not present (provided in driver data) */ 54 PMT_QUIRK_NO_DVSEC = BIT(3), 55}; 56 57struct pmt_platform_info { 58 unsigned long quirks; 59 struct intel_dvsec_header **capabilities; 60}; 61 62static const struct pmt_platform_info tgl_info = { 63 .quirks = PMT_QUIRK_NO_WATCHER | PMT_QUIRK_NO_CRASHLOG | 64 PMT_QUIRK_TABLE_SHIFT, 65}; 66 67/* DG1 Platform with DVSEC quirk*/ 68static struct intel_dvsec_header dg1_telemetry = { 69 .length = 0x10, 70 .id = 2, 71 .num_entries = 1, 72 .entry_size = 3, 73 .tbir = 0, 74 .offset = 0x466000, 75}; 76 77static struct intel_dvsec_header *dg1_capabilities[] = { 78 &dg1_telemetry, 79 NULL 80}; 81 82static const struct pmt_platform_info dg1_info = { 83 .quirks = PMT_QUIRK_NO_DVSEC, 84 .capabilities = dg1_capabilities, 85}; 86 87static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header, 88 unsigned long quirks) 89{ 90 struct device *dev = &pdev->dev; 91 struct resource *res, *tmp; 92 struct mfd_cell *cell; 93 const char *name; 94 int count = header->num_entries; 95 int size = header->entry_size; 96 int id = header->id; 97 int i; 98 99 switch (id) { 100 case DVSEC_INTEL_ID_TELEMETRY: 101 name = "pmt_telemetry"; 102 break; 103 case DVSEC_INTEL_ID_WATCHER: 104 if (quirks & PMT_QUIRK_NO_WATCHER) { 105 dev_info(dev, "Watcher not supported\n"); 106 return -EINVAL; 107 } 108 name = "pmt_watcher"; 109 break; 110 case DVSEC_INTEL_ID_CRASHLOG: 111 if (quirks & PMT_QUIRK_NO_CRASHLOG) { 112 dev_info(dev, "Crashlog not supported\n"); 113 return -EINVAL; 114 } 115 name = "pmt_crashlog"; 116 break; 117 default: 118 return -EINVAL; 119 } 120 121 if (!header->num_entries || !header->entry_size) { 122 dev_err(dev, "Invalid count or size for %s header\n", name); 123 return -EINVAL; 124 } 125 126 cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL); 127 if (!cell) 128 return -ENOMEM; 129 130 res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL); 131 if (!res) 132 return -ENOMEM; 133 134 if (quirks & PMT_QUIRK_TABLE_SHIFT) 135 header->offset >>= 3; 136 137 /* 138 * The PMT DVSEC contains the starting offset and count for a block of 139 * discovery tables, each providing access to monitoring facilities for 140 * a section of the device. Create a resource list of these tables to 141 * provide to the driver. 142 */ 143 for (i = 0, tmp = res; i < count; i++, tmp++) { 144 tmp->start = pdev->resource[header->tbir].start + 145 header->offset + i * (size << 2); 146 tmp->end = tmp->start + (size << 2) - 1; 147 tmp->flags = IORESOURCE_MEM; 148 } 149 150 cell->resources = res; 151 cell->num_resources = count; 152 cell->name = name; 153 154 return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, 155 NULL); 156} 157 158static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 159{ 160 struct pmt_platform_info *info; 161 unsigned long quirks = 0; 162 bool found_devices = false; 163 int ret, pos = 0; 164 165 ret = pcim_enable_device(pdev); 166 if (ret) 167 return ret; 168 169 info = (struct pmt_platform_info *)id->driver_data; 170 171 if (info) 172 quirks = info->quirks; 173 174 if (info && (info->quirks & PMT_QUIRK_NO_DVSEC)) { 175 struct intel_dvsec_header **header; 176 177 header = info->capabilities; 178 while (*header) { 179 ret = pmt_add_dev(pdev, *header, quirks); 180 if (ret) 181 dev_warn(&pdev->dev, 182 "Failed to add device for DVSEC id %d\n", 183 (*header)->id); 184 else 185 found_devices = true; 186 187 ++header; 188 } 189 } else { 190 do { 191 struct intel_dvsec_header header; 192 u32 table; 193 u16 vid; 194 195 pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); 196 if (!pos) 197 break; 198 199 pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid); 200 if (vid != PCI_VENDOR_ID_INTEL) 201 continue; 202 203 pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, 204 &header.id); 205 pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, 206 &header.num_entries); 207 pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, 208 &header.entry_size); 209 pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, 210 &table); 211 212 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 213 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 214 215 ret = pmt_add_dev(pdev, &header, quirks); 216 if (ret) 217 continue; 218 219 found_devices = true; 220 } while (true); 221 } 222 223 if (!found_devices) 224 return -ENODEV; 225 226 pm_runtime_put(&pdev->dev); 227 pm_runtime_allow(&pdev->dev); 228 229 return 0; 230} 231 232static void pmt_pci_remove(struct pci_dev *pdev) 233{ 234 pm_runtime_forbid(&pdev->dev); 235 pm_runtime_get_sync(&pdev->dev); 236} 237 238#define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d 239#define PCI_DEVICE_ID_INTEL_PMT_DG1 0x490e 240#define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7 241#define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d 242static const struct pci_device_id pmt_pci_ids[] = { 243 { PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) }, 244 { PCI_DEVICE_DATA(INTEL, PMT_DG1, &dg1_info) }, 245 { PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) }, 246 { PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) }, 247 { } 248}; 249MODULE_DEVICE_TABLE(pci, pmt_pci_ids); 250 251static struct pci_driver pmt_pci_driver = { 252 .name = "intel-pmt", 253 .id_table = pmt_pci_ids, 254 .probe = pmt_pci_probe, 255 .remove = pmt_pci_remove, 256}; 257module_pci_driver(pmt_pci_driver); 258 259MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 260MODULE_DESCRIPTION("Intel Platform Monitoring Technology PMT driver"); 261MODULE_LICENSE("GPL v2");