xref: /linux/drivers/platform/x86/intel/pmt/telemetry.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1e184b1e5SDavid E. Box // SPDX-License-Identifier: GPL-2.0
2e184b1e5SDavid E. Box /*
3e184b1e5SDavid E. Box  * Intel Platform Monitory Technology Telemetry driver
4e184b1e5SDavid E. Box  *
5e184b1e5SDavid E. Box  * Copyright (c) 2020, Intel Corporation.
6e184b1e5SDavid E. Box  * All Rights Reserved.
7e184b1e5SDavid E. Box  *
8e184b1e5SDavid E. Box  * Author: "David E. Box" <david.e.box@linux.intel.com>
9e184b1e5SDavid E. Box  */
10e184b1e5SDavid E. Box 
11a3c8f906SDavid E. Box #include <linux/auxiliary_bus.h>
12fc9aef43SDavid E. Box #include <linux/intel_vsec.h>
13e184b1e5SDavid E. Box #include <linux/kernel.h>
14e184b1e5SDavid E. Box #include <linux/module.h>
15e184b1e5SDavid E. Box #include <linux/pci.h>
16e184b1e5SDavid E. Box #include <linux/slab.h>
17e184b1e5SDavid E. Box #include <linux/uaccess.h>
18e184b1e5SDavid E. Box #include <linux/overflow.h>
19e184b1e5SDavid E. Box 
20e184b1e5SDavid E. Box #include "class.h"
21e184b1e5SDavid E. Box 
22e184b1e5SDavid E. Box #define TELEM_SIZE_OFFSET	0x0
23e184b1e5SDavid E. Box #define TELEM_GUID_OFFSET	0x4
24e184b1e5SDavid E. Box #define TELEM_BASE_OFFSET	0x8
25e184b1e5SDavid E. Box #define TELEM_ACCESS(v)		((v) & GENMASK(3, 0))
26ba7e421eSDavid E. Box #define TELEM_TYPE(v)		(((v) & GENMASK(7, 4)) >> 4)
27e184b1e5SDavid E. Box /* size is in bytes */
28e184b1e5SDavid E. Box #define TELEM_SIZE(v)		(((v) & GENMASK(27, 12)) >> 10)
29e184b1e5SDavid E. Box 
30e184b1e5SDavid E. Box /* Used by client hardware to identify a fixed telemetry entry*/
31e184b1e5SDavid E. Box #define TELEM_CLIENT_FIXED_BLOCK_GUID	0x10000000
32e184b1e5SDavid E. Box 
33416eeb2eSDavid E. Box #define NUM_BYTES_QWORD(v)	((v) << 3)
34416eeb2eSDavid E. Box #define SAMPLE_ID_OFFSET(v)	((v) << 3)
35416eeb2eSDavid E. Box 
36416eeb2eSDavid E. Box #define NUM_BYTES_DWORD(v)	((v) << 2)
37416eeb2eSDavid E. Box #define SAMPLE_ID_OFFSET32(v)	((v) << 2)
38416eeb2eSDavid E. Box 
39416eeb2eSDavid E. Box /* Protects access to the xarray of telemetry endpoint handles */
40416eeb2eSDavid E. Box static DEFINE_MUTEX(ep_lock);
41416eeb2eSDavid E. Box 
42ba7e421eSDavid E. Box enum telem_type {
43ba7e421eSDavid E. Box 	TELEM_TYPE_PUNIT = 0,
44ba7e421eSDavid E. Box 	TELEM_TYPE_CRASHLOG,
45ba7e421eSDavid E. Box 	TELEM_TYPE_PUNIT_FIXED,
46ba7e421eSDavid E. Box };
47ba7e421eSDavid E. Box 
48e184b1e5SDavid E. Box struct pmt_telem_priv {
49e184b1e5SDavid E. Box 	int				num_entries;
50e184b1e5SDavid E. Box 	struct intel_pmt_entry		entry[];
51e184b1e5SDavid E. Box };
52e184b1e5SDavid E. Box 
pmt_telem_region_overlaps(struct intel_pmt_entry * entry,struct device * dev)53e184b1e5SDavid E. Box static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
54e184b1e5SDavid E. Box 				      struct device *dev)
55e184b1e5SDavid E. Box {
56e184b1e5SDavid E. Box 	u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
57e184b1e5SDavid E. Box 
58ba7e421eSDavid E. Box 	if (intel_pmt_is_early_client_hw(dev)) {
59ba7e421eSDavid E. Box 		u32 type = TELEM_TYPE(readl(entry->disc_table));
60e184b1e5SDavid E. Box 
61ba7e421eSDavid E. Box 		if ((type == TELEM_TYPE_PUNIT_FIXED) ||
62ba7e421eSDavid E. Box 		    (guid == TELEM_CLIENT_FIXED_BLOCK_GUID))
63ba7e421eSDavid E. Box 			return true;
64ba7e421eSDavid E. Box 	}
65ba7e421eSDavid E. Box 
66ba7e421eSDavid E. Box 	return false;
67e184b1e5SDavid E. Box }
68e184b1e5SDavid E. Box 
pmt_telem_header_decode(struct intel_pmt_entry * entry,struct device * dev)69e184b1e5SDavid E. Box static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
70e184b1e5SDavid E. Box 				   struct device *dev)
71e184b1e5SDavid E. Box {
72e184b1e5SDavid E. Box 	void __iomem *disc_table = entry->disc_table;
734d1b7efeSDavid E. Box 	struct intel_pmt_header *header = &entry->header;
74e184b1e5SDavid E. Box 
75e184b1e5SDavid E. Box 	if (pmt_telem_region_overlaps(entry, dev))
76e184b1e5SDavid E. Box 		return 1;
77e184b1e5SDavid E. Box 
78e184b1e5SDavid E. Box 	header->access_type = TELEM_ACCESS(readl(disc_table));
79e184b1e5SDavid E. Box 	header->guid = readl(disc_table + TELEM_GUID_OFFSET);
80e184b1e5SDavid E. Box 	header->base_offset = readl(disc_table + TELEM_BASE_OFFSET);
81e184b1e5SDavid E. Box 
82e184b1e5SDavid E. Box 	/* Size is measured in DWORDS, but accessor returns bytes */
83e184b1e5SDavid E. Box 	header->size = TELEM_SIZE(readl(disc_table));
84e184b1e5SDavid E. Box 
85ef195e8aSDavid E. Box 	/*
86ef195e8aSDavid E. Box 	 * Some devices may expose non-functioning entries that are
87ef195e8aSDavid E. Box 	 * reserved for future use. They have zero size. Do not fail
88ef195e8aSDavid E. Box 	 * probe for these. Just ignore them.
89ef195e8aSDavid E. Box 	 */
903f581602SDavid E. Box 	if (header->size == 0 || header->access_type == 0xF)
91ef195e8aSDavid E. Box 		return 1;
92ef195e8aSDavid E. Box 
93e184b1e5SDavid E. Box 	return 0;
94e184b1e5SDavid E. Box }
95e184b1e5SDavid E. Box 
pmt_telem_add_endpoint(struct intel_vsec_device * ivdev,struct intel_pmt_entry * entry)96*045a5130SDavid E. Box static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev,
97*045a5130SDavid E. Box 				  struct intel_pmt_entry *entry)
98416eeb2eSDavid E. Box {
99416eeb2eSDavid E. Box 	struct telem_endpoint *ep;
100416eeb2eSDavid E. Box 
101416eeb2eSDavid E. Box 	/* Endpoint lifetimes are managed by kref, not devres */
102416eeb2eSDavid E. Box 	entry->ep = kzalloc(sizeof(*(entry->ep)), GFP_KERNEL);
103416eeb2eSDavid E. Box 	if (!entry->ep)
104416eeb2eSDavid E. Box 		return -ENOMEM;
105416eeb2eSDavid E. Box 
106416eeb2eSDavid E. Box 	ep = entry->ep;
107*045a5130SDavid E. Box 	ep->pcidev = ivdev->pcidev;
108416eeb2eSDavid E. Box 	ep->header.access_type = entry->header.access_type;
109416eeb2eSDavid E. Box 	ep->header.guid = entry->header.guid;
110416eeb2eSDavid E. Box 	ep->header.base_offset = entry->header.base_offset;
111416eeb2eSDavid E. Box 	ep->header.size = entry->header.size;
112416eeb2eSDavid E. Box 	ep->base = entry->base;
113416eeb2eSDavid E. Box 	ep->present = true;
114*045a5130SDavid E. Box 	ep->cb = ivdev->priv_data;
115416eeb2eSDavid E. Box 
116416eeb2eSDavid E. Box 	kref_init(&ep->kref);
117416eeb2eSDavid E. Box 
118416eeb2eSDavid E. Box 	return 0;
119416eeb2eSDavid E. Box }
120416eeb2eSDavid E. Box 
121e184b1e5SDavid E. Box static DEFINE_XARRAY_ALLOC(telem_array);
122e184b1e5SDavid E. Box static struct intel_pmt_namespace pmt_telem_ns = {
123e184b1e5SDavid E. Box 	.name = "telem",
124e184b1e5SDavid E. Box 	.xa = &telem_array,
125e184b1e5SDavid E. Box 	.pmt_header_decode = pmt_telem_header_decode,
126416eeb2eSDavid E. Box 	.pmt_add_endpoint = pmt_telem_add_endpoint,
127e184b1e5SDavid E. Box };
128e184b1e5SDavid E. Box 
129416eeb2eSDavid E. Box /* Called when all users unregister and the device is removed */
pmt_telem_ep_release(struct kref * kref)130416eeb2eSDavid E. Box static void pmt_telem_ep_release(struct kref *kref)
131416eeb2eSDavid E. Box {
132416eeb2eSDavid E. Box 	struct telem_endpoint *ep;
133416eeb2eSDavid E. Box 
134416eeb2eSDavid E. Box 	ep = container_of(kref, struct telem_endpoint, kref);
135416eeb2eSDavid E. Box 	kfree(ep);
136416eeb2eSDavid E. Box }
137416eeb2eSDavid E. Box 
pmt_telem_get_next_endpoint(unsigned long start)138416eeb2eSDavid E. Box unsigned long pmt_telem_get_next_endpoint(unsigned long start)
139416eeb2eSDavid E. Box {
140416eeb2eSDavid E. Box 	struct intel_pmt_entry *entry;
141416eeb2eSDavid E. Box 	unsigned long found_idx;
142416eeb2eSDavid E. Box 
143416eeb2eSDavid E. Box 	mutex_lock(&ep_lock);
144416eeb2eSDavid E. Box 	xa_for_each_start(&telem_array, found_idx, entry, start) {
145416eeb2eSDavid E. Box 		/*
146416eeb2eSDavid E. Box 		 * Return first found index after start.
147416eeb2eSDavid E. Box 		 * 0 is not valid id.
148416eeb2eSDavid E. Box 		 */
149416eeb2eSDavid E. Box 		if (found_idx > start)
150416eeb2eSDavid E. Box 			break;
151416eeb2eSDavid E. Box 	}
152416eeb2eSDavid E. Box 	mutex_unlock(&ep_lock);
153416eeb2eSDavid E. Box 
154416eeb2eSDavid E. Box 	return found_idx == start ? 0 : found_idx;
155416eeb2eSDavid E. Box }
156416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_get_next_endpoint, INTEL_PMT_TELEMETRY);
157416eeb2eSDavid E. Box 
pmt_telem_register_endpoint(int devid)158416eeb2eSDavid E. Box struct telem_endpoint *pmt_telem_register_endpoint(int devid)
159416eeb2eSDavid E. Box {
160416eeb2eSDavid E. Box 	struct intel_pmt_entry *entry;
161416eeb2eSDavid E. Box 	unsigned long index = devid;
162416eeb2eSDavid E. Box 
163416eeb2eSDavid E. Box 	mutex_lock(&ep_lock);
164416eeb2eSDavid E. Box 	entry = xa_find(&telem_array, &index, index, XA_PRESENT);
165416eeb2eSDavid E. Box 	if (!entry) {
166416eeb2eSDavid E. Box 		mutex_unlock(&ep_lock);
167416eeb2eSDavid E. Box 		return ERR_PTR(-ENXIO);
168416eeb2eSDavid E. Box 	}
169416eeb2eSDavid E. Box 
170416eeb2eSDavid E. Box 	kref_get(&entry->ep->kref);
171416eeb2eSDavid E. Box 	mutex_unlock(&ep_lock);
172416eeb2eSDavid E. Box 
173416eeb2eSDavid E. Box 	return entry->ep;
174416eeb2eSDavid E. Box }
175416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_register_endpoint, INTEL_PMT_TELEMETRY);
176416eeb2eSDavid E. Box 
pmt_telem_unregister_endpoint(struct telem_endpoint * ep)177416eeb2eSDavid E. Box void pmt_telem_unregister_endpoint(struct telem_endpoint *ep)
178416eeb2eSDavid E. Box {
179416eeb2eSDavid E. Box 	kref_put(&ep->kref, pmt_telem_ep_release);
180416eeb2eSDavid E. Box }
181416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_unregister_endpoint, INTEL_PMT_TELEMETRY);
182416eeb2eSDavid E. Box 
pmt_telem_get_endpoint_info(int devid,struct telem_endpoint_info * info)183416eeb2eSDavid E. Box int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info)
184416eeb2eSDavid E. Box {
185416eeb2eSDavid E. Box 	struct intel_pmt_entry *entry;
186416eeb2eSDavid E. Box 	unsigned long index = devid;
187416eeb2eSDavid E. Box 	int err = 0;
188416eeb2eSDavid E. Box 
189416eeb2eSDavid E. Box 	if (!info)
190416eeb2eSDavid E. Box 		return -EINVAL;
191416eeb2eSDavid E. Box 
192416eeb2eSDavid E. Box 	mutex_lock(&ep_lock);
193416eeb2eSDavid E. Box 	entry = xa_find(&telem_array, &index, index, XA_PRESENT);
194416eeb2eSDavid E. Box 	if (!entry) {
195416eeb2eSDavid E. Box 		err = -ENXIO;
196416eeb2eSDavid E. Box 		goto unlock;
197416eeb2eSDavid E. Box 	}
198416eeb2eSDavid E. Box 
199416eeb2eSDavid E. Box 	info->pdev = entry->ep->pcidev;
200416eeb2eSDavid E. Box 	info->header = entry->ep->header;
201416eeb2eSDavid E. Box 
202416eeb2eSDavid E. Box unlock:
203416eeb2eSDavid E. Box 	mutex_unlock(&ep_lock);
204416eeb2eSDavid E. Box 	return err;
205416eeb2eSDavid E. Box 
206416eeb2eSDavid E. Box }
207416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, INTEL_PMT_TELEMETRY);
208416eeb2eSDavid E. Box 
pmt_telem_read(struct telem_endpoint * ep,u32 id,u64 * data,u32 count)209416eeb2eSDavid E. Box int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count)
210416eeb2eSDavid E. Box {
211416eeb2eSDavid E. Box 	u32 offset, size;
212416eeb2eSDavid E. Box 
213416eeb2eSDavid E. Box 	if (!ep->present)
214416eeb2eSDavid E. Box 		return -ENODEV;
215416eeb2eSDavid E. Box 
216416eeb2eSDavid E. Box 	offset = SAMPLE_ID_OFFSET(id);
217416eeb2eSDavid E. Box 	size = ep->header.size;
218416eeb2eSDavid E. Box 
219416eeb2eSDavid E. Box 	if (offset + NUM_BYTES_QWORD(count) > size)
220416eeb2eSDavid E. Box 		return -EINVAL;
221416eeb2eSDavid E. Box 
222*045a5130SDavid E. Box 	pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base + offset,
223*045a5130SDavid E. Box 			    NUM_BYTES_QWORD(count));
224416eeb2eSDavid E. Box 
225416eeb2eSDavid E. Box 	return ep->present ? 0 : -EPIPE;
226416eeb2eSDavid E. Box }
227416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_read, INTEL_PMT_TELEMETRY);
228416eeb2eSDavid E. Box 
pmt_telem_read32(struct telem_endpoint * ep,u32 id,u32 * data,u32 count)229416eeb2eSDavid E. Box int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count)
230416eeb2eSDavid E. Box {
231416eeb2eSDavid E. Box 	u32 offset, size;
232416eeb2eSDavid E. Box 
233416eeb2eSDavid E. Box 	if (!ep->present)
234416eeb2eSDavid E. Box 		return -ENODEV;
235416eeb2eSDavid E. Box 
236416eeb2eSDavid E. Box 	offset = SAMPLE_ID_OFFSET32(id);
237416eeb2eSDavid E. Box 	size = ep->header.size;
238416eeb2eSDavid E. Box 
239416eeb2eSDavid E. Box 	if (offset + NUM_BYTES_DWORD(count) > size)
240416eeb2eSDavid E. Box 		return -EINVAL;
241416eeb2eSDavid E. Box 
242416eeb2eSDavid E. Box 	memcpy_fromio(data, ep->base + offset, NUM_BYTES_DWORD(count));
243416eeb2eSDavid E. Box 
244416eeb2eSDavid E. Box 	return ep->present ? 0 : -EPIPE;
245416eeb2eSDavid E. Box }
246416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, INTEL_PMT_TELEMETRY);
247416eeb2eSDavid E. Box 
248416eeb2eSDavid E. Box struct telem_endpoint *
pmt_telem_find_and_register_endpoint(struct pci_dev * pcidev,u32 guid,u16 pos)249416eeb2eSDavid E. Box pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos)
250416eeb2eSDavid E. Box {
251416eeb2eSDavid E. Box 	int devid = 0;
252416eeb2eSDavid E. Box 	int inst = 0;
253416eeb2eSDavid E. Box 	int err = 0;
254416eeb2eSDavid E. Box 
255416eeb2eSDavid E. Box 	while ((devid = pmt_telem_get_next_endpoint(devid))) {
256416eeb2eSDavid E. Box 		struct telem_endpoint_info ep_info;
257416eeb2eSDavid E. Box 
258416eeb2eSDavid E. Box 		err = pmt_telem_get_endpoint_info(devid, &ep_info);
259416eeb2eSDavid E. Box 		if (err)
260416eeb2eSDavid E. Box 			return ERR_PTR(err);
261416eeb2eSDavid E. Box 
262416eeb2eSDavid E. Box 		if (ep_info.header.guid == guid && ep_info.pdev == pcidev) {
263416eeb2eSDavid E. Box 			if (inst == pos)
264416eeb2eSDavid E. Box 				return pmt_telem_register_endpoint(devid);
265416eeb2eSDavid E. Box 			++inst;
266416eeb2eSDavid E. Box 		}
267416eeb2eSDavid E. Box 	}
268416eeb2eSDavid E. Box 
269416eeb2eSDavid E. Box 	return ERR_PTR(-ENXIO);
270416eeb2eSDavid E. Box }
271416eeb2eSDavid E. Box EXPORT_SYMBOL_NS_GPL(pmt_telem_find_and_register_endpoint, INTEL_PMT_TELEMETRY);
272416eeb2eSDavid E. Box 
pmt_telem_remove(struct auxiliary_device * auxdev)273a3c8f906SDavid E. Box static void pmt_telem_remove(struct auxiliary_device *auxdev)
274e184b1e5SDavid E. Box {
275a3c8f906SDavid E. Box 	struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev);
276e184b1e5SDavid E. Box 	int i;
277e184b1e5SDavid E. Box 
278416eeb2eSDavid E. Box 	mutex_lock(&ep_lock);
279416eeb2eSDavid E. Box 	for (i = 0; i < priv->num_entries; i++) {
280416eeb2eSDavid E. Box 		struct intel_pmt_entry *entry = &priv->entry[i];
281416eeb2eSDavid E. Box 
282416eeb2eSDavid E. Box 		kref_put(&entry->ep->kref, pmt_telem_ep_release);
283416eeb2eSDavid E. Box 		intel_pmt_dev_destroy(entry, &pmt_telem_ns);
284e184b1e5SDavid E. Box 	}
285416eeb2eSDavid E. Box 	mutex_unlock(&ep_lock);
286416eeb2eSDavid E. Box };
287e184b1e5SDavid E. Box 
pmt_telem_probe(struct auxiliary_device * auxdev,const struct auxiliary_device_id * id)288a3c8f906SDavid E. Box static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
289e184b1e5SDavid E. Box {
290a3c8f906SDavid E. Box 	struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev);
291e184b1e5SDavid E. Box 	struct pmt_telem_priv *priv;
292e184b1e5SDavid E. Box 	size_t size;
293e184b1e5SDavid E. Box 	int i, ret;
294e184b1e5SDavid E. Box 
295a3c8f906SDavid E. Box 	size = struct_size(priv, entry, intel_vsec_dev->num_resources);
296a3c8f906SDavid E. Box 	priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
297e184b1e5SDavid E. Box 	if (!priv)
298e184b1e5SDavid E. Box 		return -ENOMEM;
299e184b1e5SDavid E. Box 
300a3c8f906SDavid E. Box 	auxiliary_set_drvdata(auxdev, priv);
301e184b1e5SDavid E. Box 
302a3c8f906SDavid E. Box 	for (i = 0; i < intel_vsec_dev->num_resources; i++) {
3032cdfa0c2SPrarit Bhargava 		struct intel_pmt_entry *entry = &priv->entry[priv->num_entries];
304e184b1e5SDavid E. Box 
305416eeb2eSDavid E. Box 		mutex_lock(&ep_lock);
306a3c8f906SDavid E. Box 		ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i);
307416eeb2eSDavid E. Box 		mutex_unlock(&ep_lock);
308e184b1e5SDavid E. Box 		if (ret < 0)
309e184b1e5SDavid E. Box 			goto abort_probe;
310e184b1e5SDavid E. Box 		if (ret)
311e184b1e5SDavid E. Box 			continue;
312e184b1e5SDavid E. Box 
313e184b1e5SDavid E. Box 		priv->num_entries++;
314e184b1e5SDavid E. Box 	}
315e184b1e5SDavid E. Box 
316e184b1e5SDavid E. Box 	return 0;
317e184b1e5SDavid E. Box abort_probe:
318a3c8f906SDavid E. Box 	pmt_telem_remove(auxdev);
319e184b1e5SDavid E. Box 	return ret;
320e184b1e5SDavid E. Box }
321e184b1e5SDavid E. Box 
322a3c8f906SDavid E. Box static const struct auxiliary_device_id pmt_telem_id_table[] = {
323a3c8f906SDavid E. Box 	{ .name = "intel_vsec.telemetry" },
324a3c8f906SDavid E. Box 	{}
325a3c8f906SDavid E. Box };
326a3c8f906SDavid E. Box MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table);
327a3c8f906SDavid E. Box 
328a3c8f906SDavid E. Box static struct auxiliary_driver pmt_telem_aux_driver = {
329a3c8f906SDavid E. Box 	.id_table	= pmt_telem_id_table,
330e184b1e5SDavid E. Box 	.remove		= pmt_telem_remove,
331e184b1e5SDavid E. Box 	.probe		= pmt_telem_probe,
332e184b1e5SDavid E. Box };
333e184b1e5SDavid E. Box 
pmt_telem_init(void)334e184b1e5SDavid E. Box static int __init pmt_telem_init(void)
335e184b1e5SDavid E. Box {
336a3c8f906SDavid E. Box 	return auxiliary_driver_register(&pmt_telem_aux_driver);
337e184b1e5SDavid E. Box }
338e184b1e5SDavid E. Box module_init(pmt_telem_init);
339e184b1e5SDavid E. Box 
pmt_telem_exit(void)340e184b1e5SDavid E. Box static void __exit pmt_telem_exit(void)
341e184b1e5SDavid E. Box {
342a3c8f906SDavid E. Box 	auxiliary_driver_unregister(&pmt_telem_aux_driver);
343e184b1e5SDavid E. Box 	xa_destroy(&telem_array);
344e184b1e5SDavid E. Box }
345e184b1e5SDavid E. Box module_exit(pmt_telem_exit);
346e184b1e5SDavid E. Box 
347e184b1e5SDavid E. Box MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
348e184b1e5SDavid E. Box MODULE_DESCRIPTION("Intel PMT Telemetry driver");
349e184b1e5SDavid E. Box MODULE_LICENSE("GPL v2");
350d9080843SDavid E. Box MODULE_IMPORT_NS(INTEL_PMT);
351