| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * linux/arch/arm/kernel/etm.c | 
|  | 3 | * | 
|  | 4 | * Driver for ARM's Embedded Trace Macrocell and Embedded Trace Buffer. | 
|  | 5 | * | 
|  | 6 | * Copyright (C) 2009 Nokia Corporation. | 
|  | 7 | * Alexander Shishkin | 
|  | 8 | * | 
|  | 9 | * This program is free software; you can redistribute it and/or modify | 
|  | 10 | * it under the terms of the GNU General Public License version 2 as | 
|  | 11 | * published by the Free Software Foundation. | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/kernel.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/types.h> | 
|  | 17 | #include <linux/io.h> | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 18 | #include <linux/slab.h> | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 19 | #include <linux/sysrq.h> | 
|  | 20 | #include <linux/device.h> | 
|  | 21 | #include <linux/clk.h> | 
|  | 22 | #include <linux/amba/bus.h> | 
|  | 23 | #include <linux/fs.h> | 
|  | 24 | #include <linux/uaccess.h> | 
|  | 25 | #include <linux/miscdevice.h> | 
|  | 26 | #include <linux/vmalloc.h> | 
|  | 27 | #include <linux/mutex.h> | 
|  | 28 | #include <asm/hardware/coresight.h> | 
|  | 29 | #include <asm/sections.h> | 
|  | 30 |  | 
|  | 31 | MODULE_LICENSE("GPL"); | 
|  | 32 | MODULE_AUTHOR("Alexander Shishkin"); | 
|  | 33 |  | 
| Alexander Shishkin | 8234eae | 2010-08-04 11:22:43 +0100 | [diff] [blame] | 34 | /* | 
|  | 35 | * ETM tracer state | 
|  | 36 | */ | 
|  | 37 | struct tracectx { | 
|  | 38 | unsigned int	etb_bufsz; | 
|  | 39 | void __iomem	*etb_regs; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 40 | void __iomem	**etm_regs; | 
|  | 41 | int		etm_regs_count; | 
| Alexander Shishkin | 8234eae | 2010-08-04 11:22:43 +0100 | [diff] [blame] | 42 | unsigned long	flags; | 
|  | 43 | int		ncmppairs; | 
|  | 44 | int		etm_portsz; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 45 | u32		etb_fc; | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 46 | unsigned long	range_start; | 
|  | 47 | unsigned long	range_end; | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 48 | unsigned long	data_range_start; | 
|  | 49 | unsigned long	data_range_end; | 
| Arve Hjønnevåg | 97aba2e | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 50 | bool		dump_initial_etb; | 
| Alexander Shishkin | 8234eae | 2010-08-04 11:22:43 +0100 | [diff] [blame] | 51 | struct device	*dev; | 
|  | 52 | struct clk	*emu_clk; | 
|  | 53 | struct mutex	mutex; | 
|  | 54 | }; | 
|  | 55 |  | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 56 | static struct tracectx tracer = { | 
|  | 57 | .range_start = (unsigned long)_stext, | 
|  | 58 | .range_end = (unsigned long)_etext, | 
|  | 59 | }; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 60 |  | 
|  | 61 | static inline bool trace_isrunning(struct tracectx *t) | 
|  | 62 | { | 
|  | 63 | return !!(t->flags & TRACER_RUNNING); | 
|  | 64 | } | 
|  | 65 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 66 | static int etm_setup_address_range(struct tracectx *t, int id, int n, | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 67 | unsigned long start, unsigned long end, int exclude, int data) | 
|  | 68 | { | 
| Arve Hjønnevåg | ccd3dbc | 2011-01-28 23:33:11 -0800 | [diff] [blame] | 69 | u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 70 | ETMAAT_NOVALCMP; | 
|  | 71 |  | 
|  | 72 | if (n < 1 || n > t->ncmppairs) | 
|  | 73 | return -EINVAL; | 
|  | 74 |  | 
|  | 75 | /* comparators and ranges are numbered starting with 1 as opposed | 
|  | 76 | * to bits in a word */ | 
|  | 77 | n--; | 
|  | 78 |  | 
|  | 79 | if (data) | 
|  | 80 | flags |= ETMAAT_DLOADSTORE; | 
|  | 81 | else | 
|  | 82 | flags |= ETMAAT_IEXEC; | 
|  | 83 |  | 
|  | 84 | /* first comparator for the range */ | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 85 | etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2)); | 
|  | 86 | etm_writel(t, id, start, ETMR_COMP_VAL(n * 2)); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 87 |  | 
|  | 88 | /* second comparator is right next to it */ | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 89 | etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); | 
|  | 90 | etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1)); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 91 |  | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 92 | if (data) { | 
|  | 93 | flags = exclude ? ETMVDC3_EXCLONLY : 0; | 
|  | 94 | if (exclude) | 
|  | 95 | n += 8; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 96 | etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3); | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 97 | } else { | 
|  | 98 | flags = exclude ? ETMTE_INCLEXCL : 0; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 99 | etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL); | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 100 | } | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 101 |  | 
|  | 102 | return 0; | 
|  | 103 | } | 
|  | 104 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 105 | static int trace_start_etm(struct tracectx *t, int id) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 106 | { | 
|  | 107 | u32 v; | 
|  | 108 | unsigned long timeout = TRACER_TIMEOUT; | 
|  | 109 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 110 | v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); | 
|  | 111 |  | 
|  | 112 | if (t->flags & TRACER_CYCLE_ACC) | 
|  | 113 | v |= ETMCTRL_CYCLEACCURATE; | 
|  | 114 |  | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 115 | if (t->flags & TRACER_TRACE_DATA) | 
|  | 116 | v |= ETMCTRL_DATA_DO_ADDR; | 
|  | 117 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 118 | etm_unlock(t, id); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 119 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 120 | etm_writel(t, id, v, ETMR_CTRL); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 121 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 122 | while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 123 | ; | 
|  | 124 | if (!timeout) { | 
|  | 125 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 126 | etm_lock(t, id); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 127 | return -EFAULT; | 
|  | 128 | } | 
|  | 129 |  | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 130 | if (t->range_start || t->range_end) | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 131 | etm_setup_address_range(t, id, 1, | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 132 | t->range_start, t->range_end, 0, 0); | 
|  | 133 | else | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 134 | etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 135 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 136 | etm_writel(t, id, 0, ETMR_TRACEENCTRL2); | 
|  | 137 | etm_writel(t, id, 0, ETMR_TRACESSCTRL); | 
|  | 138 | etm_writel(t, id, 0x6f, ETMR_TRACEENEVT); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 139 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 140 | etm_writel(t, id, 0, ETMR_VIEWDATACTRL1); | 
|  | 141 | etm_writel(t, id, 0, ETMR_VIEWDATACTRL2); | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 142 |  | 
|  | 143 | if (t->data_range_start || t->data_range_end) | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 144 | etm_setup_address_range(t, id, 2, t->data_range_start, | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 145 | t->data_range_end, 0, 1); | 
|  | 146 | else | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 147 | etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 148 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 149 | etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT); | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 150 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 151 | v &= ~ETMCTRL_PROGRAM; | 
|  | 152 | v |= ETMCTRL_PORTSEL; | 
|  | 153 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 154 | etm_writel(t, id, v, ETMR_CTRL); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 155 |  | 
|  | 156 | timeout = TRACER_TIMEOUT; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 157 | while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 158 | ; | 
|  | 159 | if (!timeout) { | 
|  | 160 | dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 161 | etm_lock(t, id); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 162 | return -EFAULT; | 
|  | 163 | } | 
|  | 164 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 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 | } | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 190 |  | 
|  | 191 | t->flags |= TRACER_RUNNING; | 
|  | 192 |  | 
|  | 193 | return 0; | 
|  | 194 | } | 
|  | 195 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 196 | static int trace_stop_etm(struct tracectx *t, int id) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 197 | { | 
|  | 198 | unsigned long timeout = TRACER_TIMEOUT; | 
|  | 199 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 200 | etm_unlock(t, id); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 201 |  | 
| Arve Hjønnevåg | 38dfc33 | 2011-02-23 16:51:58 -0800 | [diff] [blame] | 202 | etm_writel(t, id, 0x441, ETMR_CTRL); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 203 | while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 204 | ; | 
|  | 205 | if (!timeout) { | 
|  | 206 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 207 | etm_lock(t, id); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 208 | return -EFAULT; | 
|  | 209 | } | 
|  | 210 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 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 | } | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 227 |  | 
|  | 228 | etb_unlock(t); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 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); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 234 |  | 
|  | 235 | timeout = TRACER_TIMEOUT; | 
|  | 236 | while (etb_readl(t, ETBR_FORMATTERCTRL) & | 
|  | 237 | ETBFF_MANUAL_FLUSH && --timeout) | 
|  | 238 | ; | 
|  | 239 | if (!timeout) { | 
|  | 240 | dev_dbg(t->dev, "Waiting for formatter flush to commence " | 
|  | 241 | "timed out\n"); | 
|  | 242 | etb_lock(t); | 
|  | 243 | return -EFAULT; | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | etb_writel(t, 0, ETBR_CTRL); | 
|  | 247 |  | 
|  | 248 | etb_lock(t); | 
|  | 249 |  | 
|  | 250 | t->flags &= ~TRACER_RUNNING; | 
|  | 251 |  | 
|  | 252 | return 0; | 
|  | 253 | } | 
|  | 254 |  | 
|  | 255 | static int etb_getdatalen(struct tracectx *t) | 
|  | 256 | { | 
|  | 257 | u32 v; | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 258 | int wp; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 259 |  | 
|  | 260 | v = etb_readl(t, ETBR_STATUS); | 
|  | 261 |  | 
|  | 262 | if (v & 1) | 
|  | 263 | return t->etb_bufsz; | 
|  | 264 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 265 | wp = etb_readl(t, ETBR_WRITEADDR); | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 266 | return wp; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 267 | } | 
|  | 268 |  | 
|  | 269 | /* sysrq+v will always stop the running trace and leave it at that */ | 
|  | 270 | static void etm_dump(void) | 
|  | 271 | { | 
|  | 272 | struct tracectx *t = &tracer; | 
|  | 273 | u32 first = 0; | 
|  | 274 | int length; | 
|  | 275 |  | 
|  | 276 | if (!t->etb_regs) { | 
|  | 277 | printk(KERN_INFO "No tracing hardware found\n"); | 
|  | 278 | return; | 
|  | 279 | } | 
|  | 280 |  | 
|  | 281 | if (trace_isrunning(t)) | 
|  | 282 | trace_stop(t); | 
|  | 283 |  | 
|  | 284 | etb_unlock(t); | 
|  | 285 |  | 
|  | 286 | length = etb_getdatalen(t); | 
|  | 287 |  | 
|  | 288 | if (length == t->etb_bufsz) | 
|  | 289 | first = etb_readl(t, ETBR_WRITEADDR); | 
|  | 290 |  | 
|  | 291 | etb_writel(t, first, ETBR_READADDR); | 
|  | 292 |  | 
|  | 293 | printk(KERN_INFO "Trace buffer contents length: %d\n", length); | 
|  | 294 | printk(KERN_INFO "--- ETB buffer begin ---\n"); | 
|  | 295 | for (; length; length--) | 
|  | 296 | printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); | 
|  | 297 | printk(KERN_INFO "\n--- ETB buffer end ---\n"); | 
|  | 298 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 299 | etb_lock(t); | 
|  | 300 | } | 
|  | 301 |  | 
| Dmitry Torokhov | 1495cc9 | 2010-08-17 21:15:46 -0700 | [diff] [blame] | 302 | static void sysrq_etm_dump(int key) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 303 | { | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 304 | if (!mutex_trylock(&tracer.mutex)) { | 
|  | 305 | printk(KERN_INFO "Tracing hardware busy\n"); | 
|  | 306 | return; | 
|  | 307 | } | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 308 | dev_dbg(tracer.dev, "Dumping ETB buffer\n"); | 
|  | 309 | etm_dump(); | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 310 | mutex_unlock(&tracer.mutex); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 311 | } | 
|  | 312 |  | 
|  | 313 | static struct sysrq_key_op sysrq_etm_op = { | 
|  | 314 | .handler = sysrq_etm_dump, | 
|  | 315 | .help_msg = "ETM buffer dump", | 
|  | 316 | .action_msg = "etm", | 
|  | 317 | }; | 
|  | 318 |  | 
|  | 319 | static int etb_open(struct inode *inode, struct file *file) | 
|  | 320 | { | 
|  | 321 | if (!tracer.etb_regs) | 
|  | 322 | return -ENODEV; | 
|  | 323 |  | 
|  | 324 | file->private_data = &tracer; | 
|  | 325 |  | 
|  | 326 | return nonseekable_open(inode, file); | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | static ssize_t etb_read(struct file *file, char __user *data, | 
|  | 330 | size_t len, loff_t *ppos) | 
|  | 331 | { | 
|  | 332 | int total, i; | 
|  | 333 | long length; | 
|  | 334 | struct tracectx *t = file->private_data; | 
|  | 335 | u32 first = 0; | 
|  | 336 | u32 *buf; | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 337 | int wpos; | 
|  | 338 | int skip; | 
|  | 339 | long wlength; | 
|  | 340 | loff_t pos = *ppos; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 341 |  | 
|  | 342 | mutex_lock(&t->mutex); | 
|  | 343 |  | 
|  | 344 | if (trace_isrunning(t)) { | 
|  | 345 | length = 0; | 
|  | 346 | goto out; | 
|  | 347 | } | 
|  | 348 |  | 
|  | 349 | etb_unlock(t); | 
|  | 350 |  | 
|  | 351 | total = etb_getdatalen(t); | 
| Arve Hjønnevåg | 97aba2e | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 352 | if (total == 0 && t->dump_initial_etb) | 
|  | 353 | total = t->etb_bufsz; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 354 | if (total == t->etb_bufsz) | 
|  | 355 | first = etb_readl(t, ETBR_WRITEADDR); | 
|  | 356 |  | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 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 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 367 | etb_writel(t, first, ETBR_READADDR); | 
|  | 368 |  | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 369 | wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); | 
|  | 370 | length = min(total * 4 - skip, (int)len); | 
|  | 371 | buf = vmalloc(wlength * 4); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 372 |  | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 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); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 376 | dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 377 | for (i = 0; i < wlength; i++) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 378 | buf[i] = etb_readl(t, ETBR_READMEM); | 
|  | 379 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 380 | etb_lock(t); | 
|  | 381 |  | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 382 | length -= copy_to_user(data, (u8 *)buf + skip, length); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 383 | vfree(buf); | 
| Arve Hjønnevåg | 5f388f3 | 2011-01-31 21:34:47 -0800 | [diff] [blame] | 384 | *ppos = pos + length; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 385 |  | 
|  | 386 | out: | 
|  | 387 | mutex_unlock(&t->mutex); | 
|  | 388 |  | 
|  | 389 | return length; | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | static int etb_release(struct inode *inode, struct file *file) | 
|  | 393 | { | 
|  | 394 | /* there's nothing to do here, actually */ | 
|  | 395 | return 0; | 
|  | 396 | } | 
|  | 397 |  | 
|  | 398 | static const struct file_operations etb_fops = { | 
|  | 399 | .owner = THIS_MODULE, | 
|  | 400 | .read = etb_read, | 
|  | 401 | .open = etb_open, | 
|  | 402 | .release = etb_release, | 
| Arnd Bergmann | 6038f37 | 2010-08-15 18:52:59 +0200 | [diff] [blame] | 403 | .llseek = no_llseek, | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 404 | }; | 
|  | 405 |  | 
|  | 406 | static struct miscdevice etb_miscdev = { | 
|  | 407 | .name = "tracebuf", | 
|  | 408 | .minor = 0, | 
|  | 409 | .fops = &etb_fops, | 
|  | 410 | }; | 
|  | 411 |  | 
| Ming Lei | 8e88069 | 2011-03-28 06:10:25 +0100 | [diff] [blame] | 412 | static int __devinit etb_probe(struct amba_device *dev, const struct amba_id *id) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 413 | { | 
|  | 414 | struct tracectx *t = &tracer; | 
|  | 415 | int ret = 0; | 
|  | 416 |  | 
|  | 417 | ret = amba_request_regions(dev, NULL); | 
|  | 418 | if (ret) | 
|  | 419 | goto out; | 
|  | 420 |  | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 421 | mutex_lock(&t->mutex); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 422 | t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); | 
|  | 423 | if (!t->etb_regs) { | 
|  | 424 | ret = -ENOMEM; | 
|  | 425 | goto out_release; | 
|  | 426 | } | 
|  | 427 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 428 | t->dev = &dev->dev; | 
| Arve Hjønnevåg | 97aba2e | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 429 | t->dump_initial_etb = true; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 430 | amba_set_drvdata(dev, t); | 
|  | 431 |  | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 432 | etb_unlock(t); | 
|  | 433 | t->etb_bufsz = etb_readl(t, ETBR_DEPTH); | 
|  | 434 | dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); | 
|  | 435 |  | 
|  | 436 | /* make sure trace capture is disabled */ | 
|  | 437 | etb_writel(t, 0, ETBR_CTRL); | 
|  | 438 | etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); | 
|  | 439 | etb_lock(t); | 
|  | 440 | mutex_unlock(&t->mutex); | 
|  | 441 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 442 | etb_miscdev.parent = &dev->dev; | 
|  | 443 |  | 
|  | 444 | ret = misc_register(&etb_miscdev); | 
|  | 445 | if (ret) | 
|  | 446 | goto out_unmap; | 
|  | 447 |  | 
| Arve Hjønnevåg | 875e2e1 | 2011-01-28 23:12:32 -0800 | [diff] [blame] | 448 | /* Get optional clock. Currently used to select clock source on omap3 */ | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 449 | t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); | 
| Arve Hjønnevåg | 875e2e1 | 2011-01-28 23:12:32 -0800 | [diff] [blame] | 450 | if (IS_ERR(t->emu_clk)) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 451 | dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); | 
| Arve Hjønnevåg | 875e2e1 | 2011-01-28 23:12:32 -0800 | [diff] [blame] | 452 | else | 
|  | 453 | clk_enable(t->emu_clk); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 454 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 455 | dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); | 
|  | 456 |  | 
|  | 457 | out: | 
|  | 458 | return ret; | 
|  | 459 |  | 
|  | 460 | out_unmap: | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 461 | mutex_lock(&t->mutex); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 462 | amba_set_drvdata(dev, NULL); | 
|  | 463 | iounmap(t->etb_regs); | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 464 | t->etb_regs = NULL; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 465 |  | 
|  | 466 | out_release: | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 467 | mutex_unlock(&t->mutex); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 468 | amba_release_regions(dev); | 
|  | 469 |  | 
|  | 470 | return ret; | 
|  | 471 | } | 
|  | 472 |  | 
|  | 473 | static int etb_remove(struct amba_device *dev) | 
|  | 474 | { | 
|  | 475 | struct tracectx *t = amba_get_drvdata(dev); | 
|  | 476 |  | 
|  | 477 | amba_set_drvdata(dev, NULL); | 
|  | 478 |  | 
|  | 479 | iounmap(t->etb_regs); | 
|  | 480 | t->etb_regs = NULL; | 
|  | 481 |  | 
| Arve Hjønnevåg | 875e2e1 | 2011-01-28 23:12:32 -0800 | [diff] [blame] | 482 | if (!IS_ERR(t->emu_clk)) { | 
|  | 483 | clk_disable(t->emu_clk); | 
|  | 484 | clk_put(t->emu_clk); | 
|  | 485 | } | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 486 |  | 
|  | 487 | amba_release_regions(dev); | 
|  | 488 |  | 
|  | 489 | return 0; | 
|  | 490 | } | 
|  | 491 |  | 
|  | 492 | static struct amba_id etb_ids[] = { | 
|  | 493 | { | 
|  | 494 | .id	= 0x0003b907, | 
|  | 495 | .mask	= 0x0007ffff, | 
|  | 496 | }, | 
|  | 497 | { 0, 0 }, | 
|  | 498 | }; | 
|  | 499 |  | 
|  | 500 | static struct amba_driver etb_driver = { | 
|  | 501 | .drv		= { | 
|  | 502 | .name	= "etb", | 
|  | 503 | .owner	= THIS_MODULE, | 
|  | 504 | }, | 
|  | 505 | .probe		= etb_probe, | 
|  | 506 | .remove		= etb_remove, | 
|  | 507 | .id_table	= etb_ids, | 
|  | 508 | }; | 
|  | 509 |  | 
|  | 510 | /* use a sysfs file "trace_running" to start/stop tracing */ | 
|  | 511 | static ssize_t trace_running_show(struct kobject *kobj, | 
|  | 512 | struct kobj_attribute *attr, | 
|  | 513 | char *buf) | 
|  | 514 | { | 
|  | 515 | return sprintf(buf, "%x\n", trace_isrunning(&tracer)); | 
|  | 516 | } | 
|  | 517 |  | 
|  | 518 | static ssize_t trace_running_store(struct kobject *kobj, | 
|  | 519 | struct kobj_attribute *attr, | 
|  | 520 | const char *buf, size_t n) | 
|  | 521 | { | 
|  | 522 | unsigned int value; | 
|  | 523 | int ret; | 
|  | 524 |  | 
|  | 525 | if (sscanf(buf, "%u", &value) != 1) | 
|  | 526 | return -EINVAL; | 
|  | 527 |  | 
|  | 528 | mutex_lock(&tracer.mutex); | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 529 | if (!tracer.etb_regs) | 
|  | 530 | ret = -ENODEV; | 
|  | 531 | else | 
|  | 532 | ret = value ? trace_start(&tracer) : trace_stop(&tracer); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 533 | mutex_unlock(&tracer.mutex); | 
|  | 534 |  | 
|  | 535 | return ret ? : n; | 
|  | 536 | } | 
|  | 537 |  | 
|  | 538 | static struct kobj_attribute trace_running_attr = | 
|  | 539 | __ATTR(trace_running, 0644, trace_running_show, trace_running_store); | 
|  | 540 |  | 
|  | 541 | static ssize_t trace_info_show(struct kobject *kobj, | 
|  | 542 | struct kobj_attribute *attr, | 
|  | 543 | char *buf) | 
|  | 544 | { | 
|  | 545 | u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; | 
|  | 546 | int datalen; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 547 | int id; | 
|  | 548 | int ret; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 549 |  | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 550 | mutex_lock(&tracer.mutex); | 
|  | 551 | if (tracer.etb_regs) { | 
|  | 552 | etb_unlock(&tracer); | 
|  | 553 | datalen = etb_getdatalen(&tracer); | 
|  | 554 | etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); | 
|  | 555 | etb_ra = etb_readl(&tracer, ETBR_READADDR); | 
|  | 556 | etb_st = etb_readl(&tracer, ETBR_STATUS); | 
|  | 557 | etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); | 
|  | 558 | etb_lock(&tracer); | 
|  | 559 | } else { | 
|  | 560 | etb_wa = etb_ra = etb_st = etb_fc = ~0; | 
|  | 561 | datalen = -1; | 
|  | 562 | } | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 563 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 564 | ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 565 | "ETBR_WRITEADDR:\t%08x\n" | 
|  | 566 | "ETBR_READADDR:\t%08x\n" | 
|  | 567 | "ETBR_STATUS:\t%08x\n" | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 568 | "ETBR_FORMATTERCTRL:\t%08x\n", | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 569 | datalen, | 
|  | 570 | tracer.ncmppairs, | 
|  | 571 | etb_wa, | 
|  | 572 | etb_ra, | 
|  | 573 | etb_st, | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 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", | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 584 | etm_ctrl, | 
|  | 585 | etm_st | 
|  | 586 | ); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 587 | } | 
|  | 588 | mutex_unlock(&tracer.mutex); | 
|  | 589 |  | 
|  | 590 | return ret; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 591 | } | 
|  | 592 |  | 
|  | 593 | static struct kobj_attribute trace_info_attr = | 
|  | 594 | __ATTR(trace_info, 0444, trace_info_show, NULL); | 
|  | 595 |  | 
|  | 596 | static ssize_t trace_mode_show(struct kobject *kobj, | 
|  | 597 | struct kobj_attribute *attr, | 
|  | 598 | char *buf) | 
|  | 599 | { | 
|  | 600 | return sprintf(buf, "%d %d\n", | 
|  | 601 | !!(tracer.flags & TRACER_CYCLE_ACC), | 
|  | 602 | tracer.etm_portsz); | 
|  | 603 | } | 
|  | 604 |  | 
|  | 605 | static ssize_t trace_mode_store(struct kobject *kobj, | 
|  | 606 | struct kobj_attribute *attr, | 
|  | 607 | const char *buf, size_t n) | 
|  | 608 | { | 
|  | 609 | unsigned int cycacc, portsz; | 
|  | 610 |  | 
|  | 611 | if (sscanf(buf, "%u %u", &cycacc, &portsz) != 2) | 
|  | 612 | return -EINVAL; | 
|  | 613 |  | 
|  | 614 | mutex_lock(&tracer.mutex); | 
|  | 615 | if (cycacc) | 
|  | 616 | tracer.flags |= TRACER_CYCLE_ACC; | 
|  | 617 | else | 
|  | 618 | tracer.flags &= ~TRACER_CYCLE_ACC; | 
|  | 619 |  | 
|  | 620 | tracer.etm_portsz = portsz & 0x0f; | 
|  | 621 | mutex_unlock(&tracer.mutex); | 
|  | 622 |  | 
|  | 623 | return n; | 
|  | 624 | } | 
|  | 625 |  | 
|  | 626 | static struct kobj_attribute trace_mode_attr = | 
|  | 627 | __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); | 
|  | 628 |  | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 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 |  | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 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 |  | 
| Ming Lei | 8e88069 | 2011-03-28 06:10:25 +0100 | [diff] [blame] | 700 | static int __devinit etm_probe(struct amba_device *dev, const struct amba_id *id) | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 701 | { | 
|  | 702 | struct tracectx *t = &tracer; | 
|  | 703 | int ret = 0; | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 704 | void __iomem **new_regs; | 
|  | 705 | int new_count; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 706 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 707 | mutex_lock(&t->mutex); | 
|  | 708 | new_count = t->etm_regs_count + 1; | 
|  | 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; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 715 | goto out; | 
|  | 716 | } | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 717 | t->etm_regs = new_regs; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 718 |  | 
|  | 719 | ret = amba_request_regions(dev, NULL); | 
|  | 720 | if (ret) | 
|  | 721 | goto out; | 
|  | 722 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 723 | t->etm_regs[t->etm_regs_count] = | 
|  | 724 | ioremap_nocache(dev->res.start, resource_size(&dev->res)); | 
|  | 725 | if (!t->etm_regs[t->etm_regs_count]) { | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 726 | ret = -ENOMEM; | 
|  | 727 | goto out_release; | 
|  | 728 | } | 
|  | 729 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 730 | amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 731 |  | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 732 | t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 733 | t->etm_portsz = 1; | 
|  | 734 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 735 | etm_unlock(t, t->etm_regs_count); | 
|  | 736 | (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); | 
| Alexander Shishkin | 988257c | 2010-08-04 11:27:33 +0100 | [diff] [blame] | 737 | /* dummy first read */ | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 738 | (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 739 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 740 | t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf; | 
| Arve Hjønnevåg | 38dfc33 | 2011-02-23 16:51:58 -0800 | [diff] [blame] | 741 | etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 742 | etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); | 
|  | 743 | etm_lock(t, t->etm_regs_count); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 744 |  | 
|  | 745 | ret = sysfs_create_file(&dev->dev.kobj, | 
|  | 746 | &trace_running_attr.attr); | 
|  | 747 | if (ret) | 
|  | 748 | goto out_unmap; | 
|  | 749 |  | 
|  | 750 | /* failing to create any of these two is not fatal */ | 
|  | 751 | ret = sysfs_create_file(&dev->dev.kobj, &trace_info_attr.attr); | 
|  | 752 | if (ret) | 
|  | 753 | dev_dbg(&dev->dev, "Failed to create trace_info in sysfs\n"); | 
|  | 754 |  | 
|  | 755 | ret = sysfs_create_file(&dev->dev.kobj, &trace_mode_attr.attr); | 
|  | 756 | if (ret) | 
|  | 757 | dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); | 
|  | 758 |  | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 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 |  | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 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 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 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; | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 775 |  | 
|  | 776 | out: | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 777 | mutex_unlock(&t->mutex); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 778 | return ret; | 
|  | 779 |  | 
|  | 780 | out_unmap: | 
|  | 781 | amba_set_drvdata(dev, NULL); | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 782 | iounmap(t->etm_regs[t->etm_regs_count]); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 783 |  | 
|  | 784 | out_release: | 
|  | 785 | amba_release_regions(dev); | 
|  | 786 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 787 | mutex_unlock(&t->mutex); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 788 | return ret; | 
|  | 789 | } | 
|  | 790 |  | 
|  | 791 | static int etm_remove(struct amba_device *dev) | 
|  | 792 | { | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 793 | int i; | 
|  | 794 | struct tracectx *t = &tracer; | 
|  | 795 | void __iomem	*etm_regs = amba_get_drvdata(dev); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 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); | 
| Arve Hjønnevåg | 5149597 | 2011-01-28 23:44:43 -0800 | [diff] [blame] | 800 | sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); | 
| Arve Hjønnevåg | 81e4312 | 2011-01-31 18:33:55 -0800 | [diff] [blame] | 801 | sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 802 |  | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 803 | amba_set_drvdata(dev, NULL); | 
|  | 804 |  | 
|  | 805 | mutex_lock(&t->mutex); | 
|  | 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); | 
|  | 817 |  | 
|  | 818 | iounmap(etm_regs); | 
|  | 819 | amba_release_regions(dev); | 
|  | 820 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 821 | return 0; | 
|  | 822 | } | 
|  | 823 |  | 
|  | 824 | static struct amba_id etm_ids[] = { | 
|  | 825 | { | 
|  | 826 | .id	= 0x0003b921, | 
|  | 827 | .mask	= 0x0007ffff, | 
|  | 828 | }, | 
| Arve Hjønnevåg | 628c494 | 2011-02-04 22:38:14 -0800 | [diff] [blame] | 829 | { | 
|  | 830 | .id	= 0x0003b950, | 
|  | 831 | .mask	= 0x0007ffff, | 
|  | 832 | }, | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 833 | { 0, 0 }, | 
|  | 834 | }; | 
|  | 835 |  | 
|  | 836 | static struct amba_driver etm_driver = { | 
|  | 837 | .drv		= { | 
|  | 838 | .name   = "etm", | 
|  | 839 | .owner  = THIS_MODULE, | 
|  | 840 | }, | 
|  | 841 | .probe		= etm_probe, | 
|  | 842 | .remove		= etm_remove, | 
|  | 843 | .id_table	= etm_ids, | 
|  | 844 | }; | 
|  | 845 |  | 
|  | 846 | static int __init etm_init(void) | 
|  | 847 | { | 
|  | 848 | int retval; | 
|  | 849 |  | 
| Arve Hjønnevåg | a321601 | 2011-02-14 21:09:51 -0800 | [diff] [blame] | 850 | mutex_init(&tracer.mutex); | 
|  | 851 |  | 
| Alexander Shishkin | c5d6c77 | 2009-12-01 14:00:51 +0100 | [diff] [blame] | 852 | retval = amba_driver_register(&etb_driver); | 
|  | 853 | if (retval) { | 
|  | 854 | printk(KERN_ERR "Failed to register etb\n"); | 
|  | 855 | return retval; | 
|  | 856 | } | 
|  | 857 |  | 
|  | 858 | retval = amba_driver_register(&etm_driver); | 
|  | 859 | if (retval) { | 
|  | 860 | amba_driver_unregister(&etb_driver); | 
|  | 861 | printk(KERN_ERR "Failed to probe etm\n"); | 
|  | 862 | return retval; | 
|  | 863 | } | 
|  | 864 |  | 
|  | 865 | /* not being able to install this handler is not fatal */ | 
|  | 866 | (void)register_sysrq_key('v', &sysrq_etm_op); | 
|  | 867 |  | 
|  | 868 | return 0; | 
|  | 869 | } | 
|  | 870 |  | 
|  | 871 | device_initcall(etm_init); | 
|  | 872 |  |