2 * Intel(R) Trace Hub PTI output driver
4 * Copyright (C) 2014-2016 Intel Corporation.
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.
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
16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
32 struct intel_th_device
*thdev
;
37 unsigned int lpp_dest_mask
;
38 unsigned int lpp_dest
;
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,
46 static int pti_width_mode(unsigned int width
)
50 for (i
= 0; i
< ARRAY_SIZE(pti_mode
); i
++)
51 if (pti_mode
[i
] == width
)
57 static ssize_t
mode_show(struct device
*dev
, struct device_attribute
*attr
,
60 struct pti_device
*pti
= dev_get_drvdata(dev
);
62 return scnprintf(buf
, PAGE_SIZE
, "%d\n", pti_mode
[pti
->mode
]);
65 static ssize_t
mode_store(struct device
*dev
, struct device_attribute
*attr
,
66 const char *buf
, size_t size
)
68 struct pti_device
*pti
= dev_get_drvdata(dev
);
72 ret
= kstrtoul(buf
, 10, &val
);
76 ret
= pti_width_mode(val
);
85 static DEVICE_ATTR_RW(mode
);
88 freerunning_clock_show(struct device
*dev
, struct device_attribute
*attr
,
91 struct pti_device
*pti
= dev_get_drvdata(dev
);
93 return scnprintf(buf
, PAGE_SIZE
, "%d\n", pti
->freeclk
);
97 freerunning_clock_store(struct device
*dev
, struct device_attribute
*attr
,
98 const char *buf
, size_t size
)
100 struct pti_device
*pti
= dev_get_drvdata(dev
);
104 ret
= kstrtoul(buf
, 10, &val
);
108 pti
->freeclk
= !!val
;
113 static DEVICE_ATTR_RW(freerunning_clock
);
116 clock_divider_show(struct device
*dev
, struct device_attribute
*attr
,
119 struct pti_device
*pti
= dev_get_drvdata(dev
);
121 return scnprintf(buf
, PAGE_SIZE
, "%d\n", 1u << pti
->clkdiv
);
125 clock_divider_store(struct device
*dev
, struct device_attribute
*attr
,
126 const char *buf
, size_t size
)
128 struct pti_device
*pti
= dev_get_drvdata(dev
);
132 ret
= kstrtoul(buf
, 10, &val
);
136 if (!is_power_of_2(val
) || val
> 8 || !val
)
144 static DEVICE_ATTR_RW(clock_divider
);
146 static struct attribute
*pti_output_attrs
[] = {
148 &dev_attr_freerunning_clock
.attr
,
149 &dev_attr_clock_divider
.attr
,
153 static struct attribute_group pti_output_group
= {
154 .attrs
= pti_output_attrs
,
157 static int intel_th_pti_activate(struct intel_th_device
*thdev
)
159 struct pti_device
*pti
= dev_get_drvdata(&thdev
->dev
);
163 ctl
|= pti
->patgen
<< __ffs(PTI_PATGENMODE
);
166 ctl
|= pti
->mode
<< __ffs(PTI_MODE
);
167 ctl
|= pti
->clkdiv
<< __ffs(PTI_CLKDIV
);
168 ctl
|= pti
->lpp_dest
<< __ffs(LPP_DEST
);
170 iowrite32(ctl
, pti
->base
+ REG_PTI_CTL
);
172 intel_th_trace_enable(thdev
);
177 static void intel_th_pti_deactivate(struct intel_th_device
*thdev
)
179 struct pti_device
*pti
= dev_get_drvdata(&thdev
->dev
);
181 intel_th_trace_disable(thdev
);
183 iowrite32(0, pti
->base
+ REG_PTI_CTL
);
186 static void read_hw_config(struct pti_device
*pti
)
188 u32 ctl
= ioread32(pti
->base
+ REG_PTI_CTL
);
190 pti
->mode
= (ctl
& PTI_MODE
) >> __ffs(PTI_MODE
);
191 pti
->clkdiv
= (ctl
& PTI_CLKDIV
) >> __ffs(PTI_CLKDIV
);
192 pti
->freeclk
= !!(ctl
& PTI_FCEN
);
194 if (!pti_mode
[pti
->mode
])
195 pti
->mode
= pti_width_mode(4);
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
;
209 static int intel_th_pti_probe(struct intel_th_device
*thdev
)
211 struct device
*dev
= &thdev
->dev
;
212 struct resource
*res
;
213 struct pti_device
*pti
;
216 res
= intel_th_device_get_resource(thdev
, IORESOURCE_MEM
, 0);
220 base
= devm_ioremap(dev
, res
->start
, resource_size(res
));
224 pti
= devm_kzalloc(dev
, sizeof(*pti
), GFP_KERNEL
);
233 dev_set_drvdata(dev
, pti
);
238 static void intel_th_pti_remove(struct intel_th_device
*thdev
)
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
,
250 .owner
= THIS_MODULE
,
254 static const char * const lpp_dest_str
[] = { "pti", "exi" };
256 static ssize_t
lpp_dest_show(struct device
*dev
, struct device_attribute
*attr
,
259 struct pti_device
*pti
= dev_get_drvdata(dev
);
263 for (i
= ARRAY_SIZE(lpp_dest_str
) - 1; i
>= 0; i
--) {
264 const char *fmt
= pti
->lpp_dest
== i
? "[%s] " : "%s ";
266 if (!(pti
->lpp_dest_mask
& BIT(i
)))
269 ret
+= scnprintf(buf
+ ret
, PAGE_SIZE
- ret
,
270 fmt
, lpp_dest_str
[i
]);
279 static ssize_t
lpp_dest_store(struct device
*dev
, struct device_attribute
*attr
,
280 const char *buf
, size_t size
)
282 struct pti_device
*pti
= dev_get_drvdata(dev
);
283 ssize_t ret
= -EINVAL
;
286 for (i
= 0; i
< ARRAY_SIZE(lpp_dest_str
); i
++)
287 if (sysfs_streq(buf
, lpp_dest_str
[i
]))
290 if (i
< ARRAY_SIZE(lpp_dest_str
) && pti
->lpp_dest_mask
& BIT(i
)) {
298 static DEVICE_ATTR_RW(lpp_dest
);
300 static struct attribute
*lpp_output_attrs
[] = {
302 &dev_attr_freerunning_clock
.attr
,
303 &dev_attr_clock_divider
.attr
,
304 &dev_attr_lpp_dest
.attr
,
308 static struct attribute_group lpp_output_group
= {
309 .attrs
= lpp_output_attrs
,
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
,
320 .owner
= THIS_MODULE
,
324 static int __init
intel_th_pti_lpp_init(void)
328 err
= intel_th_driver_register(&intel_th_pti_driver
);
332 err
= intel_th_driver_register(&intel_th_lpp_driver
);
334 intel_th_driver_unregister(&intel_th_pti_driver
);
341 module_init(intel_th_pti_lpp_init
);
343 static void __exit
intel_th_pti_lpp_exit(void)
345 intel_th_driver_unregister(&intel_th_pti_driver
);
346 intel_th_driver_unregister(&intel_th_lpp_driver
);
349 module_exit(intel_th_pti_lpp_exit
);
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>");