diff options
Diffstat (limited to 'arch/arm/kernel/etm.c')
-rw-r--r-- | arch/arm/kernel/etm.c | 473 |
1 files changed, 343 insertions, 130 deletions
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 1bec8b5f22f..496b8b84e45 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/types.h> | 16 | #include <linux/types.h> |
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/slab.h> | ||
18 | #include <linux/sysrq.h> | 19 | #include <linux/sysrq.h> |
19 | #include <linux/device.h> | 20 | #include <linux/device.h> |
20 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
@@ -36,26 +37,36 @@ MODULE_AUTHOR("Alexander Shishkin"); | |||
36 | struct tracectx { | 37 | struct tracectx { |
37 | unsigned int etb_bufsz; | 38 | unsigned int etb_bufsz; |
38 | void __iomem *etb_regs; | 39 | void __iomem *etb_regs; |
39 | void __iomem *etm_regs; | 40 | void __iomem **etm_regs; |
41 | int etm_regs_count; | ||
40 | unsigned long flags; | 42 | unsigned long flags; |
41 | int ncmppairs; | 43 | int ncmppairs; |
42 | int etm_portsz; | 44 | int etm_portsz; |
45 | u32 etb_fc; | ||
46 | unsigned long range_start; | ||
47 | unsigned long range_end; | ||
48 | unsigned long data_range_start; | ||
49 | unsigned long data_range_end; | ||
50 | bool dump_initial_etb; | ||
43 | struct device *dev; | 51 | struct device *dev; |
44 | struct clk *emu_clk; | 52 | struct clk *emu_clk; |
45 | struct mutex mutex; | 53 | struct mutex mutex; |
46 | }; | 54 | }; |
47 | 55 | ||
48 | static struct tracectx tracer; | 56 | static struct tracectx tracer = { |
57 | .range_start = (unsigned long)_stext, | ||
58 | .range_end = (unsigned long)_etext, | ||
59 | }; | ||
49 | 60 | ||
50 | static inline bool trace_isrunning(struct tracectx *t) | 61 | static inline bool trace_isrunning(struct tracectx *t) |
51 | { | 62 | { |
52 | return !!(t->flags & TRACER_RUNNING); | 63 | return !!(t->flags & TRACER_RUNNING); |
53 | } | 64 | } |
54 | 65 | ||
55 | static int etm_setup_address_range(struct tracectx *t, int n, | 66 | static int etm_setup_address_range(struct tracectx *t, int id, int n, |
56 | unsigned long start, unsigned long end, int exclude, int data) | 67 | unsigned long start, unsigned long end, int exclude, int data) |
57 | { | 68 | { |
58 | u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ | 69 | u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | |
59 | ETMAAT_NOVALCMP; | 70 | ETMAAT_NOVALCMP; |
60 | 71 | ||
61 | if (n < 1 || n > t->ncmppairs) | 72 | if (n < 1 || n > t->ncmppairs) |
@@ -71,95 +82,155 @@ static int etm_setup_address_range(struct tracectx *t, int n, | |||
71 | flags |= ETMAAT_IEXEC; | 82 | flags |= ETMAAT_IEXEC; |
72 | 83 | ||
73 | /* first comparator for the range */ | 84 | /* first comparator for the range */ |
74 | etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); | 85 | etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2)); |
75 | etm_writel(t, start, ETMR_COMP_VAL(n * 2)); | 86 | etm_writel(t, id, start, ETMR_COMP_VAL(n * 2)); |
76 | 87 | ||
77 | /* second comparator is right next to it */ | 88 | /* second comparator is right next to it */ |
78 | etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); | 89 | etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); |
79 | etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); | 90 | etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1)); |
80 | 91 | ||
81 | flags = exclude ? ETMTE_INCLEXCL : 0; | 92 | if (data) { |
82 | etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); | 93 | flags = exclude ? ETMVDC3_EXCLONLY : 0; |
94 | if (exclude) | ||
95 | n += 8; | ||
96 | etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3); | ||
97 | } else { | ||
98 | flags = exclude ? ETMTE_INCLEXCL : 0; | ||
99 | etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL); | ||
100 | } | ||
83 | 101 | ||
84 | return 0; | 102 | return 0; |
85 | } | 103 | } |
86 | 104 | ||
87 | static int trace_start(struct tracectx *t) | 105 | static int trace_start_etm(struct tracectx *t, int id) |
88 | { | 106 | { |
89 | u32 v; | 107 | u32 v; |
90 | unsigned long timeout = TRACER_TIMEOUT; | 108 | unsigned long timeout = TRACER_TIMEOUT; |
91 | 109 | ||
92 | etb_unlock(t); | ||
93 | |||
94 | etb_writel(t, 0, ETBR_FORMATTERCTRL); | ||
95 | etb_writel(t, 1, ETBR_CTRL); | ||
96 | |||
97 | etb_lock(t); | ||
98 | |||
99 | /* configure etm */ | ||
100 | v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); | 110 | v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); |
101 | 111 | ||
102 | if (t->flags & TRACER_CYCLE_ACC) | 112 | if (t->flags & TRACER_CYCLE_ACC) |
103 | v |= ETMCTRL_CYCLEACCURATE; | 113 | v |= ETMCTRL_CYCLEACCURATE; |
104 | 114 | ||
105 | etm_unlock(t); | 115 | if (t->flags & TRACER_TRACE_DATA) |
116 | v |= ETMCTRL_DATA_DO_ADDR; | ||
117 | |||
118 | etm_unlock(t, id); | ||
106 | 119 | ||
107 | etm_writel(t, v, ETMR_CTRL); | 120 | etm_writel(t, id, v, ETMR_CTRL); |
108 | 121 | ||
109 | while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) | 122 | while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) |
110 | ; | 123 | ; |
111 | if (!timeout) { | 124 | if (!timeout) { |
112 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); | 125 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); |
113 | etm_lock(t); | 126 | etm_lock(t, id); |
114 | return -EFAULT; | 127 | return -EFAULT; |
115 | } | 128 | } |
116 | 129 | ||
117 | etm_setup_address_range(t, 1, (unsigned long)_stext, | 130 | if (t->range_start || t->range_end) |
118 | (unsigned long)_etext, 0, 0); | 131 | etm_setup_address_range(t, id, 1, |
119 | etm_writel(t, 0, ETMR_TRACEENCTRL2); | 132 | t->range_start, t->range_end, 0, 0); |
120 | etm_writel(t, 0, ETMR_TRACESSCTRL); | 133 | else |
121 | etm_writel(t, 0x6f, ETMR_TRACEENEVT); | 134 | etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); |
135 | |||
136 | etm_writel(t, id, 0, ETMR_TRACEENCTRL2); | ||
137 | etm_writel(t, id, 0, ETMR_TRACESSCTRL); | ||
138 | etm_writel(t, id, 0x6f, ETMR_TRACEENEVT); | ||
139 | |||
140 | etm_writel(t, id, 0, ETMR_VIEWDATACTRL1); | ||
141 | etm_writel(t, id, 0, ETMR_VIEWDATACTRL2); | ||
142 | |||
143 | if (t->data_range_start || t->data_range_end) | ||
144 | etm_setup_address_range(t, id, 2, t->data_range_start, | ||
145 | t->data_range_end, 0, 1); | ||
146 | else | ||
147 | etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); | ||
148 | |||
149 | etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT); | ||
122 | 150 | ||
123 | v &= ~ETMCTRL_PROGRAM; | 151 | v &= ~ETMCTRL_PROGRAM; |
124 | v |= ETMCTRL_PORTSEL; | 152 | v |= ETMCTRL_PORTSEL; |
125 | 153 | ||
126 | etm_writel(t, v, ETMR_CTRL); | 154 | etm_writel(t, id, v, ETMR_CTRL); |
127 | 155 | ||
128 | timeout = TRACER_TIMEOUT; | 156 | timeout = TRACER_TIMEOUT; |
129 | while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) | 157 | while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) |
130 | ; | 158 | ; |
131 | if (!timeout) { | 159 | if (!timeout) { |
132 | dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); | 160 | dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); |
133 | etm_lock(t); | 161 | etm_lock(t, id); |
134 | return -EFAULT; | 162 | return -EFAULT; |
135 | } | 163 | } |
136 | 164 | ||
137 | etm_lock(t); | 165 | etm_lock(t, id); |
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int trace_start(struct tracectx *t) | ||
170 | { | ||
171 | int ret; | ||
172 | int id; | ||
173 | u32 etb_fc = t->etb_fc; | ||
174 | |||
175 | etb_unlock(t); | ||
176 | |||
177 | t->dump_initial_etb = false; | ||
178 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
179 | etb_writel(t, etb_fc, ETBR_FORMATTERCTRL); | ||
180 | etb_writel(t, 1, ETBR_CTRL); | ||
181 | |||
182 | etb_lock(t); | ||
183 | |||
184 | /* configure etm(s) */ | ||
185 | for (id = 0; id < t->etm_regs_count; id++) { | ||
186 | ret = trace_start_etm(t, id); | ||
187 | if (ret) | ||
188 | return ret; | ||
189 | } | ||
138 | 190 | ||
139 | t->flags |= TRACER_RUNNING; | 191 | t->flags |= TRACER_RUNNING; |
140 | 192 | ||
141 | return 0; | 193 | return 0; |
142 | } | 194 | } |
143 | 195 | ||
144 | static int trace_stop(struct tracectx *t) | 196 | static int trace_stop_etm(struct tracectx *t, int id) |
145 | { | 197 | { |
146 | unsigned long timeout = TRACER_TIMEOUT; | 198 | unsigned long timeout = TRACER_TIMEOUT; |
147 | 199 | ||
148 | etm_unlock(t); | 200 | etm_unlock(t, id); |
149 | 201 | ||
150 | etm_writel(t, 0x440, ETMR_CTRL); | 202 | etm_writel(t, id, 0x441, ETMR_CTRL); |
151 | while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) | 203 | while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) |
152 | ; | 204 | ; |
153 | if (!timeout) { | 205 | if (!timeout) { |
154 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); | 206 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); |
155 | etm_lock(t); | 207 | etm_lock(t, id); |
156 | return -EFAULT; | 208 | return -EFAULT; |
157 | } | 209 | } |
158 | 210 | ||
159 | etm_lock(t); | 211 | etm_lock(t, id); |
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static int trace_stop(struct tracectx *t) | ||
216 | { | ||
217 | int id; | ||
218 | int ret; | ||
219 | unsigned long timeout = TRACER_TIMEOUT; | ||
220 | u32 etb_fc = t->etb_fc; | ||
221 | |||
222 | for (id = 0; id < t->etm_regs_count; id++) { | ||
223 | ret = trace_stop_etm(t, id); | ||
224 | if (ret) | ||
225 | return ret; | ||
226 | } | ||
160 | 227 | ||
161 | etb_unlock(t); | 228 | etb_unlock(t); |
162 | etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); | 229 | if (etb_fc) { |
230 | etb_fc |= ETBFF_STOPFL; | ||
231 | etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL); | ||
232 | } | ||
233 | etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); | ||
163 | 234 | ||
164 | timeout = TRACER_TIMEOUT; | 235 | timeout = TRACER_TIMEOUT; |
165 | while (etb_readl(t, ETBR_FORMATTERCTRL) & | 236 | while (etb_readl(t, ETBR_FORMATTERCTRL) & |
@@ -184,24 +255,15 @@ static int trace_stop(struct tracectx *t) | |||
184 | static int etb_getdatalen(struct tracectx *t) | 255 | static int etb_getdatalen(struct tracectx *t) |
185 | { | 256 | { |
186 | u32 v; | 257 | u32 v; |
187 | int rp, wp; | 258 | int wp; |
188 | 259 | ||
189 | v = etb_readl(t, ETBR_STATUS); | 260 | v = etb_readl(t, ETBR_STATUS); |
190 | 261 | ||
191 | if (v & 1) | 262 | if (v & 1) |
192 | return t->etb_bufsz; | 263 | return t->etb_bufsz; |
193 | 264 | ||
194 | rp = etb_readl(t, ETBR_READADDR); | ||
195 | wp = etb_readl(t, ETBR_WRITEADDR); | 265 | wp = etb_readl(t, ETBR_WRITEADDR); |
196 | 266 | return wp; | |
197 | if (rp > wp) { | ||
198 | etb_writel(t, 0, ETBR_READADDR); | ||
199 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
200 | |||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | return wp - rp; | ||
205 | } | 267 | } |
206 | 268 | ||
207 | /* sysrq+v will always stop the running trace and leave it at that */ | 269 | /* sysrq+v will always stop the running trace and leave it at that */ |
@@ -234,21 +296,18 @@ static void etm_dump(void) | |||
234 | printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); | 296 | printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); |
235 | printk(KERN_INFO "\n--- ETB buffer end ---\n"); | 297 | printk(KERN_INFO "\n--- ETB buffer end ---\n"); |
236 | 298 | ||
237 | /* deassert the overflow bit */ | ||
238 | etb_writel(t, 1, ETBR_CTRL); | ||
239 | etb_writel(t, 0, ETBR_CTRL); | ||
240 | |||
241 | etb_writel(t, 0, ETBR_TRIGGERCOUNT); | ||
242 | etb_writel(t, 0, ETBR_READADDR); | ||
243 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
244 | |||
245 | etb_lock(t); | 299 | etb_lock(t); |
246 | } | 300 | } |
247 | 301 | ||
248 | static void sysrq_etm_dump(int key) | 302 | static void sysrq_etm_dump(int key) |
249 | { | 303 | { |
304 | if (!mutex_trylock(&tracer.mutex)) { | ||
305 | printk(KERN_INFO "Tracing hardware busy\n"); | ||
306 | return; | ||
307 | } | ||
250 | dev_dbg(tracer.dev, "Dumping ETB buffer\n"); | 308 | dev_dbg(tracer.dev, "Dumping ETB buffer\n"); |
251 | etm_dump(); | 309 | etm_dump(); |
310 | mutex_unlock(&tracer.mutex); | ||
252 | } | 311 | } |
253 | 312 | ||
254 | static struct sysrq_key_op sysrq_etm_op = { | 313 | static struct sysrq_key_op sysrq_etm_op = { |
@@ -275,6 +334,10 @@ static ssize_t etb_read(struct file *file, char __user *data, | |||
275 | struct tracectx *t = file->private_data; | 334 | struct tracectx *t = file->private_data; |
276 | u32 first = 0; | 335 | u32 first = 0; |
277 | u32 *buf; | 336 | u32 *buf; |
337 | int wpos; | ||
338 | int skip; | ||
339 | long wlength; | ||
340 | loff_t pos = *ppos; | ||
278 | 341 | ||
279 | mutex_lock(&t->mutex); | 342 | mutex_lock(&t->mutex); |
280 | 343 | ||
@@ -286,31 +349,39 @@ static ssize_t etb_read(struct file *file, char __user *data, | |||
286 | etb_unlock(t); | 349 | etb_unlock(t); |
287 | 350 | ||
288 | total = etb_getdatalen(t); | 351 | total = etb_getdatalen(t); |
352 | if (total == 0 && t->dump_initial_etb) | ||
353 | total = t->etb_bufsz; | ||
289 | if (total == t->etb_bufsz) | 354 | if (total == t->etb_bufsz) |
290 | first = etb_readl(t, ETBR_WRITEADDR); | 355 | first = etb_readl(t, ETBR_WRITEADDR); |
291 | 356 | ||
357 | if (pos > total * 4) { | ||
358 | skip = 0; | ||
359 | wpos = total; | ||
360 | } else { | ||
361 | skip = (int)pos % 4; | ||
362 | wpos = (int)pos / 4; | ||
363 | } | ||
364 | total -= wpos; | ||
365 | first = (first + wpos) % t->etb_bufsz; | ||
366 | |||
292 | etb_writel(t, first, ETBR_READADDR); | 367 | etb_writel(t, first, ETBR_READADDR); |
293 | 368 | ||
294 | length = min(total * 4, (int)len); | 369 | wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); |
295 | buf = vmalloc(length); | 370 | length = min(total * 4 - skip, (int)len); |
371 | buf = vmalloc(wlength * 4); | ||
296 | 372 | ||
297 | dev_dbg(t->dev, "ETB buffer length: %d\n", total); | 373 | dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n", |
374 | length, pos, wlength, first); | ||
375 | dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos); | ||
298 | dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); | 376 | dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); |
299 | for (i = 0; i < length / 4; i++) | 377 | for (i = 0; i < wlength; i++) |
300 | buf[i] = etb_readl(t, ETBR_READMEM); | 378 | buf[i] = etb_readl(t, ETBR_READMEM); |
301 | 379 | ||
302 | /* the only way to deassert overflow bit in ETB status is this */ | ||
303 | etb_writel(t, 1, ETBR_CTRL); | ||
304 | etb_writel(t, 0, ETBR_CTRL); | ||
305 | |||
306 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
307 | etb_writel(t, 0, ETBR_READADDR); | ||
308 | etb_writel(t, 0, ETBR_TRIGGERCOUNT); | ||
309 | |||
310 | etb_lock(t); | 380 | etb_lock(t); |
311 | 381 | ||
312 | length -= copy_to_user(data, buf, length); | 382 | length -= copy_to_user(data, (u8 *)buf + skip, length); |
313 | vfree(buf); | 383 | vfree(buf); |
384 | *ppos = pos + length; | ||
314 | 385 | ||
315 | out: | 386 | out: |
316 | mutex_unlock(&t->mutex); | 387 | mutex_unlock(&t->mutex); |
@@ -347,28 +418,17 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id | |||
347 | if (ret) | 418 | if (ret) |
348 | goto out; | 419 | goto out; |
349 | 420 | ||
421 | mutex_lock(&t->mutex); | ||
350 | t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); | 422 | t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); |
351 | if (!t->etb_regs) { | 423 | if (!t->etb_regs) { |
352 | ret = -ENOMEM; | 424 | ret = -ENOMEM; |
353 | goto out_release; | 425 | goto out_release; |
354 | } | 426 | } |
355 | 427 | ||
428 | t->dev = &dev->dev; | ||
429 | t->dump_initial_etb = true; | ||
356 | amba_set_drvdata(dev, t); | 430 | amba_set_drvdata(dev, t); |
357 | 431 | ||
358 | etb_miscdev.parent = &dev->dev; | ||
359 | |||
360 | ret = misc_register(&etb_miscdev); | ||
361 | if (ret) | ||
362 | goto out_unmap; | ||
363 | |||
364 | t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); | ||
365 | if (IS_ERR(t->emu_clk)) { | ||
366 | dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); | ||
367 | return -EFAULT; | ||
368 | } | ||
369 | |||
370 | clk_enable(t->emu_clk); | ||
371 | |||
372 | etb_unlock(t); | 432 | etb_unlock(t); |
373 | t->etb_bufsz = etb_readl(t, ETBR_DEPTH); | 433 | t->etb_bufsz = etb_readl(t, ETBR_DEPTH); |
374 | dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); | 434 | dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); |
@@ -377,6 +437,20 @@ static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id | |||
377 | etb_writel(t, 0, ETBR_CTRL); | 437 | etb_writel(t, 0, ETBR_CTRL); |
378 | etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); | 438 | etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); |
379 | etb_lock(t); | 439 | etb_lock(t); |
440 | mutex_unlock(&t->mutex); | ||
441 | |||
442 | etb_miscdev.parent = &dev->dev; | ||
443 | |||
444 | ret = misc_register(&etb_miscdev); | ||
445 | if (ret) | ||
446 | goto out_unmap; | ||
447 | |||
448 | /* Get optional clock. Currently used to select clock source on omap3 */ | ||
449 | t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); | ||
450 | if (IS_ERR(t->emu_clk)) | ||
451 | dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); | ||
452 | else | ||
453 | clk_enable(t->emu_clk); | ||
380 | 454 | ||
381 | dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); | 455 | dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); |
382 | 456 | ||
@@ -384,10 +458,13 @@ out: | |||
384 | return ret; | 458 | return ret; |
385 | 459 | ||
386 | out_unmap: | 460 | out_unmap: |
461 | mutex_lock(&t->mutex); | ||
387 | amba_set_drvdata(dev, NULL); | 462 | amba_set_drvdata(dev, NULL); |
388 | iounmap(t->etb_regs); | 463 | iounmap(t->etb_regs); |
464 | t->etb_regs = NULL; | ||
389 | 465 | ||
390 | out_release: | 466 | out_release: |
467 | mutex_unlock(&t->mutex); | ||
391 | amba_release_regions(dev); | 468 | amba_release_regions(dev); |
392 | 469 | ||
393 | return ret; | 470 | return ret; |
@@ -402,8 +479,10 @@ static int etb_remove(struct amba_device *dev) | |||
402 | iounmap(t->etb_regs); | 479 | iounmap(t->etb_regs); |
403 | t->etb_regs = NULL; | 480 | t->etb_regs = NULL; |
404 | 481 | ||
405 | clk_disable(t->emu_clk); | 482 | if (!IS_ERR(t->emu_clk)) { |
406 | clk_put(t->emu_clk); | 483 | clk_disable(t->emu_clk); |
484 | clk_put(t->emu_clk); | ||
485 | } | ||
407 | 486 | ||
408 | amba_release_regions(dev); | 487 | amba_release_regions(dev); |
409 | 488 | ||
@@ -447,7 +526,10 @@ static ssize_t trace_running_store(struct kobject *kobj, | |||
447 | return -EINVAL; | 526 | return -EINVAL; |
448 | 527 | ||
449 | mutex_lock(&tracer.mutex); | 528 | mutex_lock(&tracer.mutex); |
450 | ret = value ? trace_start(&tracer) : trace_stop(&tracer); | 529 | if (!tracer.etb_regs) |
530 | ret = -ENODEV; | ||
531 | else | ||
532 | ret = value ? trace_start(&tracer) : trace_stop(&tracer); | ||
451 | mutex_unlock(&tracer.mutex); | 533 | mutex_unlock(&tracer.mutex); |
452 | 534 | ||
453 | return ret ? : n; | 535 | return ret ? : n; |
@@ -462,36 +544,50 @@ static ssize_t trace_info_show(struct kobject *kobj, | |||
462 | { | 544 | { |
463 | u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; | 545 | u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; |
464 | int datalen; | 546 | int datalen; |
547 | int id; | ||
548 | int ret; | ||
465 | 549 | ||
466 | etb_unlock(&tracer); | 550 | mutex_lock(&tracer.mutex); |
467 | datalen = etb_getdatalen(&tracer); | 551 | if (tracer.etb_regs) { |
468 | etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); | 552 | etb_unlock(&tracer); |
469 | etb_ra = etb_readl(&tracer, ETBR_READADDR); | 553 | datalen = etb_getdatalen(&tracer); |
470 | etb_st = etb_readl(&tracer, ETBR_STATUS); | 554 | etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); |
471 | etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); | 555 | etb_ra = etb_readl(&tracer, ETBR_READADDR); |
472 | etb_lock(&tracer); | 556 | etb_st = etb_readl(&tracer, ETBR_STATUS); |
473 | 557 | etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); | |
474 | etm_unlock(&tracer); | 558 | etb_lock(&tracer); |
475 | etm_ctrl = etm_readl(&tracer, ETMR_CTRL); | 559 | } else { |
476 | etm_st = etm_readl(&tracer, ETMR_STATUS); | 560 | etb_wa = etb_ra = etb_st = etb_fc = ~0; |
477 | etm_lock(&tracer); | 561 | datalen = -1; |
562 | } | ||
478 | 563 | ||
479 | return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" | 564 | ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" |
480 | "ETBR_WRITEADDR:\t%08x\n" | 565 | "ETBR_WRITEADDR:\t%08x\n" |
481 | "ETBR_READADDR:\t%08x\n" | 566 | "ETBR_READADDR:\t%08x\n" |
482 | "ETBR_STATUS:\t%08x\n" | 567 | "ETBR_STATUS:\t%08x\n" |
483 | "ETBR_FORMATTERCTRL:\t%08x\n" | 568 | "ETBR_FORMATTERCTRL:\t%08x\n", |
484 | "ETMR_CTRL:\t%08x\n" | ||
485 | "ETMR_STATUS:\t%08x\n", | ||
486 | datalen, | 569 | datalen, |
487 | tracer.ncmppairs, | 570 | tracer.ncmppairs, |
488 | etb_wa, | 571 | etb_wa, |
489 | etb_ra, | 572 | etb_ra, |
490 | etb_st, | 573 | etb_st, |
491 | etb_fc, | 574 | etb_fc |
575 | ); | ||
576 | |||
577 | for (id = 0; id < tracer.etm_regs_count; id++) { | ||
578 | etm_unlock(&tracer, id); | ||
579 | etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL); | ||
580 | etm_st = etm_readl(&tracer, id, ETMR_STATUS); | ||
581 | etm_lock(&tracer, id); | ||
582 | ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n" | ||
583 | "ETMR_STATUS:\t%08x\n", | ||
492 | etm_ctrl, | 584 | etm_ctrl, |
493 | etm_st | 585 | etm_st |
494 | ); | 586 | ); |
587 | } | ||
588 | mutex_unlock(&tracer.mutex); | ||
589 | |||
590 | return ret; | ||
495 | } | 591 | } |
496 | 592 | ||
497 | static struct kobj_attribute trace_info_attr = | 593 | static struct kobj_attribute trace_info_attr = |
@@ -530,42 +626,121 @@ static ssize_t trace_mode_store(struct kobject *kobj, | |||
530 | static struct kobj_attribute trace_mode_attr = | 626 | static struct kobj_attribute trace_mode_attr = |
531 | __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); | 627 | __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); |
532 | 628 | ||
629 | static ssize_t trace_range_show(struct kobject *kobj, | ||
630 | struct kobj_attribute *attr, | ||
631 | char *buf) | ||
632 | { | ||
633 | return sprintf(buf, "%08lx %08lx\n", | ||
634 | tracer.range_start, tracer.range_end); | ||
635 | } | ||
636 | |||
637 | static ssize_t trace_range_store(struct kobject *kobj, | ||
638 | struct kobj_attribute *attr, | ||
639 | const char *buf, size_t n) | ||
640 | { | ||
641 | unsigned long range_start, range_end; | ||
642 | |||
643 | if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2) | ||
644 | return -EINVAL; | ||
645 | |||
646 | mutex_lock(&tracer.mutex); | ||
647 | tracer.range_start = range_start; | ||
648 | tracer.range_end = range_end; | ||
649 | mutex_unlock(&tracer.mutex); | ||
650 | |||
651 | return n; | ||
652 | } | ||
653 | |||
654 | |||
655 | static struct kobj_attribute trace_range_attr = | ||
656 | __ATTR(trace_range, 0644, trace_range_show, trace_range_store); | ||
657 | |||
658 | static ssize_t trace_data_range_show(struct kobject *kobj, | ||
659 | struct kobj_attribute *attr, | ||
660 | char *buf) | ||
661 | { | ||
662 | unsigned long range_start; | ||
663 | u64 range_end; | ||
664 | mutex_lock(&tracer.mutex); | ||
665 | range_start = tracer.data_range_start; | ||
666 | range_end = tracer.data_range_end; | ||
667 | if (!range_end && (tracer.flags & TRACER_TRACE_DATA)) | ||
668 | range_end = 0x100000000ULL; | ||
669 | mutex_unlock(&tracer.mutex); | ||
670 | return sprintf(buf, "%08lx %08llx\n", range_start, range_end); | ||
671 | } | ||
672 | |||
673 | static ssize_t trace_data_range_store(struct kobject *kobj, | ||
674 | struct kobj_attribute *attr, | ||
675 | const char *buf, size_t n) | ||
676 | { | ||
677 | unsigned long range_start; | ||
678 | u64 range_end; | ||
679 | |||
680 | if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2) | ||
681 | return -EINVAL; | ||
682 | |||
683 | mutex_lock(&tracer.mutex); | ||
684 | tracer.data_range_start = range_start; | ||
685 | tracer.data_range_end = (unsigned long)range_end; | ||
686 | if (range_end) | ||
687 | tracer.flags |= TRACER_TRACE_DATA; | ||
688 | else | ||
689 | tracer.flags &= ~TRACER_TRACE_DATA; | ||
690 | mutex_unlock(&tracer.mutex); | ||
691 | |||
692 | return n; | ||
693 | } | ||
694 | |||
695 | |||
696 | static struct kobj_attribute trace_data_range_attr = | ||
697 | __ATTR(trace_data_range, 0644, | ||
698 | trace_data_range_show, trace_data_range_store); | ||
699 | |||
533 | static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id) | 700 | static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id) |
534 | { | 701 | { |
535 | struct tracectx *t = &tracer; | 702 | struct tracectx *t = &tracer; |
536 | int ret = 0; | 703 | int ret = 0; |
704 | void __iomem **new_regs; | ||
705 | int new_count; | ||
537 | 706 | ||
538 | if (t->etm_regs) { | 707 | mutex_lock(&t->mutex); |
539 | dev_dbg(&dev->dev, "ETM already initialized\n"); | 708 | new_count = t->etm_regs_count + 1; |
540 | ret = -EBUSY; | 709 | new_regs = krealloc(t->etm_regs, |
710 | sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL); | ||
711 | |||
712 | if (!new_regs) { | ||
713 | dev_dbg(&dev->dev, "Failed to allocate ETM register array\n"); | ||
714 | ret = -ENOMEM; | ||
541 | goto out; | 715 | goto out; |
542 | } | 716 | } |
717 | t->etm_regs = new_regs; | ||
543 | 718 | ||
544 | ret = amba_request_regions(dev, NULL); | 719 | ret = amba_request_regions(dev, NULL); |
545 | if (ret) | 720 | if (ret) |
546 | goto out; | 721 | goto out; |
547 | 722 | ||
548 | t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); | 723 | t->etm_regs[t->etm_regs_count] = |
549 | if (!t->etm_regs) { | 724 | ioremap_nocache(dev->res.start, resource_size(&dev->res)); |
725 | if (!t->etm_regs[t->etm_regs_count]) { | ||
550 | ret = -ENOMEM; | 726 | ret = -ENOMEM; |
551 | goto out_release; | 727 | goto out_release; |
552 | } | 728 | } |
553 | 729 | ||
554 | amba_set_drvdata(dev, t); | 730 | amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); |
555 | 731 | ||
556 | mutex_init(&t->mutex); | 732 | t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; |
557 | t->dev = &dev->dev; | ||
558 | t->flags = TRACER_CYCLE_ACC; | ||
559 | t->etm_portsz = 1; | 733 | t->etm_portsz = 1; |
560 | 734 | ||
561 | etm_unlock(t); | 735 | etm_unlock(t, t->etm_regs_count); |
562 | (void)etm_readl(t, ETMMR_PDSR); | 736 | (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); |
563 | /* dummy first read */ | 737 | /* dummy first read */ |
564 | (void)etm_readl(&tracer, ETMMR_OSSRR); | 738 | (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); |
565 | 739 | ||
566 | t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; | 740 | t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf; |
567 | etm_writel(t, 0x440, ETMR_CTRL); | 741 | etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); |
568 | etm_lock(t); | 742 | etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); |
743 | etm_lock(t, t->etm_regs_count); | ||
569 | 744 | ||
570 | ret = sysfs_create_file(&dev->dev.kobj, | 745 | ret = sysfs_create_file(&dev->dev.kobj, |
571 | &trace_running_attr.attr); | 746 | &trace_running_attr.attr); |
@@ -581,36 +756,68 @@ static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id | |||
581 | if (ret) | 756 | if (ret) |
582 | dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); | 757 | dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); |
583 | 758 | ||
584 | dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); | 759 | ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr); |
760 | if (ret) | ||
761 | dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); | ||
762 | |||
763 | ret = sysfs_create_file(&dev->dev.kobj, &trace_data_range_attr.attr); | ||
764 | if (ret) | ||
765 | dev_dbg(&dev->dev, | ||
766 | "Failed to create trace_data_range in sysfs\n"); | ||
767 | |||
768 | dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n"); | ||
769 | |||
770 | /* Enable formatter if there are multiple trace sources */ | ||
771 | if (new_count > 1) | ||
772 | t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC; | ||
773 | |||
774 | t->etm_regs_count = new_count; | ||
585 | 775 | ||
586 | out: | 776 | out: |
777 | mutex_unlock(&t->mutex); | ||
587 | return ret; | 778 | return ret; |
588 | 779 | ||
589 | out_unmap: | 780 | out_unmap: |
590 | amba_set_drvdata(dev, NULL); | 781 | amba_set_drvdata(dev, NULL); |
591 | iounmap(t->etm_regs); | 782 | iounmap(t->etm_regs[t->etm_regs_count]); |
592 | 783 | ||
593 | out_release: | 784 | out_release: |
594 | amba_release_regions(dev); | 785 | amba_release_regions(dev); |
595 | 786 | ||
787 | mutex_unlock(&t->mutex); | ||
596 | return ret; | 788 | return ret; |
597 | } | 789 | } |
598 | 790 | ||
599 | static int etm_remove(struct amba_device *dev) | 791 | static int etm_remove(struct amba_device *dev) |
600 | { | 792 | { |
601 | struct tracectx *t = amba_get_drvdata(dev); | 793 | int i; |
794 | struct tracectx *t = &tracer; | ||
795 | void __iomem *etm_regs = amba_get_drvdata(dev); | ||
796 | |||
797 | sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); | ||
798 | sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); | ||
799 | sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); | ||
800 | sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); | ||
801 | sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); | ||
602 | 802 | ||
603 | amba_set_drvdata(dev, NULL); | 803 | amba_set_drvdata(dev, NULL); |
604 | 804 | ||
605 | iounmap(t->etm_regs); | 805 | mutex_lock(&t->mutex); |
606 | t->etm_regs = NULL; | 806 | for (i = 0; i < t->etm_regs_count; i++) |
807 | if (t->etm_regs[i] == etm_regs) | ||
808 | break; | ||
809 | for (; i < t->etm_regs_count - 1; i++) | ||
810 | t->etm_regs[i] = t->etm_regs[i + 1]; | ||
811 | t->etm_regs_count--; | ||
812 | if (!t->etm_regs_count) { | ||
813 | kfree(t->etm_regs); | ||
814 | t->etm_regs = NULL; | ||
815 | } | ||
816 | mutex_unlock(&t->mutex); | ||
607 | 817 | ||
818 | iounmap(etm_regs); | ||
608 | amba_release_regions(dev); | 819 | amba_release_regions(dev); |
609 | 820 | ||
610 | sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); | ||
611 | sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); | ||
612 | sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); | ||
613 | |||
614 | return 0; | 821 | return 0; |
615 | } | 822 | } |
616 | 823 | ||
@@ -619,6 +826,10 @@ static struct amba_id etm_ids[] = { | |||
619 | .id = 0x0003b921, | 826 | .id = 0x0003b921, |
620 | .mask = 0x0007ffff, | 827 | .mask = 0x0007ffff, |
621 | }, | 828 | }, |
829 | { | ||
830 | .id = 0x0003b950, | ||
831 | .mask = 0x0007ffff, | ||
832 | }, | ||
622 | { 0, 0 }, | 833 | { 0, 0 }, |
623 | }; | 834 | }; |
624 | 835 | ||
@@ -636,6 +847,8 @@ static int __init etm_init(void) | |||
636 | { | 847 | { |
637 | int retval; | 848 | int retval; |
638 | 849 | ||
850 | mutex_init(&tracer.mutex); | ||
851 | |||
639 | retval = amba_driver_register(&etb_driver); | 852 | retval = amba_driver_register(&etb_driver); |
640 | if (retval) { | 853 | if (retval) { |
641 | printk(KERN_ERR "Failed to register etb\n"); | 854 | printk(KERN_ERR "Failed to register etb\n"); |