1 // SPDX-License-Identifier: GPL-2.0
3 * Intel Platform Monitoring Technology PMT driver
5 * Copyright (c) 2020, Intel Corporation.
8 * Author: David E. Box <david.e.box@linux.intel.com>
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>
18 #include <linux/pm_runtime.h>
19 #include <linux/types.h>
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
29 /* PMT capabilities */
30 #define DVSEC_INTEL_ID_TELEMETRY 2
31 #define DVSEC_INTEL_ID_WATCHER 3
32 #define DVSEC_INTEL_ID_CRASHLOG 4
34 struct intel_dvsec_header
{
44 /* Watcher capability not supported */
45 PMT_QUIRK_NO_WATCHER
= BIT(0),
47 /* Crashlog capability not supported */
48 PMT_QUIRK_NO_CRASHLOG
= BIT(1),
50 /* Use shift instead of mask to read discovery table offset */
51 PMT_QUIRK_TABLE_SHIFT
= BIT(2),
54 struct pmt_platform_info
{
58 static const struct pmt_platform_info tgl_info
= {
59 .quirks
= PMT_QUIRK_NO_WATCHER
| PMT_QUIRK_NO_CRASHLOG
|
60 PMT_QUIRK_TABLE_SHIFT
,
63 static int pmt_add_dev(struct pci_dev
*pdev
, struct intel_dvsec_header
*header
,
66 struct device
*dev
= &pdev
->dev
;
67 struct resource
*res
, *tmp
;
68 struct mfd_cell
*cell
;
70 int count
= header
->num_entries
;
71 int size
= header
->entry_size
;
76 case DVSEC_INTEL_ID_TELEMETRY
:
77 name
= "pmt_telemetry";
79 case DVSEC_INTEL_ID_WATCHER
:
80 if (quirks
& PMT_QUIRK_NO_WATCHER
) {
81 dev_info(dev
, "Watcher not supported\n");
86 case DVSEC_INTEL_ID_CRASHLOG
:
87 if (quirks
& PMT_QUIRK_NO_CRASHLOG
) {
88 dev_info(dev
, "Crashlog not supported\n");
91 name
= "pmt_crashlog";
94 dev_err(dev
, "Unrecognized PMT capability: %d\n", id
);
98 if (!header
->num_entries
|| !header
->entry_size
) {
99 dev_err(dev
, "Invalid count or size for %s header\n", name
);
103 cell
= devm_kzalloc(dev
, sizeof(*cell
), GFP_KERNEL
);
107 res
= devm_kcalloc(dev
, count
, sizeof(*res
), GFP_KERNEL
);
111 if (quirks
& PMT_QUIRK_TABLE_SHIFT
)
112 header
->offset
>>= 3;
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.
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
;
127 cell
->resources
= res
;
128 cell
->num_resources
= count
;
131 return devm_mfd_add_devices(dev
, PLATFORM_DEVID_AUTO
, cell
, 1, NULL
, 0,
135 static int pmt_pci_probe(struct pci_dev
*pdev
, const struct pci_device_id
*id
)
137 struct pmt_platform_info
*info
;
138 unsigned long quirks
= 0;
139 bool found_devices
= false;
142 ret
= pcim_enable_device(pdev
);
146 info
= (struct pmt_platform_info
*)id
->driver_data
;
149 quirks
= info
->quirks
;
152 struct intel_dvsec_header header
;
156 pos
= pci_find_next_ext_capability(pdev
, pos
, PCI_EXT_CAP_ID_DVSEC
);
160 pci_read_config_word(pdev
, pos
+ PCI_DVSEC_HEADER1
, &vid
);
161 if (vid
!= PCI_VENDOR_ID_INTEL
)
164 pci_read_config_word(pdev
, pos
+ PCI_DVSEC_HEADER2
,
166 pci_read_config_byte(pdev
, pos
+ INTEL_DVSEC_ENTRIES
,
167 &header
.num_entries
);
168 pci_read_config_byte(pdev
, pos
+ INTEL_DVSEC_SIZE
,
170 pci_read_config_dword(pdev
, pos
+ INTEL_DVSEC_TABLE
,
173 header
.tbir
= INTEL_DVSEC_TABLE_BAR(table
);
174 header
.offset
= INTEL_DVSEC_TABLE_OFFSET(table
);
176 ret
= pmt_add_dev(pdev
, &header
, quirks
);
179 "Failed to add device for DVSEC id %d\n",
184 found_devices
= true;
190 pm_runtime_put(&pdev
->dev
);
191 pm_runtime_allow(&pdev
->dev
);
196 static void pmt_pci_remove(struct pci_dev
*pdev
)
198 pm_runtime_forbid(&pdev
->dev
);
199 pm_runtime_get_sync(&pdev
->dev
);
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
) },
211 MODULE_DEVICE_TABLE(pci
, pmt_pci_ids
);
213 static struct pci_driver pmt_pci_driver
= {
215 .id_table
= pmt_pci_ids
,
216 .probe
= pmt_pci_probe
,
217 .remove
= pmt_pci_remove
,
219 module_pci_driver(pmt_pci_driver
);
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");