perf tests: Skip 'perf stat metrics (shadow stat) test' for hybrid
[linux-2.6-microblaze.git] / 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");