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