xref: /openbmc/linux/drivers/hwtracing/intel_th/pti.c (revision 14cdbf041788f150ec3b323583b80bfd5775f73e)
1*14cdbf04SAlexander Shishkin /*
2*14cdbf04SAlexander Shishkin  * Intel(R) Trace Hub PTI output driver
3*14cdbf04SAlexander Shishkin  *
4*14cdbf04SAlexander Shishkin  * Copyright (C) 2014-2015 Intel Corporation.
5*14cdbf04SAlexander Shishkin  *
6*14cdbf04SAlexander Shishkin  * This program is free software; you can redistribute it and/or modify it
7*14cdbf04SAlexander Shishkin  * under the terms and conditions of the GNU General Public License,
8*14cdbf04SAlexander Shishkin  * version 2, as published by the Free Software Foundation.
9*14cdbf04SAlexander Shishkin  *
10*14cdbf04SAlexander Shishkin  * This program is distributed in the hope it will be useful, but WITHOUT
11*14cdbf04SAlexander Shishkin  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12*14cdbf04SAlexander Shishkin  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13*14cdbf04SAlexander Shishkin  * more details.
14*14cdbf04SAlexander Shishkin  */
15*14cdbf04SAlexander Shishkin 
16*14cdbf04SAlexander Shishkin #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
17*14cdbf04SAlexander Shishkin 
18*14cdbf04SAlexander Shishkin #include <linux/types.h>
19*14cdbf04SAlexander Shishkin #include <linux/module.h>
20*14cdbf04SAlexander Shishkin #include <linux/device.h>
21*14cdbf04SAlexander Shishkin #include <linux/sizes.h>
22*14cdbf04SAlexander Shishkin #include <linux/printk.h>
23*14cdbf04SAlexander Shishkin #include <linux/slab.h>
24*14cdbf04SAlexander Shishkin #include <linux/mm.h>
25*14cdbf04SAlexander Shishkin #include <linux/io.h>
26*14cdbf04SAlexander Shishkin 
27*14cdbf04SAlexander Shishkin #include "intel_th.h"
28*14cdbf04SAlexander Shishkin #include "pti.h"
29*14cdbf04SAlexander Shishkin 
30*14cdbf04SAlexander Shishkin struct pti_device {
31*14cdbf04SAlexander Shishkin 	void __iomem		*base;
32*14cdbf04SAlexander Shishkin 	struct intel_th_device	*thdev;
33*14cdbf04SAlexander Shishkin 	unsigned int		mode;
34*14cdbf04SAlexander Shishkin 	unsigned int		freeclk;
35*14cdbf04SAlexander Shishkin 	unsigned int		clkdiv;
36*14cdbf04SAlexander Shishkin 	unsigned int		patgen;
37*14cdbf04SAlexander Shishkin };
38*14cdbf04SAlexander Shishkin 
39*14cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */
40*14cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = {
41*14cdbf04SAlexander Shishkin 	0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
42*14cdbf04SAlexander Shishkin };
43*14cdbf04SAlexander Shishkin 
44*14cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width)
45*14cdbf04SAlexander Shishkin {
46*14cdbf04SAlexander Shishkin 	int i;
47*14cdbf04SAlexander Shishkin 
48*14cdbf04SAlexander Shishkin 	for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
49*14cdbf04SAlexander Shishkin 		if (pti_mode[i] == width)
50*14cdbf04SAlexander Shishkin 			return i;
51*14cdbf04SAlexander Shishkin 
52*14cdbf04SAlexander Shishkin 	return -EINVAL;
53*14cdbf04SAlexander Shishkin }
54*14cdbf04SAlexander Shishkin 
55*14cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
56*14cdbf04SAlexander Shishkin 			 char *buf)
57*14cdbf04SAlexander Shishkin {
58*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
59*14cdbf04SAlexander Shishkin 
60*14cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
61*14cdbf04SAlexander Shishkin }
62*14cdbf04SAlexander Shishkin 
63*14cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
64*14cdbf04SAlexander Shishkin 			  const char *buf, size_t size)
65*14cdbf04SAlexander Shishkin {
66*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
67*14cdbf04SAlexander Shishkin 	unsigned long val;
68*14cdbf04SAlexander Shishkin 	int ret;
69*14cdbf04SAlexander Shishkin 
70*14cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
71*14cdbf04SAlexander Shishkin 	if (ret)
72*14cdbf04SAlexander Shishkin 		return ret;
73*14cdbf04SAlexander Shishkin 
74*14cdbf04SAlexander Shishkin 	ret = pti_width_mode(val);
75*14cdbf04SAlexander Shishkin 	if (ret < 0)
76*14cdbf04SAlexander Shishkin 		return ret;
77*14cdbf04SAlexander Shishkin 
78*14cdbf04SAlexander Shishkin 	pti->mode = ret;
79*14cdbf04SAlexander Shishkin 
80*14cdbf04SAlexander Shishkin 	return size;
81*14cdbf04SAlexander Shishkin }
82*14cdbf04SAlexander Shishkin 
83*14cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode);
84*14cdbf04SAlexander Shishkin 
85*14cdbf04SAlexander Shishkin static ssize_t
86*14cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr,
87*14cdbf04SAlexander Shishkin 		       char *buf)
88*14cdbf04SAlexander Shishkin {
89*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
90*14cdbf04SAlexander Shishkin 
91*14cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
92*14cdbf04SAlexander Shishkin }
93*14cdbf04SAlexander Shishkin 
94*14cdbf04SAlexander Shishkin static ssize_t
95*14cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr,
96*14cdbf04SAlexander Shishkin 			const char *buf, size_t size)
97*14cdbf04SAlexander Shishkin {
98*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
99*14cdbf04SAlexander Shishkin 	unsigned long val;
100*14cdbf04SAlexander Shishkin 	int ret;
101*14cdbf04SAlexander Shishkin 
102*14cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
103*14cdbf04SAlexander Shishkin 	if (ret)
104*14cdbf04SAlexander Shishkin 		return ret;
105*14cdbf04SAlexander Shishkin 
106*14cdbf04SAlexander Shishkin 	pti->freeclk = !!val;
107*14cdbf04SAlexander Shishkin 
108*14cdbf04SAlexander Shishkin 	return size;
109*14cdbf04SAlexander Shishkin }
110*14cdbf04SAlexander Shishkin 
111*14cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock);
112*14cdbf04SAlexander Shishkin 
113*14cdbf04SAlexander Shishkin static ssize_t
114*14cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr,
115*14cdbf04SAlexander Shishkin 		   char *buf)
116*14cdbf04SAlexander Shishkin {
117*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
118*14cdbf04SAlexander Shishkin 
119*14cdbf04SAlexander Shishkin 	return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
120*14cdbf04SAlexander Shishkin }
121*14cdbf04SAlexander Shishkin 
122*14cdbf04SAlexander Shishkin static ssize_t
123*14cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr,
124*14cdbf04SAlexander Shishkin 		    const char *buf, size_t size)
125*14cdbf04SAlexander Shishkin {
126*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(dev);
127*14cdbf04SAlexander Shishkin 	unsigned long val;
128*14cdbf04SAlexander Shishkin 	int ret;
129*14cdbf04SAlexander Shishkin 
130*14cdbf04SAlexander Shishkin 	ret = kstrtoul(buf, 10, &val);
131*14cdbf04SAlexander Shishkin 	if (ret)
132*14cdbf04SAlexander Shishkin 		return ret;
133*14cdbf04SAlexander Shishkin 
134*14cdbf04SAlexander Shishkin 	if (!is_power_of_2(val) || val > 8 || !val)
135*14cdbf04SAlexander Shishkin 		return -EINVAL;
136*14cdbf04SAlexander Shishkin 
137*14cdbf04SAlexander Shishkin 	pti->clkdiv = val;
138*14cdbf04SAlexander Shishkin 
139*14cdbf04SAlexander Shishkin 	return size;
140*14cdbf04SAlexander Shishkin }
141*14cdbf04SAlexander Shishkin 
142*14cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider);
143*14cdbf04SAlexander Shishkin 
144*14cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = {
145*14cdbf04SAlexander Shishkin 	&dev_attr_mode.attr,
146*14cdbf04SAlexander Shishkin 	&dev_attr_freerunning_clock.attr,
147*14cdbf04SAlexander Shishkin 	&dev_attr_clock_divider.attr,
148*14cdbf04SAlexander Shishkin 	NULL,
149*14cdbf04SAlexander Shishkin };
150*14cdbf04SAlexander Shishkin 
151*14cdbf04SAlexander Shishkin static struct attribute_group pti_output_group = {
152*14cdbf04SAlexander Shishkin 	.attrs	= pti_output_attrs,
153*14cdbf04SAlexander Shishkin };
154*14cdbf04SAlexander Shishkin 
155*14cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev)
156*14cdbf04SAlexander Shishkin {
157*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
158*14cdbf04SAlexander Shishkin 	u32 ctl = PTI_EN;
159*14cdbf04SAlexander Shishkin 
160*14cdbf04SAlexander Shishkin 	if (pti->patgen)
161*14cdbf04SAlexander Shishkin 		ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
162*14cdbf04SAlexander Shishkin 	if (pti->freeclk)
163*14cdbf04SAlexander Shishkin 		ctl |= PTI_FCEN;
164*14cdbf04SAlexander Shishkin 	ctl |= pti->mode << __ffs(PTI_MODE);
165*14cdbf04SAlexander Shishkin 	ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
166*14cdbf04SAlexander Shishkin 
167*14cdbf04SAlexander Shishkin 	iowrite32(ctl, pti->base + REG_PTI_CTL);
168*14cdbf04SAlexander Shishkin 
169*14cdbf04SAlexander Shishkin 	intel_th_trace_enable(thdev);
170*14cdbf04SAlexander Shishkin 
171*14cdbf04SAlexander Shishkin 	return 0;
172*14cdbf04SAlexander Shishkin }
173*14cdbf04SAlexander Shishkin 
174*14cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev)
175*14cdbf04SAlexander Shishkin {
176*14cdbf04SAlexander Shishkin 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
177*14cdbf04SAlexander Shishkin 
178*14cdbf04SAlexander Shishkin 	intel_th_trace_disable(thdev);
179*14cdbf04SAlexander Shishkin 
180*14cdbf04SAlexander Shishkin 	iowrite32(0, pti->base + REG_PTI_CTL);
181*14cdbf04SAlexander Shishkin }
182*14cdbf04SAlexander Shishkin 
183*14cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti)
184*14cdbf04SAlexander Shishkin {
185*14cdbf04SAlexander Shishkin 	u32 ctl = ioread32(pti->base + REG_PTI_CTL);
186*14cdbf04SAlexander Shishkin 
187*14cdbf04SAlexander Shishkin 	pti->mode	= (ctl & PTI_MODE) >> __ffs(PTI_MODE);
188*14cdbf04SAlexander Shishkin 	pti->clkdiv	= (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
189*14cdbf04SAlexander Shishkin 	pti->freeclk	= !!(ctl & PTI_FCEN);
190*14cdbf04SAlexander Shishkin 
191*14cdbf04SAlexander Shishkin 	if (!pti_mode[pti->mode])
192*14cdbf04SAlexander Shishkin 		pti->mode = pti_width_mode(4);
193*14cdbf04SAlexander Shishkin 	if (!pti->clkdiv)
194*14cdbf04SAlexander Shishkin 		pti->clkdiv = 1;
195*14cdbf04SAlexander Shishkin }
196*14cdbf04SAlexander Shishkin 
197*14cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev)
198*14cdbf04SAlexander Shishkin {
199*14cdbf04SAlexander Shishkin 	struct device *dev = &thdev->dev;
200*14cdbf04SAlexander Shishkin 	struct resource *res;
201*14cdbf04SAlexander Shishkin 	struct pti_device *pti;
202*14cdbf04SAlexander Shishkin 	void __iomem *base;
203*14cdbf04SAlexander Shishkin 	int ret;
204*14cdbf04SAlexander Shishkin 
205*14cdbf04SAlexander Shishkin 	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
206*14cdbf04SAlexander Shishkin 	if (!res)
207*14cdbf04SAlexander Shishkin 		return -ENODEV;
208*14cdbf04SAlexander Shishkin 
209*14cdbf04SAlexander Shishkin 	base = devm_ioremap(dev, res->start, resource_size(res));
210*14cdbf04SAlexander Shishkin 	if (IS_ERR(base))
211*14cdbf04SAlexander Shishkin 		return PTR_ERR(base);
212*14cdbf04SAlexander Shishkin 
213*14cdbf04SAlexander Shishkin 	pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
214*14cdbf04SAlexander Shishkin 	if (!pti)
215*14cdbf04SAlexander Shishkin 		return -ENOMEM;
216*14cdbf04SAlexander Shishkin 
217*14cdbf04SAlexander Shishkin 	pti->thdev = thdev;
218*14cdbf04SAlexander Shishkin 	pti->base = base;
219*14cdbf04SAlexander Shishkin 
220*14cdbf04SAlexander Shishkin 	read_hw_config(pti);
221*14cdbf04SAlexander Shishkin 
222*14cdbf04SAlexander Shishkin 	ret = sysfs_create_group(&dev->kobj, &pti_output_group);
223*14cdbf04SAlexander Shishkin 	if (ret)
224*14cdbf04SAlexander Shishkin 		return ret;
225*14cdbf04SAlexander Shishkin 
226*14cdbf04SAlexander Shishkin 	dev_set_drvdata(dev, pti);
227*14cdbf04SAlexander Shishkin 
228*14cdbf04SAlexander Shishkin 	return 0;
229*14cdbf04SAlexander Shishkin }
230*14cdbf04SAlexander Shishkin 
231*14cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev)
232*14cdbf04SAlexander Shishkin {
233*14cdbf04SAlexander Shishkin }
234*14cdbf04SAlexander Shishkin 
235*14cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = {
236*14cdbf04SAlexander Shishkin 	.probe	= intel_th_pti_probe,
237*14cdbf04SAlexander Shishkin 	.remove	= intel_th_pti_remove,
238*14cdbf04SAlexander Shishkin 	.activate	= intel_th_pti_activate,
239*14cdbf04SAlexander Shishkin 	.deactivate	= intel_th_pti_deactivate,
240*14cdbf04SAlexander Shishkin 	.driver	= {
241*14cdbf04SAlexander Shishkin 		.name	= "pti",
242*14cdbf04SAlexander Shishkin 		.owner	= THIS_MODULE,
243*14cdbf04SAlexander Shishkin 	},
244*14cdbf04SAlexander Shishkin };
245*14cdbf04SAlexander Shishkin 
246*14cdbf04SAlexander Shishkin module_driver(intel_th_pti_driver,
247*14cdbf04SAlexander Shishkin 	      intel_th_driver_register,
248*14cdbf04SAlexander Shishkin 	      intel_th_driver_unregister);
249*14cdbf04SAlexander Shishkin 
250*14cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2");
251*14cdbf04SAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver");
252*14cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
253