diff options
author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2015-09-22 08:47:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-10-04 15:28:58 -0400 |
commit | 14cdbf041788f150ec3b323583b80bfd5775f73e (patch) | |
tree | 1e0236ddb73b20cfd4ffa047a37dafb4bd470aa9 /drivers/hwtracing/intel_th | |
parent | ba82664c134ef7ab97808f09a3c5e894b0a4900d (diff) |
intel_th: Add PTI output driver
Parallel Trace Interface (PTI) unit is a trace output device that sends
data over a PTI port.
The driver provides interfaces to configure bus width, bus clock divider
and mode. Tracing is enabled via output device's "active" attribute.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hwtracing/intel_th')
-rw-r--r-- | drivers/hwtracing/intel_th/Kconfig | 9 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/Makefile | 3 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/pti.c | 252 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/pti.h | 29 |
4 files changed, 293 insertions, 0 deletions
diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index d9a1217e27bf..b7a9073d968b 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig | |||
@@ -54,6 +54,15 @@ config INTEL_TH_MSU | |||
54 | 54 | ||
55 | Say Y here to enable MSU output device for Intel TH. | 55 | Say Y here to enable MSU output device for Intel TH. |
56 | 56 | ||
57 | config INTEL_TH_PTI | ||
58 | tristate "Intel(R) Trace Hub PTI output" | ||
59 | help | ||
60 | Parallel Trace Interface unit (PTI) is a trace output device | ||
61 | of Intel TH architecture that facilitates STP trace output via | ||
62 | a PTI port. | ||
63 | |||
64 | Say Y to enable PTI output of Intel TH data. | ||
65 | |||
57 | config INTEL_TH_DEBUG | 66 | config INTEL_TH_DEBUG |
58 | bool "Intel(R) Trace Hub debugging" | 67 | bool "Intel(R) Trace Hub debugging" |
59 | depends on DEBUG_FS | 68 | depends on DEBUG_FS |
diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile index 4636757df2a1..81d42fe918f7 100644 --- a/drivers/hwtracing/intel_th/Makefile +++ b/drivers/hwtracing/intel_th/Makefile | |||
@@ -13,3 +13,6 @@ intel_th_sth-y := sth.o | |||
13 | 13 | ||
14 | obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu.o | 14 | obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu.o |
15 | intel_th_msu-y := msu.o | 15 | intel_th_msu-y := msu.o |
16 | |||
17 | obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o | ||
18 | intel_th_pti-y := pti.o | ||
diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c new file mode 100644 index 000000000000..1e3bbc89825c --- /dev/null +++ b/drivers/hwtracing/intel_th/pti.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * Intel(R) Trace Hub PTI output driver | ||
3 | * | ||
4 | * Copyright (C) 2014-2015 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 | }; | ||
38 | |||
39 | /* map PTI widths to MODE settings of PTI_CTL register */ | ||
40 | static const unsigned int pti_mode[] = { | ||
41 | 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, | ||
42 | }; | ||
43 | |||
44 | static int pti_width_mode(unsigned int width) | ||
45 | { | ||
46 | int i; | ||
47 | |||
48 | for (i = 0; i < ARRAY_SIZE(pti_mode); i++) | ||
49 | if (pti_mode[i] == width) | ||
50 | return i; | ||
51 | |||
52 | return -EINVAL; | ||
53 | } | ||
54 | |||
55 | static ssize_t mode_show(struct device *dev, struct device_attribute *attr, | ||
56 | char *buf) | ||
57 | { | ||
58 | struct pti_device *pti = dev_get_drvdata(dev); | ||
59 | |||
60 | return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]); | ||
61 | } | ||
62 | |||
63 | static ssize_t mode_store(struct device *dev, struct device_attribute *attr, | ||
64 | const char *buf, size_t size) | ||
65 | { | ||
66 | struct pti_device *pti = dev_get_drvdata(dev); | ||
67 | unsigned long val; | ||
68 | int ret; | ||
69 | |||
70 | ret = kstrtoul(buf, 10, &val); | ||
71 | if (ret) | ||
72 | return ret; | ||
73 | |||
74 | ret = pti_width_mode(val); | ||
75 | if (ret < 0) | ||
76 | return ret; | ||
77 | |||
78 | pti->mode = ret; | ||
79 | |||
80 | return size; | ||
81 | } | ||
82 | |||
83 | static DEVICE_ATTR_RW(mode); | ||
84 | |||
85 | static ssize_t | ||
86 | freerunning_clock_show(struct device *dev, struct device_attribute *attr, | ||
87 | char *buf) | ||
88 | { | ||
89 | struct pti_device *pti = dev_get_drvdata(dev); | ||
90 | |||
91 | return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk); | ||
92 | } | ||
93 | |||
94 | static ssize_t | ||
95 | freerunning_clock_store(struct device *dev, struct device_attribute *attr, | ||
96 | const char *buf, size_t size) | ||
97 | { | ||
98 | struct pti_device *pti = dev_get_drvdata(dev); | ||
99 | unsigned long val; | ||
100 | int ret; | ||
101 | |||
102 | ret = kstrtoul(buf, 10, &val); | ||
103 | if (ret) | ||
104 | return ret; | ||
105 | |||
106 | pti->freeclk = !!val; | ||
107 | |||
108 | return size; | ||
109 | } | ||
110 | |||
111 | static DEVICE_ATTR_RW(freerunning_clock); | ||
112 | |||
113 | static ssize_t | ||
114 | clock_divider_show(struct device *dev, struct device_attribute *attr, | ||
115 | char *buf) | ||
116 | { | ||
117 | struct pti_device *pti = dev_get_drvdata(dev); | ||
118 | |||
119 | return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv); | ||
120 | } | ||
121 | |||
122 | static ssize_t | ||
123 | clock_divider_store(struct device *dev, struct device_attribute *attr, | ||
124 | const char *buf, size_t size) | ||
125 | { | ||
126 | struct pti_device *pti = dev_get_drvdata(dev); | ||
127 | unsigned long val; | ||
128 | int ret; | ||
129 | |||
130 | ret = kstrtoul(buf, 10, &val); | ||
131 | if (ret) | ||
132 | return ret; | ||
133 | |||
134 | if (!is_power_of_2(val) || val > 8 || !val) | ||
135 | return -EINVAL; | ||
136 | |||
137 | pti->clkdiv = val; | ||
138 | |||
139 | return size; | ||
140 | } | ||
141 | |||
142 | static DEVICE_ATTR_RW(clock_divider); | ||
143 | |||
144 | static struct attribute *pti_output_attrs[] = { | ||
145 | &dev_attr_mode.attr, | ||
146 | &dev_attr_freerunning_clock.attr, | ||
147 | &dev_attr_clock_divider.attr, | ||
148 | NULL, | ||
149 | }; | ||
150 | |||
151 | static struct attribute_group pti_output_group = { | ||
152 | .attrs = pti_output_attrs, | ||
153 | }; | ||
154 | |||
155 | static int intel_th_pti_activate(struct intel_th_device *thdev) | ||
156 | { | ||
157 | struct pti_device *pti = dev_get_drvdata(&thdev->dev); | ||
158 | u32 ctl = PTI_EN; | ||
159 | |||
160 | if (pti->patgen) | ||
161 | ctl |= pti->patgen << __ffs(PTI_PATGENMODE); | ||
162 | if (pti->freeclk) | ||
163 | ctl |= PTI_FCEN; | ||
164 | ctl |= pti->mode << __ffs(PTI_MODE); | ||
165 | ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); | ||
166 | |||
167 | iowrite32(ctl, pti->base + REG_PTI_CTL); | ||
168 | |||
169 | intel_th_trace_enable(thdev); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void intel_th_pti_deactivate(struct intel_th_device *thdev) | ||
175 | { | ||
176 | struct pti_device *pti = dev_get_drvdata(&thdev->dev); | ||
177 | |||
178 | intel_th_trace_disable(thdev); | ||
179 | |||
180 | iowrite32(0, pti->base + REG_PTI_CTL); | ||
181 | } | ||
182 | |||
183 | static void read_hw_config(struct pti_device *pti) | ||
184 | { | ||
185 | u32 ctl = ioread32(pti->base + REG_PTI_CTL); | ||
186 | |||
187 | pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE); | ||
188 | pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV); | ||
189 | pti->freeclk = !!(ctl & PTI_FCEN); | ||
190 | |||
191 | if (!pti_mode[pti->mode]) | ||
192 | pti->mode = pti_width_mode(4); | ||
193 | if (!pti->clkdiv) | ||
194 | pti->clkdiv = 1; | ||
195 | } | ||
196 | |||
197 | static int intel_th_pti_probe(struct intel_th_device *thdev) | ||
198 | { | ||
199 | struct device *dev = &thdev->dev; | ||
200 | struct resource *res; | ||
201 | struct pti_device *pti; | ||
202 | void __iomem *base; | ||
203 | int ret; | ||
204 | |||
205 | res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); | ||
206 | if (!res) | ||
207 | return -ENODEV; | ||
208 | |||
209 | base = devm_ioremap(dev, res->start, resource_size(res)); | ||
210 | if (IS_ERR(base)) | ||
211 | return PTR_ERR(base); | ||
212 | |||
213 | pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); | ||
214 | if (!pti) | ||
215 | return -ENOMEM; | ||
216 | |||
217 | pti->thdev = thdev; | ||
218 | pti->base = base; | ||
219 | |||
220 | read_hw_config(pti); | ||
221 | |||
222 | ret = sysfs_create_group(&dev->kobj, &pti_output_group); | ||
223 | if (ret) | ||
224 | return ret; | ||
225 | |||
226 | dev_set_drvdata(dev, pti); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static void intel_th_pti_remove(struct intel_th_device *thdev) | ||
232 | { | ||
233 | } | ||
234 | |||
235 | static struct intel_th_driver intel_th_pti_driver = { | ||
236 | .probe = intel_th_pti_probe, | ||
237 | .remove = intel_th_pti_remove, | ||
238 | .activate = intel_th_pti_activate, | ||
239 | .deactivate = intel_th_pti_deactivate, | ||
240 | .driver = { | ||
241 | .name = "pti", | ||
242 | .owner = THIS_MODULE, | ||
243 | }, | ||
244 | }; | ||
245 | |||
246 | module_driver(intel_th_pti_driver, | ||
247 | intel_th_driver_register, | ||
248 | intel_th_driver_unregister); | ||
249 | |||
250 | MODULE_LICENSE("GPL v2"); | ||
251 | MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); | ||
252 | MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); | ||
diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h new file mode 100644 index 000000000000..20883f5628cf --- /dev/null +++ b/drivers/hwtracing/intel_th/pti.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Intel(R) Trace Hub PTI output data structures | ||
3 | * | ||
4 | * Copyright (C) 2014-2015 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 | #ifndef __INTEL_TH_STH_H__ | ||
17 | #define __INTEL_TH_STH_H__ | ||
18 | |||
19 | enum { | ||
20 | REG_PTI_CTL = 0x1c00, | ||
21 | }; | ||
22 | |||
23 | #define PTI_EN BIT(0) | ||
24 | #define PTI_FCEN BIT(1) | ||
25 | #define PTI_MODE 0xf0 | ||
26 | #define PTI_CLKDIV 0x000f0000 | ||
27 | #define PTI_PATGENMODE 0x00f00000 | ||
28 | |||
29 | #endif /* __INTEL_TH_STH_H__ */ | ||