1 // SPDX-License-Identifier: GPL-2.0
3 * Intel(R) Trace Hub PTI output driver
5 * Copyright (C) 2014-2016 Intel Corporation.
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
24 struct intel_th_device
*thdev
;
29 unsigned int lpp_dest_mask
;
30 unsigned int lpp_dest
;
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,
38 static int pti_width_mode(unsigned int width
)
42 for (i
= 0; i
< ARRAY_SIZE(pti_mode
); i
++)
43 if (pti_mode
[i
] == width
)
49 static ssize_t
mode_show(struct device
*dev
, struct device_attribute
*attr
,
52 struct pti_device
*pti
= dev_get_drvdata(dev
);
54 return scnprintf(buf
, PAGE_SIZE
, "%d\n", pti_mode
[pti
->mode
]);
57 static ssize_t
mode_store(struct device
*dev
, struct device_attribute
*attr
,
58 const char *buf
, size_t size
)
60 struct pti_device
*pti
= dev_get_drvdata(dev
);
64 ret
= kstrtoul(buf
, 10, &val
);
68 ret
= pti_width_mode(val
);
77 static DEVICE_ATTR_RW(mode
);
80 freerunning_clock_show(struct device
*dev
, struct device_attribute
*attr
,
83 struct pti_device
*pti
= dev_get_drvdata(dev
);
85 return scnprintf(buf
, PAGE_SIZE
, "%d\n", pti
->freeclk
);
89 freerunning_clock_store(struct device
*dev
, struct device_attribute
*attr
,
90 const char *buf
, size_t size
)
92 struct pti_device
*pti
= dev_get_drvdata(dev
);
96 ret
= kstrtoul(buf
, 10, &val
);
100 pti
->freeclk
= !!val
;
105 static DEVICE_ATTR_RW(freerunning_clock
);
108 clock_divider_show(struct device
*dev
, struct device_attribute
*attr
,
111 struct pti_device
*pti
= dev_get_drvdata(dev
);
113 return scnprintf(buf
, PAGE_SIZE
, "%d\n", 1u << pti
->clkdiv
);
117 clock_divider_store(struct device
*dev
, struct device_attribute
*attr
,
118 const char *buf
, size_t size
)
120 struct pti_device
*pti
= dev_get_drvdata(dev
);
124 ret
= kstrtoul(buf
, 10, &val
);
128 if (!is_power_of_2(val
) || val
> 8 || !val
)
136 static DEVICE_ATTR_RW(clock_divider
);
138 static struct attribute
*pti_output_attrs
[] = {
140 &dev_attr_freerunning_clock
.attr
,
141 &dev_attr_clock_divider
.attr
,
145 static struct attribute_group pti_output_group
= {
146 .attrs
= pti_output_attrs
,
149 static int intel_th_pti_activate(struct intel_th_device
*thdev
)
151 struct pti_device
*pti
= dev_get_drvdata(&thdev
->dev
);
155 ctl
|= pti
->patgen
<< __ffs(PTI_PATGENMODE
);
158 ctl
|= pti
->mode
<< __ffs(PTI_MODE
);
159 ctl
|= pti
->clkdiv
<< __ffs(PTI_CLKDIV
);
160 ctl
|= pti
->lpp_dest
<< __ffs(LPP_DEST
);
162 iowrite32(ctl
, pti
->base
+ REG_PTI_CTL
);
164 intel_th_trace_enable(thdev
);
169 static void intel_th_pti_deactivate(struct intel_th_device
*thdev
)
171 struct pti_device
*pti
= dev_get_drvdata(&thdev
->dev
);
173 intel_th_trace_disable(thdev
);
175 iowrite32(0, pti
->base
+ REG_PTI_CTL
);
178 static void read_hw_config(struct pti_device
*pti
)
180 u32 ctl
= ioread32(pti
->base
+ REG_PTI_CTL
);
182 pti
->mode
= (ctl
& PTI_MODE
) >> __ffs(PTI_MODE
);
183 pti
->clkdiv
= (ctl
& PTI_CLKDIV
) >> __ffs(PTI_CLKDIV
);
184 pti
->freeclk
= !!(ctl
& PTI_FCEN
);
186 if (!pti_mode
[pti
->mode
])
187 pti
->mode
= pti_width_mode(4);
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
;
201 static int intel_th_pti_probe(struct intel_th_device
*thdev
)
203 struct device
*dev
= &thdev
->dev
;
204 struct resource
*res
;
205 struct pti_device
*pti
;
208 res
= intel_th_device_get_resource(thdev
, IORESOURCE_MEM
, 0);
212 base
= devm_ioremap(dev
, res
->start
, resource_size(res
));
216 pti
= devm_kzalloc(dev
, sizeof(*pti
), GFP_KERNEL
);
225 dev_set_drvdata(dev
, pti
);
230 static void intel_th_pti_remove(struct intel_th_device
*thdev
)
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
,
242 .owner
= THIS_MODULE
,
246 static const char * const lpp_dest_str
[] = { "pti", "exi" };
248 static ssize_t
lpp_dest_show(struct device
*dev
, struct device_attribute
*attr
,
251 struct pti_device
*pti
= dev_get_drvdata(dev
);
255 for (i
= ARRAY_SIZE(lpp_dest_str
) - 1; i
>= 0; i
--) {
256 const char *fmt
= pti
->lpp_dest
== i
? "[%s] " : "%s ";
258 if (!(pti
->lpp_dest_mask
& BIT(i
)))
261 ret
+= scnprintf(buf
+ ret
, PAGE_SIZE
- ret
,
262 fmt
, lpp_dest_str
[i
]);
271 static ssize_t
lpp_dest_store(struct device
*dev
, struct device_attribute
*attr
,
272 const char *buf
, size_t size
)
274 struct pti_device
*pti
= dev_get_drvdata(dev
);
277 i
= sysfs_match_string(lpp_dest_str
, buf
);
281 if (!(pti
->lpp_dest_mask
& BIT(i
)))
288 static DEVICE_ATTR_RW(lpp_dest
);
290 static struct attribute
*lpp_output_attrs
[] = {
292 &dev_attr_freerunning_clock
.attr
,
293 &dev_attr_clock_divider
.attr
,
294 &dev_attr_lpp_dest
.attr
,
298 static struct attribute_group lpp_output_group
= {
299 .attrs
= lpp_output_attrs
,
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
,
310 .owner
= THIS_MODULE
,
314 static int __init
intel_th_pti_lpp_init(void)
318 err
= intel_th_driver_register(&intel_th_pti_driver
);
322 err
= intel_th_driver_register(&intel_th_lpp_driver
);
324 intel_th_driver_unregister(&intel_th_pti_driver
);
331 module_init(intel_th_pti_lpp_init
);
333 static void __exit
intel_th_pti_lpp_exit(void)
335 intel_th_driver_unregister(&intel_th_pti_driver
);
336 intel_th_driver_unregister(&intel_th_lpp_driver
);
339 module_exit(intel_th_pti_lpp_exit
);
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>");