xref: /linux/drivers/hwtracing/intel_th/pti.c (revision d9962f6f)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Intel(R) Trace Hub PTI output driver
4  *
5  * Copyright (C) 2014-2016 Intel Corporation.
6  */
7 
8 #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
9 
10 #include <linux/types.h>
11 #include <linux/module.h>
12 #include <linux/device.h>
13 #include <linux/sizes.h>
14 #include <linux/printk.h>
15 #include <linux/slab.h>
16 #include <linux/mm.h>
17 #include <linux/io.h>
18 
19 #include "intel_th.h"
20 #include "pti.h"
21 
22 struct pti_device {
23 	void __iomem		*base;
24 	struct intel_th_device	*thdev;
25 	unsigned int		mode;
26 	unsigned int		freeclk;
27 	unsigned int		clkdiv;
28 	unsigned int		patgen;
29 	unsigned int		lpp_dest_mask;
30 	unsigned int		lpp_dest;
31 };
32 
33 /* map PTI widths to MODE settings of PTI_CTL register */
34 static const unsigned int pti_mode[] = {
35 	0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
36 };
37 
pti_width_mode(unsigned int width)38 static int pti_width_mode(unsigned int width)
39 {
40 	int i;
41 
42 	for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
43 		if (pti_mode[i] == width)
44 			return i;
45 
46 	return -EINVAL;
47 }
48 
mode_show(struct device * dev,struct device_attribute * attr,char * buf)49 static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
50 			 char *buf)
51 {
52 	struct pti_device *pti = dev_get_drvdata(dev);
53 
54 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
55 }
56 
mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)57 static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
58 			  const char *buf, size_t size)
59 {
60 	struct pti_device *pti = dev_get_drvdata(dev);
61 	unsigned long val;
62 	int ret;
63 
64 	ret = kstrtoul(buf, 10, &val);
65 	if (ret)
66 		return ret;
67 
68 	ret = pti_width_mode(val);
69 	if (ret < 0)
70 		return ret;
71 
72 	pti->mode = ret;
73 
74 	return size;
75 }
76 
77 static DEVICE_ATTR_RW(mode);
78 
79 static ssize_t
freerunning_clock_show(struct device * dev,struct device_attribute * attr,char * buf)80 freerunning_clock_show(struct device *dev, struct device_attribute *attr,
81 		       char *buf)
82 {
83 	struct pti_device *pti = dev_get_drvdata(dev);
84 
85 	return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
86 }
87 
88 static ssize_t
freerunning_clock_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)89 freerunning_clock_store(struct device *dev, struct device_attribute *attr,
90 			const char *buf, size_t size)
91 {
92 	struct pti_device *pti = dev_get_drvdata(dev);
93 	unsigned long val;
94 	int ret;
95 
96 	ret = kstrtoul(buf, 10, &val);
97 	if (ret)
98 		return ret;
99 
100 	pti->freeclk = !!val;
101 
102 	return size;
103 }
104 
105 static DEVICE_ATTR_RW(freerunning_clock);
106 
107 static ssize_t
clock_divider_show(struct device * dev,struct device_attribute * attr,char * buf)108 clock_divider_show(struct device *dev, struct device_attribute *attr,
109 		   char *buf)
110 {
111 	struct pti_device *pti = dev_get_drvdata(dev);
112 
113 	return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
114 }
115 
116 static ssize_t
clock_divider_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)117 clock_divider_store(struct device *dev, struct device_attribute *attr,
118 		    const char *buf, size_t size)
119 {
120 	struct pti_device *pti = dev_get_drvdata(dev);
121 	unsigned long val;
122 	int ret;
123 
124 	ret = kstrtoul(buf, 10, &val);
125 	if (ret)
126 		return ret;
127 
128 	if (!is_power_of_2(val) || val > 8 || !val)
129 		return -EINVAL;
130 
131 	pti->clkdiv = val;
132 
133 	return size;
134 }
135 
136 static DEVICE_ATTR_RW(clock_divider);
137 
138 static struct attribute *pti_output_attrs[] = {
139 	&dev_attr_mode.attr,
140 	&dev_attr_freerunning_clock.attr,
141 	&dev_attr_clock_divider.attr,
142 	NULL,
143 };
144 
145 static const struct attribute_group pti_output_group = {
146 	.attrs	= pti_output_attrs,
147 };
148 
intel_th_pti_activate(struct intel_th_device * thdev)149 static int intel_th_pti_activate(struct intel_th_device *thdev)
150 {
151 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
152 	u32 ctl = PTI_EN;
153 
154 	if (pti->patgen)
155 		ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
156 	if (pti->freeclk)
157 		ctl |= PTI_FCEN;
158 	ctl |= pti->mode << __ffs(PTI_MODE);
159 	ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
160 	ctl |= pti->lpp_dest << __ffs(LPP_DEST);
161 
162 	iowrite32(ctl, pti->base + REG_PTI_CTL);
163 
164 	intel_th_trace_enable(thdev);
165 
166 	return 0;
167 }
168 
intel_th_pti_deactivate(struct intel_th_device * thdev)169 static void intel_th_pti_deactivate(struct intel_th_device *thdev)
170 {
171 	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
172 
173 	intel_th_trace_disable(thdev);
174 
175 	iowrite32(0, pti->base + REG_PTI_CTL);
176 }
177 
read_hw_config(struct pti_device * pti)178 static void read_hw_config(struct pti_device *pti)
179 {
180 	u32 ctl = ioread32(pti->base + REG_PTI_CTL);
181 
182 	pti->mode	= (ctl & PTI_MODE) >> __ffs(PTI_MODE);
183 	pti->clkdiv	= (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
184 	pti->freeclk	= !!(ctl & PTI_FCEN);
185 
186 	if (!pti_mode[pti->mode])
187 		pti->mode = pti_width_mode(4);
188 	if (!pti->clkdiv)
189 		pti->clkdiv = 1;
190 
191 	if (pti->thdev->output.type == GTH_LPP) {
192 		if (ctl & LPP_PTIPRESENT)
193 			pti->lpp_dest_mask |= LPP_DEST_PTI;
194 		if (ctl & LPP_BSSBPRESENT)
195 			pti->lpp_dest_mask |= LPP_DEST_EXI;
196 		if (ctl & LPP_DEST)
197 			pti->lpp_dest = 1;
198 	}
199 }
200 
intel_th_pti_probe(struct intel_th_device * thdev)201 static int intel_th_pti_probe(struct intel_th_device *thdev)
202 {
203 	struct device *dev = &thdev->dev;
204 	struct resource *res;
205 	struct pti_device *pti;
206 	void __iomem *base;
207 
208 	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
209 	if (!res)
210 		return -ENODEV;
211 
212 	base = devm_ioremap(dev, res->start, resource_size(res));
213 	if (!base)
214 		return -ENOMEM;
215 
216 	pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
217 	if (!pti)
218 		return -ENOMEM;
219 
220 	pti->thdev = thdev;
221 	pti->base = base;
222 
223 	read_hw_config(pti);
224 
225 	dev_set_drvdata(dev, pti);
226 
227 	return 0;
228 }
229 
intel_th_pti_remove(struct intel_th_device * thdev)230 static void intel_th_pti_remove(struct intel_th_device *thdev)
231 {
232 }
233 
234 static struct intel_th_driver intel_th_pti_driver = {
235 	.probe	= intel_th_pti_probe,
236 	.remove	= intel_th_pti_remove,
237 	.activate	= intel_th_pti_activate,
238 	.deactivate	= intel_th_pti_deactivate,
239 	.attr_group	= &pti_output_group,
240 	.driver	= {
241 		.name	= "pti",
242 		.owner	= THIS_MODULE,
243 	},
244 };
245 
246 static const char * const lpp_dest_str[] = { "pti", "exi" };
247 
lpp_dest_show(struct device * dev,struct device_attribute * attr,char * buf)248 static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
249 			     char *buf)
250 {
251 	struct pti_device *pti = dev_get_drvdata(dev);
252 	ssize_t ret = 0;
253 	int i;
254 
255 	for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
256 		const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
257 
258 		if (!(pti->lpp_dest_mask & BIT(i)))
259 			continue;
260 
261 		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
262 				 fmt, lpp_dest_str[i]);
263 	}
264 
265 	if (ret)
266 		buf[ret - 1] = '\n';
267 
268 	return ret;
269 }
270 
lpp_dest_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)271 static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
272 			      const char *buf, size_t size)
273 {
274 	struct pti_device *pti = dev_get_drvdata(dev);
275 	int i;
276 
277 	i = sysfs_match_string(lpp_dest_str, buf);
278 	if (i < 0)
279 		return i;
280 
281 	if (!(pti->lpp_dest_mask & BIT(i)))
282 		return -EINVAL;
283 
284 	pti->lpp_dest = i;
285 	return size;
286 }
287 
288 static DEVICE_ATTR_RW(lpp_dest);
289 
290 static struct attribute *lpp_output_attrs[] = {
291 	&dev_attr_mode.attr,
292 	&dev_attr_freerunning_clock.attr,
293 	&dev_attr_clock_divider.attr,
294 	&dev_attr_lpp_dest.attr,
295 	NULL,
296 };
297 
298 static const struct attribute_group lpp_output_group = {
299 	.attrs	= lpp_output_attrs,
300 };
301 
302 static struct intel_th_driver intel_th_lpp_driver = {
303 	.probe		= intel_th_pti_probe,
304 	.remove		= intel_th_pti_remove,
305 	.activate	= intel_th_pti_activate,
306 	.deactivate	= intel_th_pti_deactivate,
307 	.attr_group	= &lpp_output_group,
308 	.driver	= {
309 		.name	= "lpp",
310 		.owner	= THIS_MODULE,
311 	},
312 };
313 
intel_th_pti_lpp_init(void)314 static int __init intel_th_pti_lpp_init(void)
315 {
316 	int err;
317 
318 	err = intel_th_driver_register(&intel_th_pti_driver);
319 	if (err)
320 		return err;
321 
322 	err = intel_th_driver_register(&intel_th_lpp_driver);
323 	if (err) {
324 		intel_th_driver_unregister(&intel_th_pti_driver);
325 		return err;
326 	}
327 
328 	return 0;
329 }
330 
331 module_init(intel_th_pti_lpp_init);
332 
intel_th_pti_lpp_exit(void)333 static void __exit intel_th_pti_lpp_exit(void)
334 {
335 	intel_th_driver_unregister(&intel_th_pti_driver);
336 	intel_th_driver_unregister(&intel_th_lpp_driver);
337 }
338 
339 module_exit(intel_th_pti_lpp_exit);
340 
341 MODULE_LICENSE("GPL v2");
342 MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");
343 MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
344