xref: /openbmc/linux/drivers/hwtracing/intel_th/pti.c (revision 73061da07d28ac6724bcd9ce4b51cf51a8b6b40e)
114cdbf04SAlexander Shishkin /*
214cdbf04SAlexander Shishkin  * Intel(R) Trace Hub PTI output driver
314cdbf04SAlexander Shishkin  *
414cdbf04SAlexander Shishkin  * Copyright (C) 2014-2015 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;
3714cdbf04SAlexander Shishkin };
3814cdbf04SAlexander Shishkin 
3914cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */
4014cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = {
4114cdbf04SAlexander Shishkin 	0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
4214cdbf04SAlexander Shishkin };
4314cdbf04SAlexander Shishkin 
4414cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width)
4514cdbf04SAlexander Shishkin {
4614cdbf04SAlexander Shishkin 	int i;
4714cdbf04SAlexander Shishkin 
4814cdbf04SAlexander Shishkin 	for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
4914cdbf04SAlexander Shishkin 		if (pti_mode[i] == width)
5014cdbf04SAlexander Shishkin 			return i;
5114cdbf04SAlexander Shishkin 
5214cdbf04SAlexander Shishkin 	return -EINVAL;
5314cdbf04SAlexander Shishkin }
5414cdbf04SAlexander Shishkin 
5514cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
5614cdbf04SAlexander Shishkin 			 char *buf)
5714cdbf04SAlexander Shishkin {
5814cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
5914cdbf04SAlexander Shishkin 
6014cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
6114cdbf04SAlexander Shishkin }
6214cdbf04SAlexander Shishkin 
6314cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
6414cdbf04SAlexander Shishkin 			  const char *buf, size_t size)
6514cdbf04SAlexander Shishkin {
6614cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
6714cdbf04SAlexander Shishkin 	unsigned long val;
6814cdbf04SAlexander Shishkin 	int ret;
6914cdbf04SAlexander Shishkin 
7014cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
7114cdbf04SAlexander Shishkin 	if (ret)
7214cdbf04SAlexander Shishkin 		return ret;
7314cdbf04SAlexander Shishkin 
7414cdbf04SAlexander Shishkin 	ret = pti_width_mode(val);
7514cdbf04SAlexander Shishkin 	if (ret < 0)
7614cdbf04SAlexander Shishkin 		return ret;
7714cdbf04SAlexander Shishkin 
7814cdbf04SAlexander Shishkin 	pti->mode = ret;
7914cdbf04SAlexander Shishkin 
8014cdbf04SAlexander Shishkin 	return size;
8114cdbf04SAlexander Shishkin }
8214cdbf04SAlexander Shishkin 
8314cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode);
8414cdbf04SAlexander Shishkin 
8514cdbf04SAlexander Shishkin static ssize_t
8614cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr,
8714cdbf04SAlexander Shishkin 		       char *buf)
8814cdbf04SAlexander Shishkin {
8914cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
9014cdbf04SAlexander Shishkin 
9114cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
9214cdbf04SAlexander Shishkin }
9314cdbf04SAlexander Shishkin 
9414cdbf04SAlexander Shishkin static ssize_t
9514cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr,
9614cdbf04SAlexander Shishkin 			const char *buf, size_t size)
9714cdbf04SAlexander Shishkin {
9814cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
9914cdbf04SAlexander Shishkin 	unsigned long val;
10014cdbf04SAlexander Shishkin 	int ret;
10114cdbf04SAlexander Shishkin 
10214cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
10314cdbf04SAlexander Shishkin 	if (ret)
10414cdbf04SAlexander Shishkin 		return ret;
10514cdbf04SAlexander Shishkin 
10614cdbf04SAlexander Shishkin 	pti->freeclk = !!val;
10714cdbf04SAlexander Shishkin 
10814cdbf04SAlexander Shishkin 	return size;
10914cdbf04SAlexander Shishkin }
11014cdbf04SAlexander Shishkin 
11114cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock);
11214cdbf04SAlexander Shishkin 
11314cdbf04SAlexander Shishkin static ssize_t
11414cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr,
11514cdbf04SAlexander Shishkin 		   char *buf)
11614cdbf04SAlexander Shishkin {
11714cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
11814cdbf04SAlexander Shishkin 
11914cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
12014cdbf04SAlexander Shishkin }
12114cdbf04SAlexander Shishkin 
12214cdbf04SAlexander Shishkin static ssize_t
12314cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr,
12414cdbf04SAlexander Shishkin 		    const char *buf, size_t size)
12514cdbf04SAlexander Shishkin {
12614cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
12714cdbf04SAlexander Shishkin 	unsigned long val;
12814cdbf04SAlexander Shishkin 	int ret;
12914cdbf04SAlexander Shishkin 
13014cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
13114cdbf04SAlexander Shishkin 	if (ret)
13214cdbf04SAlexander Shishkin 		return ret;
13314cdbf04SAlexander Shishkin 
13414cdbf04SAlexander Shishkin 	if (!is_power_of_2(val) || val > 8 || !val)
13514cdbf04SAlexander Shishkin 		return -EINVAL;
13614cdbf04SAlexander Shishkin 
13714cdbf04SAlexander Shishkin 	pti->clkdiv = val;
13814cdbf04SAlexander Shishkin 
13914cdbf04SAlexander Shishkin 	return size;
14014cdbf04SAlexander Shishkin }
14114cdbf04SAlexander Shishkin 
14214cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider);
14314cdbf04SAlexander Shishkin 
14414cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = {
14514cdbf04SAlexander Shishkin 	&dev_attr_mode.attr,
14614cdbf04SAlexander Shishkin 	&dev_attr_freerunning_clock.attr,
14714cdbf04SAlexander Shishkin 	&dev_attr_clock_divider.attr,
14814cdbf04SAlexander Shishkin 	NULL,
14914cdbf04SAlexander Shishkin };
15014cdbf04SAlexander Shishkin 
15114cdbf04SAlexander Shishkin static struct attribute_group pti_output_group = {
15214cdbf04SAlexander Shishkin 	.attrs	= pti_output_attrs,
15314cdbf04SAlexander Shishkin };
15414cdbf04SAlexander Shishkin 
15514cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev)
15614cdbf04SAlexander Shishkin {
15714cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
15814cdbf04SAlexander Shishkin 	u32 ctl = PTI_EN;
15914cdbf04SAlexander Shishkin 
16014cdbf04SAlexander Shishkin 	if (pti->patgen)
16114cdbf04SAlexander Shishkin 		ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
16214cdbf04SAlexander Shishkin 	if (pti->freeclk)
16314cdbf04SAlexander Shishkin 		ctl |= PTI_FCEN;
16414cdbf04SAlexander Shishkin 	ctl |= pti->mode << __ffs(PTI_MODE);
16514cdbf04SAlexander Shishkin 	ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
16614cdbf04SAlexander Shishkin 
16714cdbf04SAlexander Shishkin 	iowrite32(ctl, pti->base + REG_PTI_CTL);
16814cdbf04SAlexander Shishkin 
16914cdbf04SAlexander Shishkin 	intel_th_trace_enable(thdev);
17014cdbf04SAlexander Shishkin 
17114cdbf04SAlexander Shishkin 	return 0;
17214cdbf04SAlexander Shishkin }
17314cdbf04SAlexander Shishkin 
17414cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev)
17514cdbf04SAlexander Shishkin {
17614cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
17714cdbf04SAlexander Shishkin 
17814cdbf04SAlexander Shishkin 	intel_th_trace_disable(thdev);
17914cdbf04SAlexander Shishkin 
18014cdbf04SAlexander Shishkin 	iowrite32(0, pti->base + REG_PTI_CTL);
18114cdbf04SAlexander Shishkin }
18214cdbf04SAlexander Shishkin 
18314cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti)
18414cdbf04SAlexander Shishkin {
18514cdbf04SAlexander Shishkin 	u32 ctl = ioread32(pti->base + REG_PTI_CTL);
18614cdbf04SAlexander Shishkin 
18714cdbf04SAlexander Shishkin 	pti->mode	= (ctl & PTI_MODE) >> __ffs(PTI_MODE);
18814cdbf04SAlexander Shishkin 	pti->clkdiv	= (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
18914cdbf04SAlexander Shishkin 	pti->freeclk	= !!(ctl & PTI_FCEN);
19014cdbf04SAlexander Shishkin 
19114cdbf04SAlexander Shishkin 	if (!pti_mode[pti->mode])
19214cdbf04SAlexander Shishkin 		pti->mode = pti_width_mode(4);
19314cdbf04SAlexander Shishkin 	if (!pti->clkdiv)
19414cdbf04SAlexander Shishkin 		pti->clkdiv = 1;
19514cdbf04SAlexander Shishkin }
19614cdbf04SAlexander Shishkin 
19714cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev)
19814cdbf04SAlexander Shishkin {
19914cdbf04SAlexander Shishkin 	struct device *dev = &thdev->dev;
20014cdbf04SAlexander Shishkin 	struct resource *res;
20114cdbf04SAlexander Shishkin 	struct pti_device *pti;
20214cdbf04SAlexander Shishkin 	void __iomem *base;
20314cdbf04SAlexander Shishkin 	int ret;
20414cdbf04SAlexander Shishkin 
20514cdbf04SAlexander Shishkin 	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
20614cdbf04SAlexander Shishkin 	if (!res)
20714cdbf04SAlexander Shishkin 		return -ENODEV;
20814cdbf04SAlexander Shishkin 
20914cdbf04SAlexander Shishkin 	base = devm_ioremap(dev, res->start, resource_size(res));
210*73061da0SDan Carpenter 	if (!base)
211*73061da0SDan Carpenter 		return -ENOMEM;
21214cdbf04SAlexander Shishkin 
21314cdbf04SAlexander Shishkin 	pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
21414cdbf04SAlexander Shishkin 	if (!pti)
21514cdbf04SAlexander Shishkin 		return -ENOMEM;
21614cdbf04SAlexander Shishkin 
21714cdbf04SAlexander Shishkin 	pti->thdev = thdev;
21814cdbf04SAlexander Shishkin 	pti->base = base;
21914cdbf04SAlexander Shishkin 
22014cdbf04SAlexander Shishkin 	read_hw_config(pti);
22114cdbf04SAlexander Shishkin 
22214cdbf04SAlexander Shishkin 	ret = sysfs_create_group(&dev->kobj, &pti_output_group);
22314cdbf04SAlexander Shishkin 	if (ret)
22414cdbf04SAlexander Shishkin 		return ret;
22514cdbf04SAlexander Shishkin 
22614cdbf04SAlexander Shishkin 	dev_set_drvdata(dev, pti);
22714cdbf04SAlexander Shishkin 
22814cdbf04SAlexander Shishkin 	return 0;
22914cdbf04SAlexander Shishkin }
23014cdbf04SAlexander Shishkin 
23114cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev)
23214cdbf04SAlexander Shishkin {
23314cdbf04SAlexander Shishkin }
23414cdbf04SAlexander Shishkin 
23514cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = {
23614cdbf04SAlexander Shishkin 	.probe	= intel_th_pti_probe,
23714cdbf04SAlexander Shishkin 	.remove	= intel_th_pti_remove,
23814cdbf04SAlexander Shishkin 	.activate	= intel_th_pti_activate,
23914cdbf04SAlexander Shishkin 	.deactivate	= intel_th_pti_deactivate,
24014cdbf04SAlexander Shishkin 	.driver	= {
24114cdbf04SAlexander Shishkin 		.name	= "pti",
24214cdbf04SAlexander Shishkin 		.owner	= THIS_MODULE,
24314cdbf04SAlexander Shishkin 	},
24414cdbf04SAlexander Shishkin };
24514cdbf04SAlexander Shishkin 
24614cdbf04SAlexander Shishkin module_driver(intel_th_pti_driver,
24714cdbf04SAlexander Shishkin 	      intel_th_driver_register,
24814cdbf04SAlexander Shishkin 	      intel_th_driver_unregister);
24914cdbf04SAlexander Shishkin 
25014cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2");
25114cdbf04SAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver");
25214cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
253