1 // SPDX-License-Identifier: GPL-2.0
3 * Intel Platform Monitory Technology Telemetry driver
5 * Copyright (c) 2020, Intel Corporation.
8 * Author: "David E. Box" <david.e.box@linux.intel.com>
11 #include <linux/auxiliary_bus.h>
12 #include <linux/intel_vsec.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/pci.h>
16 #include <linux/slab.h>
17 #include <linux/uaccess.h>
18 #include <linux/overflow.h>
22 #define TELEM_SIZE_OFFSET 0x0
23 #define TELEM_GUID_OFFSET 0x4
24 #define TELEM_BASE_OFFSET 0x8
25 #define TELEM_ACCESS(v) ((v) & GENMASK(3, 0))
26 #define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4)
27 /* size is in bytes */
28 #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10)
30 /* Used by client hardware to identify a fixed telemetry entry*/
31 #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000
33 #define NUM_BYTES_QWORD(v) ((v) << 3)
34 #define SAMPLE_ID_OFFSET(v) ((v) << 3)
36 #define NUM_BYTES_DWORD(v) ((v) << 2)
37 #define SAMPLE_ID_OFFSET32(v) ((v) << 2)
39 /* Protects access to the xarray of telemetry endpoint handles */
40 static DEFINE_MUTEX(ep_lock
);
45 TELEM_TYPE_PUNIT_FIXED
,
48 struct pmt_telem_priv
{
50 struct intel_pmt_entry entry
[];
53 static bool pmt_telem_region_overlaps(struct intel_pmt_entry
*entry
,
56 u32 guid
= readl(entry
->disc_table
+ TELEM_GUID_OFFSET
);
58 if (intel_pmt_is_early_client_hw(dev
)) {
59 u32 type
= TELEM_TYPE(readl(entry
->disc_table
));
61 if ((type
== TELEM_TYPE_PUNIT_FIXED
) ||
62 (guid
== TELEM_CLIENT_FIXED_BLOCK_GUID
))
69 static int pmt_telem_header_decode(struct intel_pmt_entry
*entry
,
72 void __iomem
*disc_table
= entry
->disc_table
;
73 struct intel_pmt_header
*header
= &entry
->header
;
75 if (pmt_telem_region_overlaps(entry
, dev
))
78 header
->access_type
= TELEM_ACCESS(readl(disc_table
));
79 header
->guid
= readl(disc_table
+ TELEM_GUID_OFFSET
);
80 header
->base_offset
= readl(disc_table
+ TELEM_BASE_OFFSET
);
82 /* Size is measured in DWORDS, but accessor returns bytes */
83 header
->size
= TELEM_SIZE(readl(disc_table
));
86 * Some devices may expose non-functioning entries that are
87 * reserved for future use. They have zero size. Do not fail
88 * probe for these. Just ignore them.
90 if (header
->size
== 0 || header
->access_type
== 0xF)
96 static int pmt_telem_add_endpoint(struct intel_vsec_device
*ivdev
,
97 struct intel_pmt_entry
*entry
)
99 struct telem_endpoint
*ep
;
101 /* Endpoint lifetimes are managed by kref, not devres */
102 entry
->ep
= kzalloc(sizeof(*(entry
->ep
)), GFP_KERNEL
);
107 ep
->pcidev
= ivdev
->pcidev
;
108 ep
->header
.access_type
= entry
->header
.access_type
;
109 ep
->header
.guid
= entry
->header
.guid
;
110 ep
->header
.base_offset
= entry
->header
.base_offset
;
111 ep
->header
.size
= entry
->header
.size
;
112 ep
->base
= entry
->base
;
114 ep
->cb
= ivdev
->priv_data
;
116 kref_init(&ep
->kref
);
121 static DEFINE_XARRAY_ALLOC(telem_array
);
122 static struct intel_pmt_namespace pmt_telem_ns
= {
125 .pmt_header_decode
= pmt_telem_header_decode
,
126 .pmt_add_endpoint
= pmt_telem_add_endpoint
,
129 /* Called when all users unregister and the device is removed */
130 static void pmt_telem_ep_release(struct kref
*kref
)
132 struct telem_endpoint
*ep
;
134 ep
= container_of(kref
, struct telem_endpoint
, kref
);
138 unsigned long pmt_telem_get_next_endpoint(unsigned long start
)
140 struct intel_pmt_entry
*entry
;
141 unsigned long found_idx
;
143 mutex_lock(&ep_lock
);
144 xa_for_each_start(&telem_array
, found_idx
, entry
, start
) {
146 * Return first found index after start.
149 if (found_idx
> start
)
152 mutex_unlock(&ep_lock
);
154 return found_idx
== start
? 0 : found_idx
;
156 EXPORT_SYMBOL_NS_GPL(pmt_telem_get_next_endpoint
, "INTEL_PMT_TELEMETRY");
158 struct telem_endpoint
*pmt_telem_register_endpoint(int devid
)
160 struct intel_pmt_entry
*entry
;
161 unsigned long index
= devid
;
163 mutex_lock(&ep_lock
);
164 entry
= xa_find(&telem_array
, &index
, index
, XA_PRESENT
);
166 mutex_unlock(&ep_lock
);
167 return ERR_PTR(-ENXIO
);
170 kref_get(&entry
->ep
->kref
);
171 mutex_unlock(&ep_lock
);
175 EXPORT_SYMBOL_NS_GPL(pmt_telem_register_endpoint
, "INTEL_PMT_TELEMETRY");
177 void pmt_telem_unregister_endpoint(struct telem_endpoint
*ep
)
179 kref_put(&ep
->kref
, pmt_telem_ep_release
);
181 EXPORT_SYMBOL_NS_GPL(pmt_telem_unregister_endpoint
, "INTEL_PMT_TELEMETRY");
183 int pmt_telem_get_endpoint_info(int devid
, struct telem_endpoint_info
*info
)
185 struct intel_pmt_entry
*entry
;
186 unsigned long index
= devid
;
192 mutex_lock(&ep_lock
);
193 entry
= xa_find(&telem_array
, &index
, index
, XA_PRESENT
);
199 info
->pdev
= entry
->ep
->pcidev
;
200 info
->header
= entry
->ep
->header
;
203 mutex_unlock(&ep_lock
);
207 EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info
, "INTEL_PMT_TELEMETRY");
209 int pmt_telem_read(struct telem_endpoint
*ep
, u32 id
, u64
*data
, u32 count
)
216 offset
= SAMPLE_ID_OFFSET(id
);
217 size
= ep
->header
.size
;
219 if (offset
+ NUM_BYTES_QWORD(count
) > size
)
222 pmt_telem_read_mmio(ep
->pcidev
, ep
->cb
, ep
->header
.guid
, data
, ep
->base
, offset
,
223 NUM_BYTES_QWORD(count
));
225 return ep
->present
? 0 : -EPIPE
;
227 EXPORT_SYMBOL_NS_GPL(pmt_telem_read
, "INTEL_PMT_TELEMETRY");
229 int pmt_telem_read32(struct telem_endpoint
*ep
, u32 id
, u32
*data
, u32 count
)
236 offset
= SAMPLE_ID_OFFSET32(id
);
237 size
= ep
->header
.size
;
239 if (offset
+ NUM_BYTES_DWORD(count
) > size
)
242 memcpy_fromio(data
, ep
->base
+ offset
, NUM_BYTES_DWORD(count
));
244 return ep
->present
? 0 : -EPIPE
;
246 EXPORT_SYMBOL_NS_GPL(pmt_telem_read32
, "INTEL_PMT_TELEMETRY");
248 struct telem_endpoint
*
249 pmt_telem_find_and_register_endpoint(struct pci_dev
*pcidev
, u32 guid
, u16 pos
)
255 while ((devid
= pmt_telem_get_next_endpoint(devid
))) {
256 struct telem_endpoint_info ep_info
;
258 err
= pmt_telem_get_endpoint_info(devid
, &ep_info
);
262 if (ep_info
.header
.guid
== guid
&& ep_info
.pdev
== pcidev
) {
264 return pmt_telem_register_endpoint(devid
);
269 return ERR_PTR(-ENXIO
);
271 EXPORT_SYMBOL_NS_GPL(pmt_telem_find_and_register_endpoint
, "INTEL_PMT_TELEMETRY");
273 static void pmt_telem_remove(struct auxiliary_device
*auxdev
)
275 struct pmt_telem_priv
*priv
= auxiliary_get_drvdata(auxdev
);
278 mutex_lock(&ep_lock
);
279 for (i
= 0; i
< priv
->num_entries
; i
++) {
280 struct intel_pmt_entry
*entry
= &priv
->entry
[i
];
282 kref_put(&entry
->ep
->kref
, pmt_telem_ep_release
);
283 intel_pmt_dev_destroy(entry
, &pmt_telem_ns
);
285 mutex_unlock(&ep_lock
);
288 static int pmt_telem_probe(struct auxiliary_device
*auxdev
, const struct auxiliary_device_id
*id
)
290 struct intel_vsec_device
*intel_vsec_dev
= auxdev_to_ivdev(auxdev
);
291 struct pmt_telem_priv
*priv
;
295 size
= struct_size(priv
, entry
, intel_vsec_dev
->num_resources
);
296 priv
= devm_kzalloc(&auxdev
->dev
, size
, GFP_KERNEL
);
300 auxiliary_set_drvdata(auxdev
, priv
);
302 for (i
= 0; i
< intel_vsec_dev
->num_resources
; i
++) {
303 struct intel_pmt_entry
*entry
= &priv
->entry
[priv
->num_entries
];
305 mutex_lock(&ep_lock
);
306 ret
= intel_pmt_dev_create(entry
, &pmt_telem_ns
, intel_vsec_dev
, i
);
307 mutex_unlock(&ep_lock
);
318 pmt_telem_remove(auxdev
);
322 static const struct auxiliary_device_id pmt_telem_id_table
[] = {
323 { .name
= "intel_vsec.telemetry" },
326 MODULE_DEVICE_TABLE(auxiliary
, pmt_telem_id_table
);
328 static struct auxiliary_driver pmt_telem_aux_driver
= {
329 .id_table
= pmt_telem_id_table
,
330 .remove
= pmt_telem_remove
,
331 .probe
= pmt_telem_probe
,
334 static int __init
pmt_telem_init(void)
336 return auxiliary_driver_register(&pmt_telem_aux_driver
);
338 module_init(pmt_telem_init
);
340 static void __exit
pmt_telem_exit(void)
342 auxiliary_driver_unregister(&pmt_telem_aux_driver
);
343 xa_destroy(&telem_array
);
345 module_exit(pmt_telem_exit
);
347 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
348 MODULE_DESCRIPTION("Intel PMT Telemetry driver");
349 MODULE_LICENSE("GPL v2");
350 MODULE_IMPORT_NS("INTEL_PMT");