xref: /openbmc/linux/drivers/hwtracing/intel_th/pti.c (revision f77d22bc1221409b6c0cb6f32c6241161f5c2bc6)
114cdbf04SAlexander Shishkin /*
214cdbf04SAlexander Shishkin  * Intel(R) Trace Hub PTI output driver
314cdbf04SAlexander Shishkin  *
4*f77d22bcSAlexander Shishkin  * Copyright (C) 2014-2016 Intel Corporation.
514cdbf04SAlexander Shishkin  *
614cdbf04SAlexander Shishkin  * This program is free software; you can redistribute it and/or modify it
714cdbf04SAlexander Shishkin  * under the terms and conditions of the GNU General Public License,
814cdbf04SAlexander Shishkin  * version 2, as published by the Free Software Foundation.
914cdbf04SAlexander Shishkin  *
1014cdbf04SAlexander Shishkin  * This program is distributed in the hope it will be useful, but WITHOUT
1114cdbf04SAlexander Shishkin  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1214cdbf04SAlexander Shishkin  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1314cdbf04SAlexander Shishkin  * more details.
1414cdbf04SAlexander Shishkin  */
1514cdbf04SAlexander Shishkin 
1614cdbf04SAlexander Shishkin #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
1714cdbf04SAlexander Shishkin 
1814cdbf04SAlexander Shishkin #include <linux/types.h>
1914cdbf04SAlexander Shishkin #include <linux/module.h>
2014cdbf04SAlexander Shishkin #include <linux/device.h>
2114cdbf04SAlexander Shishkin #include <linux/sizes.h>
2214cdbf04SAlexander Shishkin #include <linux/printk.h>
2314cdbf04SAlexander Shishkin #include <linux/slab.h>
2414cdbf04SAlexander Shishkin #include <linux/mm.h>
2514cdbf04SAlexander Shishkin #include <linux/io.h>
2614cdbf04SAlexander Shishkin 
2714cdbf04SAlexander Shishkin #include "intel_th.h"
2814cdbf04SAlexander Shishkin #include "pti.h"
2914cdbf04SAlexander Shishkin 
3014cdbf04SAlexander Shishkin struct pti_device {
3114cdbf04SAlexander Shishkin 	void __iomem		*base;
3214cdbf04SAlexander Shishkin 	struct intel_th_device	*thdev;
3314cdbf04SAlexander Shishkin 	unsigned int		mode;
3414cdbf04SAlexander Shishkin 	unsigned int		freeclk;
3514cdbf04SAlexander Shishkin 	unsigned int		clkdiv;
3614cdbf04SAlexander Shishkin 	unsigned int		patgen;
37*f77d22bcSAlexander Shishkin 	unsigned int		lpp_dest_mask;
38*f77d22bcSAlexander Shishkin 	unsigned int		lpp_dest;
3914cdbf04SAlexander Shishkin };
4014cdbf04SAlexander Shishkin 
4114cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */
4214cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = {
4314cdbf04SAlexander Shishkin 	0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
4414cdbf04SAlexander Shishkin };
4514cdbf04SAlexander Shishkin 
4614cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width)
4714cdbf04SAlexander Shishkin {
4814cdbf04SAlexander Shishkin 	int i;
4914cdbf04SAlexander Shishkin 
5014cdbf04SAlexander Shishkin 	for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
5114cdbf04SAlexander Shishkin 		if (pti_mode[i] == width)
5214cdbf04SAlexander Shishkin 			return i;
5314cdbf04SAlexander Shishkin 
5414cdbf04SAlexander Shishkin 	return -EINVAL;
5514cdbf04SAlexander Shishkin }
5614cdbf04SAlexander Shishkin 
5714cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
5814cdbf04SAlexander Shishkin 			 char *buf)
5914cdbf04SAlexander Shishkin {
6014cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
6114cdbf04SAlexander Shishkin 
6214cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
6314cdbf04SAlexander Shishkin }
6414cdbf04SAlexander Shishkin 
6514cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
6614cdbf04SAlexander Shishkin 			  const char *buf, size_t size)
6714cdbf04SAlexander Shishkin {
6814cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
6914cdbf04SAlexander Shishkin 	unsigned long val;
7014cdbf04SAlexander Shishkin 	int ret;
7114cdbf04SAlexander Shishkin 
7214cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
7314cdbf04SAlexander Shishkin 	if (ret)
7414cdbf04SAlexander Shishkin 		return ret;
7514cdbf04SAlexander Shishkin 
7614cdbf04SAlexander Shishkin 	ret = pti_width_mode(val);
7714cdbf04SAlexander Shishkin 	if (ret < 0)
7814cdbf04SAlexander Shishkin 		return ret;
7914cdbf04SAlexander Shishkin 
8014cdbf04SAlexander Shishkin 	pti->mode = ret;
8114cdbf04SAlexander Shishkin 
8214cdbf04SAlexander Shishkin 	return size;
8314cdbf04SAlexander Shishkin }
8414cdbf04SAlexander Shishkin 
8514cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode);
8614cdbf04SAlexander Shishkin 
8714cdbf04SAlexander Shishkin static ssize_t
8814cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr,
8914cdbf04SAlexander Shishkin 		       char *buf)
9014cdbf04SAlexander Shishkin {
9114cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
9214cdbf04SAlexander Shishkin 
9314cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
9414cdbf04SAlexander Shishkin }
9514cdbf04SAlexander Shishkin 
9614cdbf04SAlexander Shishkin static ssize_t
9714cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr,
9814cdbf04SAlexander Shishkin 			const char *buf, size_t size)
9914cdbf04SAlexander Shishkin {
10014cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
10114cdbf04SAlexander Shishkin 	unsigned long val;
10214cdbf04SAlexander Shishkin 	int ret;
10314cdbf04SAlexander Shishkin 
10414cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
10514cdbf04SAlexander Shishkin 	if (ret)
10614cdbf04SAlexander Shishkin 		return ret;
10714cdbf04SAlexander Shishkin 
10814cdbf04SAlexander Shishkin 	pti->freeclk = !!val;
10914cdbf04SAlexander Shishkin 
11014cdbf04SAlexander Shishkin 	return size;
11114cdbf04SAlexander Shishkin }
11214cdbf04SAlexander Shishkin 
11314cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock);
11414cdbf04SAlexander Shishkin 
11514cdbf04SAlexander Shishkin static ssize_t
11614cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr,
11714cdbf04SAlexander Shishkin 		   char *buf)
11814cdbf04SAlexander Shishkin {
11914cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
12014cdbf04SAlexander Shishkin 
12114cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
12214cdbf04SAlexander Shishkin }
12314cdbf04SAlexander Shishkin 
12414cdbf04SAlexander Shishkin static ssize_t
12514cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr,
12614cdbf04SAlexander Shishkin 		    const char *buf, size_t size)
12714cdbf04SAlexander Shishkin {
12814cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
12914cdbf04SAlexander Shishkin 	unsigned long val;
13014cdbf04SAlexander Shishkin 	int ret;
13114cdbf04SAlexander Shishkin 
13214cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
13314cdbf04SAlexander Shishkin 	if (ret)
13414cdbf04SAlexander Shishkin 		return ret;
13514cdbf04SAlexander Shishkin 
13614cdbf04SAlexander Shishkin 	if (!is_power_of_2(val) || val > 8 || !val)
13714cdbf04SAlexander Shishkin 		return -EINVAL;
13814cdbf04SAlexander Shishkin 
13914cdbf04SAlexander Shishkin 	pti->clkdiv = val;
14014cdbf04SAlexander Shishkin 
14114cdbf04SAlexander Shishkin 	return size;
14214cdbf04SAlexander Shishkin }
14314cdbf04SAlexander Shishkin 
14414cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider);
14514cdbf04SAlexander Shishkin 
14614cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = {
14714cdbf04SAlexander Shishkin 	&dev_attr_mode.attr,
14814cdbf04SAlexander Shishkin 	&dev_attr_freerunning_clock.attr,
14914cdbf04SAlexander Shishkin 	&dev_attr_clock_divider.attr,
15014cdbf04SAlexander Shishkin 	NULL,
15114cdbf04SAlexander Shishkin };
15214cdbf04SAlexander Shishkin 
15314cdbf04SAlexander Shishkin static struct attribute_group pti_output_group = {
15414cdbf04SAlexander Shishkin 	.attrs	= pti_output_attrs,
15514cdbf04SAlexander Shishkin };
15614cdbf04SAlexander Shishkin 
15714cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev)
15814cdbf04SAlexander Shishkin {
15914cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
16014cdbf04SAlexander Shishkin 	u32 ctl = PTI_EN;
16114cdbf04SAlexander Shishkin 
16214cdbf04SAlexander Shishkin 	if (pti->patgen)
16314cdbf04SAlexander Shishkin 		ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
16414cdbf04SAlexander Shishkin 	if (pti->freeclk)
16514cdbf04SAlexander Shishkin 		ctl |= PTI_FCEN;
16614cdbf04SAlexander Shishkin 	ctl |= pti->mode << __ffs(PTI_MODE);
16714cdbf04SAlexander Shishkin 	ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
168*f77d22bcSAlexander Shishkin 	ctl |= pti->lpp_dest << __ffs(LPP_DEST);
16914cdbf04SAlexander Shishkin 
17014cdbf04SAlexander Shishkin 	iowrite32(ctl, pti->base + REG_PTI_CTL);
17114cdbf04SAlexander Shishkin 
17214cdbf04SAlexander Shishkin 	intel_th_trace_enable(thdev);
17314cdbf04SAlexander Shishkin 
17414cdbf04SAlexander Shishkin 	return 0;
17514cdbf04SAlexander Shishkin }
17614cdbf04SAlexander Shishkin 
17714cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev)
17814cdbf04SAlexander Shishkin {
17914cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
18014cdbf04SAlexander Shishkin 
18114cdbf04SAlexander Shishkin 	intel_th_trace_disable(thdev);
18214cdbf04SAlexander Shishkin 
18314cdbf04SAlexander Shishkin 	iowrite32(0, pti->base + REG_PTI_CTL);
18414cdbf04SAlexander Shishkin }
18514cdbf04SAlexander Shishkin 
18614cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti)
18714cdbf04SAlexander Shishkin {
18814cdbf04SAlexander Shishkin 	u32 ctl = ioread32(pti->base + REG_PTI_CTL);
18914cdbf04SAlexander Shishkin 
19014cdbf04SAlexander Shishkin 	pti->mode	= (ctl & PTI_MODE) >> __ffs(PTI_MODE);
19114cdbf04SAlexander Shishkin 	pti->clkdiv	= (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
19214cdbf04SAlexander Shishkin 	pti->freeclk	= !!(ctl & PTI_FCEN);
19314cdbf04SAlexander Shishkin 
19414cdbf04SAlexander Shishkin 	if (!pti_mode[pti->mode])
19514cdbf04SAlexander Shishkin 		pti->mode = pti_width_mode(4);
19614cdbf04SAlexander Shishkin 	if (!pti->clkdiv)
19714cdbf04SAlexander Shishkin 		pti->clkdiv = 1;
198*f77d22bcSAlexander Shishkin 
199*f77d22bcSAlexander Shishkin 	if (pti->thdev->output.type == GTH_LPP) {
200*f77d22bcSAlexander Shishkin 		if (ctl & LPP_PTIPRESENT)
201*f77d22bcSAlexander Shishkin 			pti->lpp_dest_mask |= LPP_DEST_PTI;
202*f77d22bcSAlexander Shishkin 		if (ctl & LPP_BSSBPRESENT)
203*f77d22bcSAlexander Shishkin 			pti->lpp_dest_mask |= LPP_DEST_EXI;
204*f77d22bcSAlexander Shishkin 		if (ctl & LPP_DEST)
205*f77d22bcSAlexander Shishkin 			pti->lpp_dest = 1;
206*f77d22bcSAlexander Shishkin 	}
20714cdbf04SAlexander Shishkin }
20814cdbf04SAlexander Shishkin 
20914cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev)
21014cdbf04SAlexander Shishkin {
21114cdbf04SAlexander Shishkin 	struct device *dev = &thdev->dev;
21214cdbf04SAlexander Shishkin 	struct resource *res;
21314cdbf04SAlexander Shishkin 	struct pti_device *pti;
21414cdbf04SAlexander Shishkin 	void __iomem *base;
21514cdbf04SAlexander Shishkin 
21614cdbf04SAlexander Shishkin 	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
21714cdbf04SAlexander Shishkin 	if (!res)
21814cdbf04SAlexander Shishkin 		return -ENODEV;
21914cdbf04SAlexander Shishkin 
22014cdbf04SAlexander Shishkin 	base = devm_ioremap(dev, res->start, resource_size(res));
22173061da0SDan Carpenter 	if (!base)
22273061da0SDan Carpenter 		return -ENOMEM;
22314cdbf04SAlexander Shishkin 
22414cdbf04SAlexander Shishkin 	pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
22514cdbf04SAlexander Shishkin 	if (!pti)
22614cdbf04SAlexander Shishkin 		return -ENOMEM;
22714cdbf04SAlexander Shishkin 
22814cdbf04SAlexander Shishkin 	pti->thdev = thdev;
22914cdbf04SAlexander Shishkin 	pti->base = base;
23014cdbf04SAlexander Shishkin 
23114cdbf04SAlexander Shishkin 	read_hw_config(pti);
23214cdbf04SAlexander Shishkin 
23314cdbf04SAlexander Shishkin 	dev_set_drvdata(dev, pti);
23414cdbf04SAlexander Shishkin 
23514cdbf04SAlexander Shishkin 	return 0;
23614cdbf04SAlexander Shishkin }
23714cdbf04SAlexander Shishkin 
23814cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev)
23914cdbf04SAlexander Shishkin {
24014cdbf04SAlexander Shishkin }
24114cdbf04SAlexander Shishkin 
24214cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = {
24314cdbf04SAlexander Shishkin 	.probe	= intel_th_pti_probe,
24414cdbf04SAlexander Shishkin 	.remove	= intel_th_pti_remove,
24514cdbf04SAlexander Shishkin 	.activate	= intel_th_pti_activate,
24614cdbf04SAlexander Shishkin 	.deactivate	= intel_th_pti_deactivate,
247e8644e4cSAlexander Shishkin 	.attr_group	= &pti_output_group,
24814cdbf04SAlexander Shishkin 	.driver	= {
24914cdbf04SAlexander Shishkin 		.name	= "pti",
25014cdbf04SAlexander Shishkin 		.owner	= THIS_MODULE,
25114cdbf04SAlexander Shishkin 	},
25214cdbf04SAlexander Shishkin };
25314cdbf04SAlexander Shishkin 
254*f77d22bcSAlexander Shishkin static const char * const lpp_dest_str[] = { "pti", "exi" };
255*f77d22bcSAlexander Shishkin 
256*f77d22bcSAlexander Shishkin static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
257*f77d22bcSAlexander Shishkin 			     char *buf)
258*f77d22bcSAlexander Shishkin {
259*f77d22bcSAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
260*f77d22bcSAlexander Shishkin 	ssize_t ret = 0;
261*f77d22bcSAlexander Shishkin 	int i;
262*f77d22bcSAlexander Shishkin 
263*f77d22bcSAlexander Shishkin 	for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
264*f77d22bcSAlexander Shishkin 		const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
265*f77d22bcSAlexander Shishkin 
266*f77d22bcSAlexander Shishkin 		if (!(pti->lpp_dest_mask & BIT(i)))
267*f77d22bcSAlexander Shishkin 			continue;
268*f77d22bcSAlexander Shishkin 
269*f77d22bcSAlexander Shishkin 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
270*f77d22bcSAlexander Shishkin 				 fmt, lpp_dest_str[i]);
271*f77d22bcSAlexander Shishkin 	}
272*f77d22bcSAlexander Shishkin 
273*f77d22bcSAlexander Shishkin 	if (ret)
274*f77d22bcSAlexander Shishkin 		buf[ret - 1] = '\n';
275*f77d22bcSAlexander Shishkin 
276*f77d22bcSAlexander Shishkin 	return ret;
277*f77d22bcSAlexander Shishkin }
278*f77d22bcSAlexander Shishkin 
279*f77d22bcSAlexander Shishkin static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
280*f77d22bcSAlexander Shishkin 			      const char *buf, size_t size)
281*f77d22bcSAlexander Shishkin {
282*f77d22bcSAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
283*f77d22bcSAlexander Shishkin 	ssize_t ret = -EINVAL;
284*f77d22bcSAlexander Shishkin 	int i;
285*f77d22bcSAlexander Shishkin 
286*f77d22bcSAlexander Shishkin 	for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++)
287*f77d22bcSAlexander Shishkin 		if (sysfs_streq(buf, lpp_dest_str[i]))
288*f77d22bcSAlexander Shishkin 			break;
289*f77d22bcSAlexander Shishkin 
290*f77d22bcSAlexander Shishkin 	if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) {
291*f77d22bcSAlexander Shishkin 		pti->lpp_dest = i;
292*f77d22bcSAlexander Shishkin 		ret = size;
293*f77d22bcSAlexander Shishkin 	}
294*f77d22bcSAlexander Shishkin 
295*f77d22bcSAlexander Shishkin 	return ret;
296*f77d22bcSAlexander Shishkin }
297*f77d22bcSAlexander Shishkin 
298*f77d22bcSAlexander Shishkin static DEVICE_ATTR_RW(lpp_dest);
299*f77d22bcSAlexander Shishkin 
300*f77d22bcSAlexander Shishkin static struct attribute *lpp_output_attrs[] = {
301*f77d22bcSAlexander Shishkin 	&dev_attr_mode.attr,
302*f77d22bcSAlexander Shishkin 	&dev_attr_freerunning_clock.attr,
303*f77d22bcSAlexander Shishkin 	&dev_attr_clock_divider.attr,
304*f77d22bcSAlexander Shishkin 	&dev_attr_lpp_dest.attr,
305*f77d22bcSAlexander Shishkin 	NULL,
306*f77d22bcSAlexander Shishkin };
307*f77d22bcSAlexander Shishkin 
308*f77d22bcSAlexander Shishkin static struct attribute_group lpp_output_group = {
309*f77d22bcSAlexander Shishkin 	.attrs	= lpp_output_attrs,
310*f77d22bcSAlexander Shishkin };
311*f77d22bcSAlexander Shishkin 
312*f77d22bcSAlexander Shishkin static struct intel_th_driver intel_th_lpp_driver = {
313*f77d22bcSAlexander Shishkin 	.probe		= intel_th_pti_probe,
314*f77d22bcSAlexander Shishkin 	.remove		= intel_th_pti_remove,
315*f77d22bcSAlexander Shishkin 	.activate	= intel_th_pti_activate,
316*f77d22bcSAlexander Shishkin 	.deactivate	= intel_th_pti_deactivate,
317*f77d22bcSAlexander Shishkin 	.attr_group	= &lpp_output_group,
318*f77d22bcSAlexander Shishkin 	.driver	= {
319*f77d22bcSAlexander Shishkin 		.name	= "lpp",
320*f77d22bcSAlexander Shishkin 		.owner	= THIS_MODULE,
321*f77d22bcSAlexander Shishkin 	},
322*f77d22bcSAlexander Shishkin };
323*f77d22bcSAlexander Shishkin 
324*f77d22bcSAlexander Shishkin static int __init intel_th_pti_lpp_init(void)
325*f77d22bcSAlexander Shishkin {
326*f77d22bcSAlexander Shishkin 	int err;
327*f77d22bcSAlexander Shishkin 
328*f77d22bcSAlexander Shishkin 	err = intel_th_driver_register(&intel_th_pti_driver);
329*f77d22bcSAlexander Shishkin 	if (err)
330*f77d22bcSAlexander Shishkin 		return err;
331*f77d22bcSAlexander Shishkin 
332*f77d22bcSAlexander Shishkin 	err = intel_th_driver_register(&intel_th_lpp_driver);
333*f77d22bcSAlexander Shishkin 	if (err) {
334*f77d22bcSAlexander Shishkin 		intel_th_driver_unregister(&intel_th_pti_driver);
335*f77d22bcSAlexander Shishkin 		return err;
336*f77d22bcSAlexander Shishkin 	}
337*f77d22bcSAlexander Shishkin 
338*f77d22bcSAlexander Shishkin 	return 0;
339*f77d22bcSAlexander Shishkin }
340*f77d22bcSAlexander Shishkin 
341*f77d22bcSAlexander Shishkin module_init(intel_th_pti_lpp_init);
342*f77d22bcSAlexander Shishkin 
343*f77d22bcSAlexander Shishkin static void __exit intel_th_pti_lpp_exit(void)
344*f77d22bcSAlexander Shishkin {
345*f77d22bcSAlexander Shishkin 	intel_th_driver_unregister(&intel_th_pti_driver);
346*f77d22bcSAlexander Shishkin 	intel_th_driver_unregister(&intel_th_lpp_driver);
347*f77d22bcSAlexander Shishkin }
348*f77d22bcSAlexander Shishkin 
349*f77d22bcSAlexander Shishkin module_exit(intel_th_pti_lpp_exit);
35014cdbf04SAlexander Shishkin 
35114cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2");
352*f77d22bcSAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");
35314cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
354