GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / hwtracing / intel_th / pti.c
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>");