Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
33f9cff
Documentation
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
cpufreq
cpuidle
crypto
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fmc
fpga
gpio
gpu
hid
hsi
hv
hwmon
hwspinlock
hwtracing
coresight
intel_th
Kconfig
Makefile
core.c
debug.c
debug.h
gth.c
gth.h
intel_th.h
msu.c
msu.h
pci.c
pti.c
pti.h
sth.c
sth.h
stm
i2c
ide
idle
iio
infiniband
input
iommu
ipack
irqchip
isdn
leds
lguest
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
mtd
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
sn
soc
spi
spmi
ssb
staging
target
tc
thermal
thunderbolt
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.get_maintainer.ignore
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
drivers
/
hwtracing
/
intel_th
/
core.c
Copy path
Blame
Blame
Latest commit
History
History
725 lines (592 loc) · 15.9 KB
Breadcrumbs
linux
/
drivers
/
hwtracing
/
intel_th
/
core.c
Top
File metadata and controls
Code
Blame
725 lines (592 loc) · 15.9 KB
Raw
/* * Intel(R) Trace Hub driver core * * Copyright (C) 2014-2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/types.h> #include <linux/module.h> #include <linux/device.h> #include <linux/sysfs.h> #include <linux/kdev_t.h> #include <linux/debugfs.h> #include <linux/idr.h> #include <linux/pci.h> #include <linux/dma-mapping.h> #include "intel_th.h" #include "debug.h" static DEFINE_IDA(intel_th_ida); static int intel_th_match(struct device *dev, struct device_driver *driver) { struct intel_th_driver *thdrv = to_intel_th_driver(driver); struct intel_th_device *thdev = to_intel_th_device(dev); if (thdev->type == INTEL_TH_SWITCH && (!thdrv->enable || !thdrv->disable)) return 0; return !strcmp(thdev->name, driver->name); } static int intel_th_child_remove(struct device *dev, void *data) { device_release_driver(dev); return 0; } static int intel_th_probe(struct device *dev) { struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); struct intel_th_device *thdev = to_intel_th_device(dev); struct intel_th_driver *hubdrv; struct intel_th_device *hub = NULL; int ret; if (thdev->type == INTEL_TH_SWITCH) hub = thdev; else if (dev->parent) hub = to_intel_th_device(dev->parent); if (!hub || !hub->dev.driver) return -EPROBE_DEFER; hubdrv = to_intel_th_driver(hub->dev.driver); ret = thdrv->probe(to_intel_th_device(dev)); if (ret) return ret; if (thdev->type == INTEL_TH_OUTPUT && !intel_th_output_assigned(thdev)) ret = hubdrv->assign(hub, thdev); return ret; } static int intel_th_remove(struct device *dev) { struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); struct intel_th_device *thdev = to_intel_th_device(dev); struct intel_th_device *hub = to_intel_th_device(dev->parent); int err; if (thdev->type == INTEL_TH_SWITCH) { err = device_for_each_child(dev, thdev, intel_th_child_remove); if (err) return err; } thdrv->remove(thdev); if (intel_th_output_assigned(thdev)) { struct intel_th_driver *hubdrv = to_intel_th_driver(dev->parent->driver); if (hub->dev.driver) hubdrv->unassign(hub, thdev); } return 0; } static struct bus_type intel_th_bus = { .name = "intel_th", .dev_attrs = NULL, .match = intel_th_match, .probe = intel_th_probe, .remove = intel_th_remove, }; static void intel_th_device_free(struct intel_th_device *thdev); static void intel_th_device_release(struct device *dev) { intel_th_device_free(to_intel_th_device(dev)); } static struct device_type intel_th_source_device_type = { .name = "intel_th_source_device", .release = intel_th_device_release, }; static char *intel_th_output_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) { struct intel_th_device *thdev = to_intel_th_device(dev); char *node; if (thdev->id >= 0) node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", 0, thdev->name, thdev->id); else node = kasprintf(GFP_KERNEL, "intel_th%d/%s", 0, thdev->name); return node; } static ssize_t port_show(struct device *dev, struct device_attribute *attr, char *buf) { struct intel_th_device *thdev = to_intel_th_device(dev); if (thdev->output.port >= 0) return scnprintf(buf, PAGE_SIZE, "%u\n", thdev->output.port); return scnprintf(buf, PAGE_SIZE, "unassigned\n"); } static DEVICE_ATTR_RO(port); static int intel_th_output_activate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver); if (thdrv->activate) return thdrv->activate(thdev); intel_th_trace_enable(thdev); return 0; } static void intel_th_output_deactivate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver); if (thdrv->deactivate) thdrv->deactivate(thdev); else intel_th_trace_disable(thdev); } static ssize_t active_show(struct device *dev, struct device_attribute *attr, char *buf) { struct intel_th_device *thdev = to_intel_th_device(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", thdev->output.active); } static ssize_t active_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct intel_th_device *thdev = to_intel_th_device(dev); unsigned long val; int ret; ret = kstrtoul(buf, 10, &val); if (ret) return ret; if (!!val != thdev->output.active) { if (val) ret = intel_th_output_activate(thdev); else intel_th_output_deactivate(thdev); } return ret ? ret : size; } static DEVICE_ATTR_RW(active); static struct attribute *intel_th_output_attrs[] = { &dev_attr_port.attr, &dev_attr_active.attr, NULL, }; ATTRIBUTE_GROUPS(intel_th_output); static struct device_type intel_th_output_device_type = { .name = "intel_th_output_device", .groups = intel_th_output_groups, .release = intel_th_device_release, .devnode = intel_th_output_devnode, }; static struct device_type intel_th_switch_device_type = { .name = "intel_th_switch_device", .release = intel_th_device_release, }; static struct device_type *intel_th_device_type[] = { [INTEL_TH_SOURCE] = &intel_th_source_device_type, [INTEL_TH_OUTPUT] = &intel_th_output_device_type, [INTEL_TH_SWITCH] = &intel_th_switch_device_type, }; int intel_th_driver_register(struct intel_th_driver *thdrv) { if (!thdrv->probe || !thdrv->remove) return -EINVAL; thdrv->driver.bus = &intel_th_bus; return driver_register(&thdrv->driver); } EXPORT_SYMBOL_GPL(intel_th_driver_register); void intel_th_driver_unregister(struct intel_th_driver *thdrv) { driver_unregister(&thdrv->driver); } EXPORT_SYMBOL_GPL(intel_th_driver_unregister); static struct intel_th_device * intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, int id) { struct device *parent; struct intel_th_device *thdev; if (type == INTEL_TH_SWITCH) parent = th->dev; else parent = &th->hub->dev; thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL); if (!thdev) return NULL; thdev->id = id; thdev->type = type; strcpy(thdev->name, name); device_initialize(&thdev->dev); thdev->dev.bus = &intel_th_bus; thdev->dev.type = intel_th_device_type[type]; thdev->dev.parent = parent; thdev->dev.dma_mask = parent->dma_mask; thdev->dev.dma_parms = parent->dma_parms; dma_set_coherent_mask(&thdev->dev, parent->coherent_dma_mask); if (id >= 0) dev_set_name(&thdev->dev, "%d-%s%d", th->id, name, id); else dev_set_name(&thdev->dev, "%d-%s", th->id, name); return thdev; } static int intel_th_device_add_resources(struct intel_th_device *thdev, struct resource *res, int nres) { struct resource *r; r = kmemdup(res, sizeof(*res) * nres, GFP_KERNEL); if (!r) return -ENOMEM; thdev->resource = r; thdev->num_resources = nres; return 0; } static void intel_th_device_remove(struct intel_th_device *thdev) { device_del(&thdev->dev); put_device(&thdev->dev); } static void intel_th_device_free(struct intel_th_device *thdev) { kfree(thdev->resource); kfree(thdev); } /* * Intel(R) Trace Hub subdevices */ static struct intel_th_subdevice { const char *name; struct resource res[3]; unsigned nres; unsigned type; unsigned otype; int id; } intel_th_subdevices[TH_SUBDEVICE_MAX] = { { .nres = 1, .res = { { .start = REG_GTH_OFFSET, .end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, .name = "gth", .type = INTEL_TH_SWITCH, .id = -1, }, { .nres = 2, .res = { { .start = REG_MSU_OFFSET, .end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1, .flags = IORESOURCE_MEM, }, { .start = BUF_MSU_OFFSET, .end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, .name = "msc", .id = 0, .type = INTEL_TH_OUTPUT, .otype = GTH_MSU, }, { .nres = 2, .res = { { .start = REG_MSU_OFFSET, .end = REG_MSU_OFFSET + REG_MSU_LENGTH - 1, .flags = IORESOURCE_MEM, }, { .start = BUF_MSU_OFFSET, .end = BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, .name = "msc", .id = 1, .type = INTEL_TH_OUTPUT, .otype = GTH_MSU, }, { .nres = 2, .res = { { .start = REG_STH_OFFSET, .end = REG_STH_OFFSET + REG_STH_LENGTH - 1, .flags = IORESOURCE_MEM, }, { .start = TH_MMIO_SW, .end = 0, .flags = IORESOURCE_MEM, }, }, .id = -1, .name = "sth", .type = INTEL_TH_SOURCE, }, { .nres = 1, .res = { { .start = REG_PTI_OFFSET, .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, .id = -1, .name = "pti", .type = INTEL_TH_OUTPUT, .otype = GTH_PTI, }, { .nres = 1, .res = { { .start = REG_DCIH_OFFSET, .end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, .id = -1, .name = "dcih", .type = INTEL_TH_OUTPUT, }, }; #ifdef CONFIG_MODULES static void __intel_th_request_hub_module(struct work_struct *work) { struct intel_th *th = container_of(work, struct intel_th, request_module_work); request_module("intel_th_%s", th->hub->name); } static int intel_th_request_hub_module(struct intel_th *th) { INIT_WORK(&th->request_module_work, __intel_th_request_hub_module); schedule_work(&th->request_module_work); return 0; } static void intel_th_request_hub_module_flush(struct intel_th *th) { flush_work(&th->request_module_work); } #else static inline int intel_th_request_hub_module(struct intel_th *th) { return -EINVAL; } static inline void intel_th_request_hub_module_flush(struct intel_th *th) { } #endif /* CONFIG_MODULES */ static int intel_th_populate(struct intel_th *th, struct resource *devres, unsigned int ndevres, int irq) { struct resource res[3]; unsigned int req = 0; int i, err; /* create devices for each intel_th_subdevice */ for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) { struct intel_th_subdevice *subdev = &intel_th_subdevices[i]; struct intel_th_device *thdev; int r; thdev = intel_th_device_alloc(th, subdev->type, subdev->name, subdev->id); if (!thdev) { err = -ENOMEM; goto kill_subdevs; } memcpy(res, subdev->res, sizeof(struct resource) * subdev->nres); for (r = 0; r < subdev->nres; r++) { int bar = TH_MMIO_CONFIG; /* * Take .end == 0 to mean 'take the whole bar', * .start then tells us which bar it is. Default to * TH_MMIO_CONFIG. */ if (!res[r].end && res[r].flags == IORESOURCE_MEM) { bar = res[r].start; res[r].start = 0; res[r].end = resource_size(&devres[bar]) - 1; } if (res[r].flags & IORESOURCE_MEM) { res[r].start += devres[bar].start; res[r].end += devres[bar].start; dev_dbg(th->dev, "%s:%d @ %pR\n", subdev->name, r, &res[r]); } else if (res[r].flags & IORESOURCE_IRQ) { res[r].start = irq; } } err = intel_th_device_add_resources(thdev, res, subdev->nres); if (err) { put_device(&thdev->dev); goto kill_subdevs; } if (subdev->type == INTEL_TH_OUTPUT) { thdev->dev.devt = MKDEV(th->major, i); thdev->output.type = subdev->otype; thdev->output.port = -1; } err = device_add(&thdev->dev); if (err) { put_device(&thdev->dev); goto kill_subdevs; } /* need switch driver to be loaded to enumerate the rest */ if (subdev->type == INTEL_TH_SWITCH && !req) { th->hub = thdev; err = intel_th_request_hub_module(th); if (!err) req++; } th->thdev[i] = thdev; } return 0; kill_subdevs: for (i-- ; i >= 0; i--) intel_th_device_remove(th->thdev[i]); return err; } static int match_devt(struct device *dev, void *data) { dev_t devt = (dev_t)(unsigned long)data; return dev->devt == devt; } static int intel_th_output_open(struct inode *inode, struct file *file) { const struct file_operations *fops; struct intel_th_driver *thdrv; struct device *dev; int err; dev = bus_find_device(&intel_th_bus, NULL, (void *)(unsigned long)inode->i_rdev, match_devt); if (!dev || !dev->driver) return -ENODEV; thdrv = to_intel_th_driver(dev->driver); fops = fops_get(thdrv->fops); if (!fops) return -ENODEV; replace_fops(file, fops); file->private_data = to_intel_th_device(dev); if (file->f_op->open) { err = file->f_op->open(inode, file); return err; } return 0; } static const struct file_operations intel_th_output_fops = { .open = intel_th_output_open, .llseek = noop_llseek, }; /** * intel_th_alloc() - allocate a new Intel TH device and its subdevices * @dev: parent device * @devres: parent's resources * @ndevres: number of resources * @irq: irq number */ struct intel_th * intel_th_alloc(struct device *dev, struct resource *devres, unsigned int ndevres, int irq) { struct intel_th *th; int err; th = kzalloc(sizeof(*th), GFP_KERNEL); if (!th) return ERR_PTR(-ENOMEM); th->id = ida_simple_get(&intel_th_ida, 0, 0, GFP_KERNEL); if (th->id < 0) { err = th->id; goto err_alloc; } th->major = __register_chrdev(0, 0, TH_POSSIBLE_OUTPUTS, "intel_th/output", &intel_th_output_fops); if (th->major < 0) { err = th->major; goto err_ida; } th->dev = dev; err = intel_th_populate(th, devres, ndevres, irq); if (err) goto err_chrdev; return th; err_chrdev: __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, "intel_th/output"); err_ida: ida_simple_remove(&intel_th_ida, th->id); err_alloc: kfree(th); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(intel_th_alloc); void intel_th_free(struct intel_th *th) { int i; intel_th_request_hub_module_flush(th); for (i = 0; i < TH_SUBDEVICE_MAX; i++) if (th->thdev[i] != th->hub) intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->hub); __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, "intel_th/output"); ida_simple_remove(&intel_th_ida, th->id); kfree(th); } EXPORT_SYMBOL_GPL(intel_th_free); /** * intel_th_trace_enable() - enable tracing for an output device * @thdev: output device that requests tracing be enabled */ int intel_th_trace_enable(struct intel_th_device *thdev) { struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) return -EINVAL; if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) return -EINVAL; hubdrv->enable(hub, &thdev->output); return 0; } EXPORT_SYMBOL_GPL(intel_th_trace_enable); /** * intel_th_trace_disable() - disable tracing for an output device * @thdev: output device that requests tracing be disabled */ int intel_th_trace_disable(struct intel_th_device *thdev) { struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH); if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) return -EINVAL; hubdrv->disable(hub, &thdev->output); return 0; } EXPORT_SYMBOL_GPL(intel_th_trace_disable); int intel_th_set_output(struct intel_th_device *thdev, unsigned int master) { struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); if (!hubdrv->set_output) return -ENOTSUPP; return hubdrv->set_output(hub, master); } EXPORT_SYMBOL_GPL(intel_th_set_output); static int __init intel_th_init(void) { intel_th_debug_init(); return bus_register(&intel_th_bus); } subsys_initcall(intel_th_init); static void __exit intel_th_exit(void) { intel_th_debug_done(); bus_unregister(&intel_th_bus); } module_exit(intel_th_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver"); MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
You can’t perform that action at this time.