Merge tag 'block-5.11-2021-01-10' of git://git.kernel.dk/linux-block
[linux/fpc-iii.git] / drivers / mfd / intel_pmt.c
blob744b230cdccaa4fa096cb3b74b30c9a936eeb940
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Intel Platform Monitoring Technology PMT driver
5 * Copyright (c) 2020, Intel Corporation.
6 * All Rights Reserved.
8 * Author: David E. Box <david.e.box@linux.intel.com>
9 */
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>
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 {
35 u16 length;
36 u16 id;
37 u8 num_entries;
38 u8 entry_size;
39 u8 tbir;
40 u32 offset;
43 enum pmt_quirks {
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 {
55 unsigned long quirks;
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,
64 unsigned long quirks)
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;
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;
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;
91 name = "pmt_crashlog";
92 break;
93 default:
94 dev_err(dev, "Unrecognized PMT capability: %d\n", id);
95 return -EINVAL;
98 if (!header->num_entries || !header->entry_size) {
99 dev_err(dev, "Invalid count or size for %s header\n", name);
100 return -EINVAL;
103 cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL);
104 if (!cell)
105 return -ENOMEM;
107 res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL);
108 if (!res)
109 return -ENOMEM;
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;
129 cell->name = name;
131 return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0,
132 NULL);
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;
140 int ret, pos = 0;
142 ret = pcim_enable_device(pdev);
143 if (ret)
144 return ret;
146 info = (struct pmt_platform_info *)id->driver_data;
148 if (info)
149 quirks = info->quirks;
151 do {
152 struct intel_dvsec_header header;
153 u32 table;
154 u16 vid;
156 pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC);
157 if (!pos)
158 break;
160 pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid);
161 if (vid != PCI_VENDOR_ID_INTEL)
162 continue;
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);
173 header.tbir = INTEL_DVSEC_TABLE_BAR(table);
174 header.offset = INTEL_DVSEC_TABLE_OFFSET(table);
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;
184 found_devices = true;
185 } while (true);
187 if (!found_devices)
188 return -ENODEV;
190 pm_runtime_put(&pdev->dev);
191 pm_runtime_allow(&pdev->dev);
193 return 0;
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 = {
214 .name = "intel-pmt",
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");