2 * Intel(R) Trace Hub Global Trace Hub
4 * Copyright (C) 2014-2015 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>
23 #include <linux/slab.h>
24 #include <linux/bitmap.h>
25 #include <linux/pm_runtime.h>
33 * struct gth_output - GTH view on an output port
34 * @gth: backlink to the GTH device
35 * @output: link to output device's output descriptor
36 * @index: output port number
37 * @port_type: one of GTH_* port type values
38 * @master: bitmap of masters configured for this output
41 struct gth_device
*gth
;
42 struct intel_th_output
*output
;
44 unsigned int port_type
;
45 DECLARE_BITMAP(master
, TH_CONFIGURABLE_MASTERS
+ 1);
49 * struct gth_device - GTH device
50 * @dev: driver core's device
51 * @base: register window base address
52 * @output_group: attributes describing output ports
53 * @master_group: attributes describing master assignments
54 * @output: output ports
55 * @master: master/output port assignments
56 * @gth_lock: serializes accesses to GTH bits
62 struct attribute_group output_group
;
63 struct attribute_group master_group
;
64 struct gth_output output
[TH_POSSIBLE_OUTPUTS
];
65 signed char master
[TH_CONFIGURABLE_MASTERS
+ 1];
69 static void gth_output_set(struct gth_device
*gth
, int port
,
72 unsigned long reg
= port
& 4 ? REG_GTH_GTHOPT1
: REG_GTH_GTHOPT0
;
74 int shift
= (port
& 3) * 8;
76 val
= ioread32(gth
->base
+ reg
);
77 val
&= ~(0xff << shift
);
78 val
|= config
<< shift
;
79 iowrite32(val
, gth
->base
+ reg
);
82 static unsigned int gth_output_get(struct gth_device
*gth
, int port
)
84 unsigned long reg
= port
& 4 ? REG_GTH_GTHOPT1
: REG_GTH_GTHOPT0
;
86 int shift
= (port
& 3) * 8;
88 val
= ioread32(gth
->base
+ reg
);
95 static void gth_smcfreq_set(struct gth_device
*gth
, int port
,
98 unsigned long reg
= REG_GTH_SMCR0
+ ((port
/ 2) * 4);
99 int shift
= (port
& 1) * 16;
102 val
= ioread32(gth
->base
+ reg
);
103 val
&= ~(0xffff << shift
);
104 val
|= freq
<< shift
;
105 iowrite32(val
, gth
->base
+ reg
);
108 static unsigned int gth_smcfreq_get(struct gth_device
*gth
, int port
)
110 unsigned long reg
= REG_GTH_SMCR0
+ ((port
/ 2) * 4);
111 int shift
= (port
& 1) * 16;
114 val
= ioread32(gth
->base
+ reg
);
115 val
&= 0xffff << shift
;
122 * "masters" attribute group
125 struct master_attribute
{
126 struct device_attribute attr
;
127 struct gth_device
*gth
;
132 gth_master_set(struct gth_device
*gth
, unsigned int master
, int port
)
134 unsigned int reg
= REG_GTH_SWDEST0
+ ((master
>> 1) & ~3u);
135 unsigned int shift
= (master
& 0x7) * 4;
139 reg
= REG_GTH_GSWTDEST
;
143 val
= ioread32(gth
->base
+ reg
);
144 val
&= ~(0xf << shift
);
146 val
|= (0x8 | port
) << shift
;
147 iowrite32(val
, gth
->base
+ reg
);
150 static ssize_t
master_attr_show(struct device
*dev
,
151 struct device_attribute
*attr
,
154 struct master_attribute
*ma
=
155 container_of(attr
, struct master_attribute
, attr
);
156 struct gth_device
*gth
= ma
->gth
;
160 spin_lock(>h
->gth_lock
);
161 port
= gth
->master
[ma
->master
];
162 spin_unlock(>h
->gth_lock
);
165 count
= snprintf(buf
, PAGE_SIZE
, "%x\n", port
);
167 count
= snprintf(buf
, PAGE_SIZE
, "disabled\n");
172 static ssize_t
master_attr_store(struct device
*dev
,
173 struct device_attribute
*attr
,
174 const char *buf
, size_t count
)
176 struct master_attribute
*ma
=
177 container_of(attr
, struct master_attribute
, attr
);
178 struct gth_device
*gth
= ma
->gth
;
181 if (kstrtoint(buf
, 10, &port
) < 0)
184 if (port
>= TH_POSSIBLE_OUTPUTS
|| port
< -1)
187 spin_lock(>h
->gth_lock
);
189 /* disconnect from the previous output port, if any */
190 old_port
= gth
->master
[ma
->master
];
192 gth
->master
[ma
->master
] = -1;
193 clear_bit(ma
->master
, gth
->output
[old_port
].master
);
196 * if the port is active, program this setting,
197 * implies that runtime PM is on
199 if (gth
->output
[old_port
].output
->active
)
200 gth_master_set(gth
, ma
->master
, -1);
203 /* connect to the new output port, if any */
205 /* check if there's a driver for this port */
206 if (!gth
->output
[port
].output
) {
211 set_bit(ma
->master
, gth
->output
[port
].master
);
213 /* if the port is active, program this setting, see above */
214 if (gth
->output
[port
].output
->active
)
215 gth_master_set(gth
, ma
->master
, port
);
218 gth
->master
[ma
->master
] = port
;
221 spin_unlock(>h
->gth_lock
);
226 struct output_attribute
{
227 struct device_attribute attr
;
228 struct gth_device
*gth
;
233 #define OUTPUT_PARM(_name, _mask, _r, _w, _what) \
234 [TH_OUTPUT_PARM(_name)] = { .name = __stringify(_name), \
235 .get = gth_ ## _what ## _get, \
236 .set = gth_ ## _what ## _set, \
241 static const struct output_parm
{
243 unsigned int (*get
)(struct gth_device
*gth
, int port
);
244 void (*set
)(struct gth_device
*gth
, int port
,
247 unsigned int readable
: 1,
250 OUTPUT_PARM(port
, 0x7, 1, 0, output
),
251 OUTPUT_PARM(null
, BIT(3), 1, 1, output
),
252 OUTPUT_PARM(drop
, BIT(4), 1, 1, output
),
253 OUTPUT_PARM(reset
, BIT(5), 1, 0, output
),
254 OUTPUT_PARM(flush
, BIT(7), 0, 1, output
),
255 OUTPUT_PARM(smcfreq
, 0xffff, 1, 1, smcfreq
),
259 gth_output_parm_set(struct gth_device
*gth
, int port
, unsigned int parm
,
262 unsigned int config
= output_parms
[parm
].get(gth
, port
);
263 unsigned int mask
= output_parms
[parm
].mask
;
264 unsigned int shift
= __ffs(mask
);
267 config
|= (val
<< shift
) & mask
;
268 output_parms
[parm
].set(gth
, port
, config
);
272 gth_output_parm_get(struct gth_device
*gth
, int port
, unsigned int parm
)
274 unsigned int config
= output_parms
[parm
].get(gth
, port
);
275 unsigned int mask
= output_parms
[parm
].mask
;
276 unsigned int shift
= __ffs(mask
);
284 * Reset outputs and sources
286 static int intel_th_gth_reset(struct gth_device
*gth
)
291 scratchpad
= ioread32(gth
->base
+ REG_GTH_SCRPD0
);
292 if (scratchpad
& SCRPD_DEBUGGER_IN_USE
)
295 /* Always save/restore STH and TU registers in S0ix entry/exit */
296 scratchpad
|= SCRPD_STH_IS_ENABLED
| SCRPD_TRIGGER_IS_ENABLED
;
297 iowrite32(scratchpad
, gth
->base
+ REG_GTH_SCRPD0
);
300 for (port
= 0; port
< 8; port
++) {
301 if (gth_output_parm_get(gth
, port
, TH_OUTPUT_PARM(port
)) ==
305 gth_output_set(gth
, port
, 0);
306 gth_smcfreq_set(gth
, port
, 16);
308 /* disable overrides */
309 iowrite32(0, gth
->base
+ REG_GTH_DESTOVR
);
311 /* masters swdest_0~31 and gswdest */
312 for (i
= 0; i
< 33; i
++)
313 iowrite32(0, gth
->base
+ REG_GTH_SWDEST0
+ i
* 4);
316 iowrite32(0, gth
->base
+ REG_GTH_SCR
);
317 iowrite32(0xfc, gth
->base
+ REG_GTH_SCR2
);
323 * "outputs" attribute group
326 static ssize_t
output_attr_show(struct device
*dev
,
327 struct device_attribute
*attr
,
330 struct output_attribute
*oa
=
331 container_of(attr
, struct output_attribute
, attr
);
332 struct gth_device
*gth
= oa
->gth
;
335 pm_runtime_get_sync(dev
);
337 spin_lock(>h
->gth_lock
);
338 count
= snprintf(buf
, PAGE_SIZE
, "%x\n",
339 gth_output_parm_get(gth
, oa
->port
, oa
->parm
));
340 spin_unlock(>h
->gth_lock
);
347 static ssize_t
output_attr_store(struct device
*dev
,
348 struct device_attribute
*attr
,
349 const char *buf
, size_t count
)
351 struct output_attribute
*oa
=
352 container_of(attr
, struct output_attribute
, attr
);
353 struct gth_device
*gth
= oa
->gth
;
356 if (kstrtouint(buf
, 16, &config
) < 0)
359 pm_runtime_get_sync(dev
);
361 spin_lock(>h
->gth_lock
);
362 gth_output_parm_set(gth
, oa
->port
, oa
->parm
, config
);
363 spin_unlock(>h
->gth_lock
);
370 static int intel_th_master_attributes(struct gth_device
*gth
)
372 struct master_attribute
*master_attrs
;
373 struct attribute
**attrs
;
374 int i
, nattrs
= TH_CONFIGURABLE_MASTERS
+ 2;
376 attrs
= devm_kcalloc(gth
->dev
, nattrs
, sizeof(void *), GFP_KERNEL
);
380 master_attrs
= devm_kcalloc(gth
->dev
, nattrs
,
381 sizeof(struct master_attribute
),
386 for (i
= 0; i
< TH_CONFIGURABLE_MASTERS
+ 1; i
++) {
389 name
= devm_kasprintf(gth
->dev
, GFP_KERNEL
, "%d%s", i
,
390 i
== TH_CONFIGURABLE_MASTERS
? "+" : "");
394 master_attrs
[i
].attr
.attr
.name
= name
;
395 master_attrs
[i
].attr
.attr
.mode
= S_IRUGO
| S_IWUSR
;
396 master_attrs
[i
].attr
.show
= master_attr_show
;
397 master_attrs
[i
].attr
.store
= master_attr_store
;
399 sysfs_attr_init(&master_attrs
[i
].attr
.attr
);
400 attrs
[i
] = &master_attrs
[i
].attr
.attr
;
402 master_attrs
[i
].gth
= gth
;
403 master_attrs
[i
].master
= i
;
406 gth
->master_group
.name
= "masters";
407 gth
->master_group
.attrs
= attrs
;
409 return sysfs_create_group(>h
->dev
->kobj
, >h
->master_group
);
412 static int intel_th_output_attributes(struct gth_device
*gth
)
414 struct output_attribute
*out_attrs
;
415 struct attribute
**attrs
;
416 int i
, j
, nouts
= TH_POSSIBLE_OUTPUTS
;
417 int nparms
= ARRAY_SIZE(output_parms
);
418 int nattrs
= nouts
* nparms
+ 1;
420 attrs
= devm_kcalloc(gth
->dev
, nattrs
, sizeof(void *), GFP_KERNEL
);
424 out_attrs
= devm_kcalloc(gth
->dev
, nattrs
,
425 sizeof(struct output_attribute
),
430 for (i
= 0; i
< nouts
; i
++) {
431 for (j
= 0; j
< nparms
; j
++) {
432 unsigned int idx
= i
* nparms
+ j
;
435 name
= devm_kasprintf(gth
->dev
, GFP_KERNEL
, "%d_%s", i
,
436 output_parms
[j
].name
);
440 out_attrs
[idx
].attr
.attr
.name
= name
;
442 if (output_parms
[j
].readable
) {
443 out_attrs
[idx
].attr
.attr
.mode
|= S_IRUGO
;
444 out_attrs
[idx
].attr
.show
= output_attr_show
;
447 if (output_parms
[j
].writable
) {
448 out_attrs
[idx
].attr
.attr
.mode
|= S_IWUSR
;
449 out_attrs
[idx
].attr
.store
= output_attr_store
;
452 sysfs_attr_init(&out_attrs
[idx
].attr
.attr
);
453 attrs
[idx
] = &out_attrs
[idx
].attr
.attr
;
455 out_attrs
[idx
].gth
= gth
;
456 out_attrs
[idx
].port
= i
;
457 out_attrs
[idx
].parm
= j
;
461 gth
->output_group
.name
= "outputs";
462 gth
->output_group
.attrs
= attrs
;
464 return sysfs_create_group(>h
->dev
->kobj
, >h
->output_group
);
468 * intel_th_gth_disable() - disable tracing to an output device
470 * @output: output device's descriptor
472 * This will deconfigure all masters set to output to this device,
473 * disable tracing using force storeEn off signal and wait for the
474 * "pipeline empty" bit for corresponding output port.
476 static void intel_th_gth_disable(struct intel_th_device
*thdev
,
477 struct intel_th_output
*output
)
479 struct gth_device
*gth
= dev_get_drvdata(&thdev
->dev
);
484 spin_lock(>h
->gth_lock
);
485 output
->active
= false;
487 for_each_set_bit(master
, gth
->output
[output
->port
].master
,
488 TH_CONFIGURABLE_MASTERS
) {
489 gth_master_set(gth
, master
, -1);
491 spin_unlock(>h
->gth_lock
);
493 iowrite32(0, gth
->base
+ REG_GTH_SCR
);
494 iowrite32(0xfd, gth
->base
+ REG_GTH_SCR2
);
496 /* wait on pipeline empty for the given port */
497 for (reg
= 0, count
= GTH_PLE_WAITLOOP_DEPTH
;
498 count
&& !(reg
& BIT(output
->port
)); count
--) {
499 reg
= ioread32(gth
->base
+ REG_GTH_STAT
);
503 /* clear force capture done for next captures */
504 iowrite32(0xfc, gth
->base
+ REG_GTH_SCR2
);
507 dev_dbg(&thdev
->dev
, "timeout waiting for GTH[%d] PLE\n",
510 reg
= ioread32(gth
->base
+ REG_GTH_SCRPD0
);
511 reg
&= ~output
->scratchpad
;
512 iowrite32(reg
, gth
->base
+ REG_GTH_SCRPD0
);
516 * intel_th_gth_enable() - enable tracing to an output device
518 * @output: output device's descriptor
520 * This will configure all masters set to output to this device and
521 * enable tracing using force storeEn signal.
523 static void intel_th_gth_enable(struct intel_th_device
*thdev
,
524 struct intel_th_output
*output
)
526 struct gth_device
*gth
= dev_get_drvdata(&thdev
->dev
);
527 u32 scr
= 0xfc0000, scrpd
;
530 spin_lock(>h
->gth_lock
);
531 for_each_set_bit(master
, gth
->output
[output
->port
].master
,
532 TH_CONFIGURABLE_MASTERS
+ 1) {
533 gth_master_set(gth
, master
, output
->port
);
536 if (output
->multiblock
)
539 output
->active
= true;
540 spin_unlock(>h
->gth_lock
);
542 scrpd
= ioread32(gth
->base
+ REG_GTH_SCRPD0
);
543 scrpd
|= output
->scratchpad
;
544 iowrite32(scrpd
, gth
->base
+ REG_GTH_SCRPD0
);
546 iowrite32(scr
, gth
->base
+ REG_GTH_SCR
);
547 iowrite32(0, gth
->base
+ REG_GTH_SCR2
);
551 * intel_th_gth_assign() - assign output device to a GTH output port
553 * @othdev: output device
555 * This will match a given output device parameters against present
556 * output ports on the GTH and fill out relevant bits in output device's
559 * Return: 0 on success, -errno on error.
561 static int intel_th_gth_assign(struct intel_th_device
*thdev
,
562 struct intel_th_device
*othdev
)
564 struct gth_device
*gth
= dev_get_drvdata(&thdev
->dev
);
567 if (othdev
->type
!= INTEL_TH_OUTPUT
)
570 for (i
= 0, id
= 0; i
< TH_POSSIBLE_OUTPUTS
; i
++) {
571 if (gth
->output
[i
].port_type
!= othdev
->output
.type
)
574 if (othdev
->id
== -1 || othdev
->id
== id
)
583 spin_lock(>h
->gth_lock
);
584 othdev
->output
.port
= i
;
585 othdev
->output
.active
= false;
586 gth
->output
[i
].output
= &othdev
->output
;
587 spin_unlock(>h
->gth_lock
);
593 * intel_th_gth_unassign() - deassociate an output device from its output port
595 * @othdev: output device
597 static void intel_th_gth_unassign(struct intel_th_device
*thdev
,
598 struct intel_th_device
*othdev
)
600 struct gth_device
*gth
= dev_get_drvdata(&thdev
->dev
);
601 int port
= othdev
->output
.port
;
603 spin_lock(>h
->gth_lock
);
604 othdev
->output
.port
= -1;
605 othdev
->output
.active
= false;
606 gth
->output
[port
].output
= NULL
;
607 spin_unlock(>h
->gth_lock
);
611 intel_th_gth_set_output(struct intel_th_device
*thdev
, unsigned int master
)
613 struct gth_device
*gth
= dev_get_drvdata(&thdev
->dev
);
614 int port
= 0; /* FIXME: make default output configurable */
617 * everything above TH_CONFIGURABLE_MASTERS is controlled by the
620 if (master
> TH_CONFIGURABLE_MASTERS
)
621 master
= TH_CONFIGURABLE_MASTERS
;
623 spin_lock(>h
->gth_lock
);
624 if (gth
->master
[master
] == -1) {
625 set_bit(master
, gth
->output
[port
].master
);
626 gth
->master
[master
] = port
;
628 spin_unlock(>h
->gth_lock
);
633 static int intel_th_gth_probe(struct intel_th_device
*thdev
)
635 struct device
*dev
= &thdev
->dev
;
636 struct gth_device
*gth
;
637 struct resource
*res
;
641 res
= intel_th_device_get_resource(thdev
, IORESOURCE_MEM
, 0);
645 base
= devm_ioremap(dev
, res
->start
, resource_size(res
));
649 gth
= devm_kzalloc(dev
, sizeof(*gth
), GFP_KERNEL
);
655 spin_lock_init(>h
->gth_lock
);
657 ret
= intel_th_gth_reset(gth
);
661 for (i
= 0; i
< TH_CONFIGURABLE_MASTERS
+ 1; i
++)
664 for (i
= 0; i
< TH_POSSIBLE_OUTPUTS
; i
++) {
665 gth
->output
[i
].gth
= gth
;
666 gth
->output
[i
].index
= i
;
667 gth
->output
[i
].port_type
=
668 gth_output_parm_get(gth
, i
, TH_OUTPUT_PARM(port
));
671 if (intel_th_output_attributes(gth
) ||
672 intel_th_master_attributes(gth
)) {
673 pr_warn("Can't initialize sysfs attributes\n");
675 if (gth
->output_group
.attrs
)
676 sysfs_remove_group(>h
->dev
->kobj
, >h
->output_group
);
680 dev_set_drvdata(dev
, gth
);
685 static void intel_th_gth_remove(struct intel_th_device
*thdev
)
687 struct gth_device
*gth
= dev_get_drvdata(&thdev
->dev
);
689 sysfs_remove_group(>h
->dev
->kobj
, >h
->output_group
);
690 sysfs_remove_group(>h
->dev
->kobj
, >h
->master_group
);
693 static struct intel_th_driver intel_th_gth_driver
= {
694 .probe
= intel_th_gth_probe
,
695 .remove
= intel_th_gth_remove
,
696 .assign
= intel_th_gth_assign
,
697 .unassign
= intel_th_gth_unassign
,
698 .set_output
= intel_th_gth_set_output
,
699 .enable
= intel_th_gth_enable
,
700 .disable
= intel_th_gth_disable
,
703 .owner
= THIS_MODULE
,
707 module_driver(intel_th_gth_driver
,
708 intel_th_driver_register
,
709 intel_th_driver_unregister
);
711 MODULE_ALIAS("intel_th_switch");
712 MODULE_LICENSE("GPL v2");
713 MODULE_DESCRIPTION("Intel(R) Trace Hub Global Trace Hub driver");
714 MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");