diff options
| -rw-r--r-- | arch/arm/Kconfig.debug | 8 | ||||
| -rw-r--r-- | arch/arm/include/asm/hardware/coresight.h | 165 | ||||
| -rw-r--r-- | arch/arm/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/etm.c | 641 |
4 files changed, 816 insertions, 0 deletions
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 1a6f70e52921..ff54c23d085e 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug | |||
| @@ -83,6 +83,14 @@ config DEBUG_ICEDCC | |||
| 83 | It does include a timeout to ensure that the system does not | 83 | It does include a timeout to ensure that the system does not |
| 84 | totally freeze when there is nothing connected to read. | 84 | totally freeze when there is nothing connected to read. |
| 85 | 85 | ||
| 86 | config OC_ETM | ||
| 87 | bool "On-chip ETM and ETB" | ||
| 88 | select ARM_AMBA | ||
| 89 | help | ||
| 90 | Enables the on-chip embedded trace macrocell and embedded trace | ||
| 91 | buffer driver that will allow you to collect traces of the | ||
| 92 | kernel code. | ||
| 93 | |||
| 86 | config DEBUG_DC21285_PORT | 94 | config DEBUG_DC21285_PORT |
| 87 | bool "Kernel low-level debugging messages via footbridge serial port" | 95 | bool "Kernel low-level debugging messages via footbridge serial port" |
| 88 | depends on DEBUG_LL && FOOTBRIDGE | 96 | depends on DEBUG_LL && FOOTBRIDGE |
diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h new file mode 100644 index 000000000000..f82b25d4f73e --- /dev/null +++ b/arch/arm/include/asm/hardware/coresight.h | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/include/asm/hardware/coresight.h | ||
| 3 | * | ||
| 4 | * CoreSight components' registers | ||
| 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 | #ifndef __ASM_HARDWARE_CORESIGHT_H | ||
| 15 | #define __ASM_HARDWARE_CORESIGHT_H | ||
| 16 | |||
| 17 | #define TRACER_ACCESSED_BIT 0 | ||
| 18 | #define TRACER_RUNNING_BIT 1 | ||
| 19 | #define TRACER_CYCLE_ACC_BIT 2 | ||
| 20 | #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) | ||
| 21 | #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) | ||
| 22 | #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) | ||
| 23 | |||
| 24 | struct tracectx { | ||
| 25 | unsigned int etb_bufsz; | ||
| 26 | void __iomem *etb_regs; | ||
| 27 | void __iomem *etm_regs; | ||
| 28 | unsigned long flags; | ||
| 29 | int ncmppairs; | ||
| 30 | int etm_portsz; | ||
| 31 | struct device *dev; | ||
| 32 | struct clk *emu_clk; | ||
| 33 | struct mutex mutex; | ||
| 34 | }; | ||
| 35 | |||
| 36 | #define TRACER_TIMEOUT 10000 | ||
| 37 | |||
| 38 | #define etm_writel(t, v, x) \ | ||
| 39 | (__raw_writel((v), (t)->etm_regs + (x))) | ||
| 40 | #define etm_readl(t, x) (__raw_readl((t)->etm_regs + (x))) | ||
| 41 | |||
| 42 | /* CoreSight Management Registers */ | ||
| 43 | #define CSMR_LOCKACCESS 0xfb0 | ||
| 44 | #define CSMR_LOCKSTATUS 0xfb4 | ||
| 45 | #define CSMR_AUTHSTATUS 0xfb8 | ||
| 46 | #define CSMR_DEVID 0xfc8 | ||
| 47 | #define CSMR_DEVTYPE 0xfcc | ||
| 48 | /* CoreSight Component Registers */ | ||
| 49 | #define CSCR_CLASS 0xff4 | ||
| 50 | |||
| 51 | #define CSCR_PRSR 0x314 | ||
| 52 | |||
| 53 | #define UNLOCK_MAGIC 0xc5acce55 | ||
| 54 | |||
| 55 | /* ETM control register, "ETM Architecture", 3.3.1 */ | ||
| 56 | #define ETMR_CTRL 0 | ||
| 57 | #define ETMCTRL_POWERDOWN 1 | ||
| 58 | #define ETMCTRL_PROGRAM (1 << 10) | ||
| 59 | #define ETMCTRL_PORTSEL (1 << 11) | ||
| 60 | #define ETMCTRL_DO_CONTEXTID (3 << 14) | ||
| 61 | #define ETMCTRL_PORTMASK1 (7 << 4) | ||
| 62 | #define ETMCTRL_PORTMASK2 (1 << 21) | ||
| 63 | #define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2) | ||
| 64 | #define ETMCTRL_PORTSIZE(x) ((((x) & 7) << 4) | (!!((x) & 8)) << 21) | ||
| 65 | #define ETMCTRL_DO_CPRT (1 << 1) | ||
| 66 | #define ETMCTRL_DATAMASK (3 << 2) | ||
| 67 | #define ETMCTRL_DATA_DO_DATA (1 << 2) | ||
| 68 | #define ETMCTRL_DATA_DO_ADDR (1 << 3) | ||
| 69 | #define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR) | ||
| 70 | #define ETMCTRL_BRANCH_OUTPUT (1 << 8) | ||
| 71 | #define ETMCTRL_CYCLEACCURATE (1 << 12) | ||
| 72 | |||
| 73 | /* ETM configuration code register */ | ||
| 74 | #define ETMR_CONFCODE (0x04) | ||
| 75 | |||
| 76 | /* ETM trace start/stop resource control register */ | ||
| 77 | #define ETMR_TRACESSCTRL (0x18) | ||
| 78 | |||
| 79 | /* ETM trigger event register */ | ||
| 80 | #define ETMR_TRIGEVT (0x08) | ||
| 81 | |||
| 82 | /* address access type register bits, "ETM architecture", | ||
| 83 | * table 3-27 */ | ||
| 84 | /* - access type */ | ||
| 85 | #define ETMAAT_IFETCH 0 | ||
| 86 | #define ETMAAT_IEXEC 1 | ||
| 87 | #define ETMAAT_IEXECPASS 2 | ||
| 88 | #define ETMAAT_IEXECFAIL 3 | ||
| 89 | #define ETMAAT_DLOADSTORE 4 | ||
| 90 | #define ETMAAT_DLOAD 5 | ||
| 91 | #define ETMAAT_DSTORE 6 | ||
| 92 | /* - comparison access size */ | ||
| 93 | #define ETMAAT_JAVA (0 << 3) | ||
| 94 | #define ETMAAT_THUMB (1 << 3) | ||
| 95 | #define ETMAAT_ARM (3 << 3) | ||
| 96 | /* - data value comparison control */ | ||
| 97 | #define ETMAAT_NOVALCMP (0 << 5) | ||
| 98 | #define ETMAAT_VALMATCH (1 << 5) | ||
| 99 | #define ETMAAT_VALNOMATCH (3 << 5) | ||
| 100 | /* - exact match */ | ||
| 101 | #define ETMAAT_EXACTMATCH (1 << 7) | ||
| 102 | /* - context id comparator control */ | ||
| 103 | #define ETMAAT_IGNCONTEXTID (0 << 8) | ||
| 104 | #define ETMAAT_VALUE1 (1 << 8) | ||
| 105 | #define ETMAAT_VALUE2 (2 << 8) | ||
| 106 | #define ETMAAT_VALUE3 (3 << 8) | ||
| 107 | /* - security level control */ | ||
| 108 | #define ETMAAT_IGNSECURITY (0 << 10) | ||
| 109 | #define ETMAAT_NSONLY (1 << 10) | ||
| 110 | #define ETMAAT_SONLY (2 << 10) | ||
| 111 | |||
| 112 | #define ETMR_COMP_VAL(x) (0x40 + (x) * 4) | ||
| 113 | #define ETMR_COMP_ACC_TYPE(x) (0x80 + (x) * 4) | ||
| 114 | |||
| 115 | /* ETM status register, "ETM Architecture", 3.3.2 */ | ||
| 116 | #define ETMR_STATUS (0x10) | ||
| 117 | #define ETMST_OVERFLOW (1 << 0) | ||
| 118 | #define ETMST_PROGBIT (1 << 1) | ||
| 119 | #define ETMST_STARTSTOP (1 << 2) | ||
| 120 | #define ETMST_TRIGGER (1 << 3) | ||
| 121 | |||
| 122 | #define etm_progbit(t) (etm_readl((t), ETMR_STATUS) & ETMST_PROGBIT) | ||
| 123 | #define etm_started(t) (etm_readl((t), ETMR_STATUS) & ETMST_STARTSTOP) | ||
| 124 | #define etm_triggered(t) (etm_readl((t), ETMR_STATUS) & ETMST_TRIGGER) | ||
| 125 | |||
| 126 | #define ETMR_TRACEENCTRL2 0x1c | ||
| 127 | #define ETMR_TRACEENCTRL 0x24 | ||
| 128 | #define ETMTE_INCLEXCL (1 << 24) | ||
| 129 | #define ETMR_TRACEENEVT 0x20 | ||
| 130 | #define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ | ||
| 131 | ETMCTRL_DATA_DO_ADDR | \ | ||
| 132 | ETMCTRL_BRANCH_OUTPUT | \ | ||
| 133 | ETMCTRL_DO_CONTEXTID) | ||
| 134 | |||
| 135 | /* ETB registers, "CoreSight Components TRM", 9.3 */ | ||
| 136 | #define ETBR_DEPTH 0x04 | ||
| 137 | #define ETBR_STATUS 0x0c | ||
| 138 | #define ETBR_READMEM 0x10 | ||
| 139 | #define ETBR_READADDR 0x14 | ||
| 140 | #define ETBR_WRITEADDR 0x18 | ||
| 141 | #define ETBR_TRIGGERCOUNT 0x1c | ||
| 142 | #define ETBR_CTRL 0x20 | ||
| 143 | #define ETBR_FORMATTERCTRL 0x304 | ||
| 144 | #define ETBFF_ENFTC 1 | ||
| 145 | #define ETBFF_ENFCONT (1 << 1) | ||
| 146 | #define ETBFF_FONFLIN (1 << 4) | ||
| 147 | #define ETBFF_MANUAL_FLUSH (1 << 6) | ||
| 148 | #define ETBFF_TRIGIN (1 << 8) | ||
| 149 | #define ETBFF_TRIGEVT (1 << 9) | ||
| 150 | #define ETBFF_TRIGFL (1 << 10) | ||
| 151 | |||
| 152 | #define etb_writel(t, v, x) \ | ||
| 153 | (__raw_writel((v), (t)->etb_regs + (x))) | ||
| 154 | #define etb_readl(t, x) (__raw_readl((t)->etb_regs + (x))) | ||
| 155 | |||
| 156 | #define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0) | ||
| 157 | #define etm_unlock(t) \ | ||
| 158 | do { etm_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0) | ||
| 159 | |||
| 160 | #define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0) | ||
| 161 | #define etb_unlock(t) \ | ||
| 162 | do { etb_writel((t), UNLOCK_MAGIC, CSMR_LOCKACCESS); } while (0) | ||
| 163 | |||
| 164 | #endif /* __ASM_HARDWARE_CORESIGHT_H */ | ||
| 165 | |||
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 79087dd6d869..e7ccf7e697ce 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
| @@ -17,6 +17,8 @@ obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \ | |||
| 17 | process.o ptrace.o return_address.o setup.o signal.o \ | 17 | process.o ptrace.o return_address.o setup.o signal.o \ |
| 18 | sys_arm.o stacktrace.o time.o traps.o | 18 | sys_arm.o stacktrace.o time.o traps.o |
| 19 | 19 | ||
| 20 | obj-$(CONFIG_OC_ETM) += etm.o | ||
| 21 | |||
| 20 | obj-$(CONFIG_ISA_DMA_API) += dma.o | 22 | obj-$(CONFIG_ISA_DMA_API) += dma.o |
| 21 | obj-$(CONFIG_ARCH_ACORN) += ecard.o | 23 | obj-$(CONFIG_ARCH_ACORN) += ecard.o |
| 22 | obj-$(CONFIG_FIQ) += fiq.o | 24 | obj-$(CONFIG_FIQ) += fiq.o |
diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c new file mode 100644 index 000000000000..827753966301 --- /dev/null +++ b/arch/arm/kernel/etm.c | |||
| @@ -0,0 +1,641 @@ | |||
| 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> | ||
| 18 | #include <linux/sysrq.h> | ||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/clk.h> | ||
| 21 | #include <linux/amba/bus.h> | ||
| 22 | #include <linux/fs.h> | ||
| 23 | #include <linux/uaccess.h> | ||
| 24 | #include <linux/miscdevice.h> | ||
| 25 | #include <linux/vmalloc.h> | ||
| 26 | #include <linux/mutex.h> | ||
| 27 | #include <asm/hardware/coresight.h> | ||
| 28 | #include <asm/sections.h> | ||
| 29 | |||
| 30 | MODULE_LICENSE("GPL"); | ||
| 31 | MODULE_AUTHOR("Alexander Shishkin"); | ||
| 32 | |||
| 33 | static struct tracectx tracer; | ||
| 34 | |||
| 35 | static inline bool trace_isrunning(struct tracectx *t) | ||
| 36 | { | ||
| 37 | return !!(t->flags & TRACER_RUNNING); | ||
| 38 | } | ||
| 39 | |||
| 40 | static int etm_setup_address_range(struct tracectx *t, int n, | ||
| 41 | unsigned long start, unsigned long end, int exclude, int data) | ||
| 42 | { | ||
| 43 | u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ | ||
| 44 | ETMAAT_NOVALCMP; | ||
| 45 | |||
| 46 | if (n < 1 || n > t->ncmppairs) | ||
| 47 | return -EINVAL; | ||
| 48 | |||
| 49 | /* comparators and ranges are numbered starting with 1 as opposed | ||
| 50 | * to bits in a word */ | ||
| 51 | n--; | ||
| 52 | |||
| 53 | if (data) | ||
| 54 | flags |= ETMAAT_DLOADSTORE; | ||
| 55 | else | ||
| 56 | flags |= ETMAAT_IEXEC; | ||
| 57 | |||
| 58 | /* first comparator for the range */ | ||
| 59 | etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); | ||
| 60 | etm_writel(t, start, ETMR_COMP_VAL(n * 2)); | ||
| 61 | |||
| 62 | /* second comparator is right next to it */ | ||
| 63 | etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); | ||
| 64 | etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); | ||
| 65 | |||
| 66 | flags = exclude ? ETMTE_INCLEXCL : 0; | ||
| 67 | etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int trace_start(struct tracectx *t) | ||
| 73 | { | ||
| 74 | u32 v; | ||
| 75 | unsigned long timeout = TRACER_TIMEOUT; | ||
| 76 | |||
| 77 | etb_unlock(t); | ||
| 78 | |||
| 79 | etb_writel(t, 0, ETBR_FORMATTERCTRL); | ||
| 80 | etb_writel(t, 1, ETBR_CTRL); | ||
| 81 | |||
| 82 | etb_lock(t); | ||
| 83 | |||
| 84 | /* configure etm */ | ||
| 85 | v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); | ||
| 86 | |||
| 87 | if (t->flags & TRACER_CYCLE_ACC) | ||
| 88 | v |= ETMCTRL_CYCLEACCURATE; | ||
| 89 | |||
| 90 | etm_unlock(t); | ||
| 91 | |||
| 92 | etm_writel(t, v, ETMR_CTRL); | ||
| 93 | |||
| 94 | while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) | ||
| 95 | ; | ||
| 96 | if (!timeout) { | ||
| 97 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); | ||
| 98 | etm_lock(t); | ||
| 99 | return -EFAULT; | ||
| 100 | } | ||
| 101 | |||
| 102 | etm_setup_address_range(t, 1, (unsigned long)_stext, | ||
| 103 | (unsigned long)_etext, 0, 0); | ||
| 104 | etm_writel(t, 0, ETMR_TRACEENCTRL2); | ||
| 105 | etm_writel(t, 0, ETMR_TRACESSCTRL); | ||
| 106 | etm_writel(t, 0x6f, ETMR_TRACEENEVT); | ||
| 107 | |||
| 108 | v &= ~ETMCTRL_PROGRAM; | ||
| 109 | v |= ETMCTRL_PORTSEL; | ||
| 110 | |||
| 111 | etm_writel(t, v, ETMR_CTRL); | ||
| 112 | |||
| 113 | timeout = TRACER_TIMEOUT; | ||
| 114 | while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) | ||
| 115 | ; | ||
| 116 | if (!timeout) { | ||
| 117 | dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); | ||
| 118 | etm_lock(t); | ||
| 119 | return -EFAULT; | ||
| 120 | } | ||
| 121 | |||
| 122 | etm_lock(t); | ||
| 123 | |||
| 124 | t->flags |= TRACER_RUNNING; | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int trace_stop(struct tracectx *t) | ||
| 130 | { | ||
| 131 | unsigned long timeout = TRACER_TIMEOUT; | ||
| 132 | |||
| 133 | etm_unlock(t); | ||
| 134 | |||
| 135 | etm_writel(t, 0x440, ETMR_CTRL); | ||
| 136 | while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) | ||
| 137 | ; | ||
| 138 | if (!timeout) { | ||
| 139 | dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); | ||
| 140 | etm_lock(t); | ||
| 141 | return -EFAULT; | ||
| 142 | } | ||
| 143 | |||
| 144 | etm_lock(t); | ||
| 145 | |||
| 146 | etb_unlock(t); | ||
| 147 | etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); | ||
| 148 | |||
| 149 | timeout = TRACER_TIMEOUT; | ||
| 150 | while (etb_readl(t, ETBR_FORMATTERCTRL) & | ||
| 151 | ETBFF_MANUAL_FLUSH && --timeout) | ||
| 152 | ; | ||
| 153 | if (!timeout) { | ||
| 154 | dev_dbg(t->dev, "Waiting for formatter flush to commence " | ||
| 155 | "timed out\n"); | ||
| 156 | etb_lock(t); | ||
| 157 | return -EFAULT; | ||
| 158 | } | ||
| 159 | |||
| 160 | etb_writel(t, 0, ETBR_CTRL); | ||
| 161 | |||
| 162 | etb_lock(t); | ||
| 163 | |||
| 164 | t->flags &= ~TRACER_RUNNING; | ||
| 165 | |||
| 166 | return 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | static int etb_getdatalen(struct tracectx *t) | ||
| 170 | { | ||
| 171 | u32 v; | ||
| 172 | int rp, wp; | ||
| 173 | |||
| 174 | v = etb_readl(t, ETBR_STATUS); | ||
| 175 | |||
| 176 | if (v & 1) | ||
| 177 | return t->etb_bufsz; | ||
| 178 | |||
| 179 | rp = etb_readl(t, ETBR_READADDR); | ||
| 180 | wp = etb_readl(t, ETBR_WRITEADDR); | ||
| 181 | |||
| 182 | if (rp > wp) { | ||
| 183 | etb_writel(t, 0, ETBR_READADDR); | ||
| 184 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
| 185 | |||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | return wp - rp; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* sysrq+v will always stop the running trace and leave it at that */ | ||
| 193 | static void etm_dump(void) | ||
| 194 | { | ||
| 195 | struct tracectx *t = &tracer; | ||
| 196 | u32 first = 0; | ||
| 197 | int length; | ||
| 198 | |||
| 199 | if (!t->etb_regs) { | ||
| 200 | printk(KERN_INFO "No tracing hardware found\n"); | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | |||
| 204 | if (trace_isrunning(t)) | ||
| 205 | trace_stop(t); | ||
| 206 | |||
| 207 | etb_unlock(t); | ||
| 208 | |||
| 209 | length = etb_getdatalen(t); | ||
| 210 | |||
| 211 | if (length == t->etb_bufsz) | ||
| 212 | first = etb_readl(t, ETBR_WRITEADDR); | ||
| 213 | |||
| 214 | etb_writel(t, first, ETBR_READADDR); | ||
| 215 | |||
| 216 | printk(KERN_INFO "Trace buffer contents length: %d\n", length); | ||
| 217 | printk(KERN_INFO "--- ETB buffer begin ---\n"); | ||
| 218 | for (; length; length--) | ||
| 219 | printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); | ||
| 220 | printk(KERN_INFO "\n--- ETB buffer end ---\n"); | ||
| 221 | |||
| 222 | /* deassert the overflow bit */ | ||
| 223 | etb_writel(t, 1, ETBR_CTRL); | ||
| 224 | etb_writel(t, 0, ETBR_CTRL); | ||
| 225 | |||
| 226 | etb_writel(t, 0, ETBR_TRIGGERCOUNT); | ||
| 227 | etb_writel(t, 0, ETBR_READADDR); | ||
| 228 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
| 229 | |||
| 230 | etb_lock(t); | ||
| 231 | } | ||
| 232 | |||
| 233 | static void sysrq_etm_dump(int key, struct tty_struct *tty) | ||
| 234 | { | ||
| 235 | dev_dbg(tracer.dev, "Dumping ETB buffer\n"); | ||
| 236 | etm_dump(); | ||
| 237 | } | ||
| 238 | |||
| 239 | static struct sysrq_key_op sysrq_etm_op = { | ||
| 240 | .handler = sysrq_etm_dump, | ||
| 241 | .help_msg = "ETM buffer dump", | ||
| 242 | .action_msg = "etm", | ||
| 243 | }; | ||
| 244 | |||
| 245 | static int etb_open(struct inode *inode, struct file *file) | ||
| 246 | { | ||
| 247 | if (!tracer.etb_regs) | ||
| 248 | return -ENODEV; | ||
| 249 | |||
| 250 | file->private_data = &tracer; | ||
| 251 | |||
| 252 | return nonseekable_open(inode, file); | ||
| 253 | } | ||
| 254 | |||
| 255 | static ssize_t etb_read(struct file *file, char __user *data, | ||
| 256 | size_t len, loff_t *ppos) | ||
| 257 | { | ||
| 258 | int total, i; | ||
| 259 | long length; | ||
| 260 | struct tracectx *t = file->private_data; | ||
| 261 | u32 first = 0; | ||
| 262 | u32 *buf; | ||
| 263 | |||
| 264 | mutex_lock(&t->mutex); | ||
| 265 | |||
| 266 | if (trace_isrunning(t)) { | ||
| 267 | length = 0; | ||
| 268 | goto out; | ||
| 269 | } | ||
| 270 | |||
| 271 | etb_unlock(t); | ||
| 272 | |||
| 273 | total = etb_getdatalen(t); | ||
| 274 | if (total == t->etb_bufsz) | ||
| 275 | first = etb_readl(t, ETBR_WRITEADDR); | ||
| 276 | |||
| 277 | etb_writel(t, first, ETBR_READADDR); | ||
| 278 | |||
| 279 | length = min(total * 4, (int)len); | ||
| 280 | buf = vmalloc(length); | ||
| 281 | |||
| 282 | dev_dbg(t->dev, "ETB buffer length: %d\n", total); | ||
| 283 | dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); | ||
| 284 | for (i = 0; i < length / 4; i++) | ||
| 285 | buf[i] = etb_readl(t, ETBR_READMEM); | ||
| 286 | |||
| 287 | /* the only way to deassert overflow bit in ETB status is this */ | ||
| 288 | etb_writel(t, 1, ETBR_CTRL); | ||
| 289 | etb_writel(t, 0, ETBR_CTRL); | ||
| 290 | |||
| 291 | etb_writel(t, 0, ETBR_WRITEADDR); | ||
| 292 | etb_writel(t, 0, ETBR_READADDR); | ||
| 293 | etb_writel(t, 0, ETBR_TRIGGERCOUNT); | ||
| 294 | |||
| 295 | etb_lock(t); | ||
| 296 | |||
| 297 | length -= copy_to_user(data, buf, length); | ||
| 298 | vfree(buf); | ||
| 299 | |||
| 300 | out: | ||
| 301 | mutex_unlock(&t->mutex); | ||
| 302 | |||
| 303 | return length; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int etb_release(struct inode *inode, struct file *file) | ||
| 307 | { | ||
| 308 | /* there's nothing to do here, actually */ | ||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | static const struct file_operations etb_fops = { | ||
| 313 | .owner = THIS_MODULE, | ||
| 314 | .read = etb_read, | ||
| 315 | .open = etb_open, | ||
| 316 | .release = etb_release, | ||
| 317 | }; | ||
| 318 | |||
| 319 | static struct miscdevice etb_miscdev = { | ||
| 320 | .name = "tracebuf", | ||
| 321 | .minor = 0, | ||
| 322 | .fops = &etb_fops, | ||
| 323 | }; | ||
| 324 | |||
| 325 | static int __init etb_probe(struct amba_device *dev, struct amba_id *id) | ||
| 326 | { | ||
| 327 | struct tracectx *t = &tracer; | ||
| 328 | int ret = 0; | ||
| 329 | |||
| 330 | ret = amba_request_regions(dev, NULL); | ||
| 331 | if (ret) | ||
| 332 | goto out; | ||
| 333 | |||
| 334 | t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); | ||
| 335 | if (!t->etb_regs) { | ||
| 336 | ret = -ENOMEM; | ||
| 337 | goto out_release; | ||
| 338 | } | ||
| 339 | |||
| 340 | amba_set_drvdata(dev, t); | ||
| 341 | |||
| 342 | etb_miscdev.parent = &dev->dev; | ||
| 343 | |||
| 344 | ret = misc_register(&etb_miscdev); | ||
| 345 | if (ret) | ||
| 346 | goto out_unmap; | ||
| 347 | |||
| 348 | t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); | ||
| 349 | if (IS_ERR(t->emu_clk)) { | ||
| 350 | dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); | ||
| 351 | return -EFAULT; | ||
| 352 | } | ||
| 353 | |||
| 354 | clk_enable(t->emu_clk); | ||
| 355 | |||
| 356 | etb_unlock(t); | ||
| 357 | t->etb_bufsz = etb_readl(t, ETBR_DEPTH); | ||
| 358 | dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); | ||
| 359 | |||
| 360 | /* make sure trace capture is disabled */ | ||
| 361 | etb_writel(t, 0, ETBR_CTRL); | ||
| 362 | etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); | ||
| 363 | etb_lock(t); | ||
| 364 | |||
| 365 | dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); | ||
| 366 | |||
| 367 | out: | ||
| 368 | return ret; | ||
| 369 | |||
| 370 | out_unmap: | ||
| 371 | amba_set_drvdata(dev, NULL); | ||
| 372 | iounmap(t->etb_regs); | ||
| 373 | |||
| 374 | out_release: | ||
| 375 | amba_release_regions(dev); | ||
| 376 | |||
| 377 | return ret; | ||
| 378 | } | ||
| 379 | |||
| 380 | static int etb_remove(struct amba_device *dev) | ||
| 381 | { | ||
| 382 | struct tracectx *t = amba_get_drvdata(dev); | ||
| 383 | |||
| 384 | amba_set_drvdata(dev, NULL); | ||
| 385 | |||
| 386 | iounmap(t->etb_regs); | ||
| 387 | t->etb_regs = NULL; | ||
| 388 | |||
| 389 | clk_disable(t->emu_clk); | ||
| 390 | clk_put(t->emu_clk); | ||
| 391 | |||
| 392 | amba_release_regions(dev); | ||
| 393 | |||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | static struct amba_id etb_ids[] = { | ||
| 398 | { | ||
| 399 | .id = 0x0003b907, | ||
| 400 | .mask = 0x0007ffff, | ||
| 401 | }, | ||
| 402 | { 0, 0 }, | ||
| 403 | }; | ||
| 404 | |||
| 405 | static struct amba_driver etb_driver = { | ||
| 406 | .drv = { | ||
| 407 | .name = "etb", | ||
| 408 | .owner = THIS_MODULE, | ||
| 409 | }, | ||
| 410 | .probe = etb_probe, | ||
| 411 | .remove = etb_remove, | ||
| 412 | .id_table = etb_ids, | ||
| 413 | }; | ||
| 414 | |||
| 415 | /* use a sysfs file "trace_running" to start/stop tracing */ | ||
| 416 | static ssize_t trace_running_show(struct kobject *kobj, | ||
| 417 | struct kobj_attribute *attr, | ||
| 418 | char *buf) | ||
| 419 | { | ||
| 420 | return sprintf(buf, "%x\n", trace_isrunning(&tracer)); | ||
| 421 | } | ||
| 422 | |||
| 423 | static ssize_t trace_running_store(struct kobject *kobj, | ||
| 424 | struct kobj_attribute *attr, | ||
| 425 | const char *buf, size_t n) | ||
| 426 | { | ||
| 427 | unsigned int value; | ||
| 428 | int ret; | ||
| 429 | |||
| 430 | if (sscanf(buf, "%u", &value) != 1) | ||
| 431 | return -EINVAL; | ||
| 432 | |||
| 433 | mutex_lock(&tracer.mutex); | ||
| 434 | ret = value ? trace_start(&tracer) : trace_stop(&tracer); | ||
| 435 | mutex_unlock(&tracer.mutex); | ||
| 436 | |||
| 437 | return ret ? : n; | ||
| 438 | } | ||
| 439 | |||
| 440 | static struct kobj_attribute trace_running_attr = | ||
| 441 | __ATTR(trace_running, 0644, trace_running_show, trace_running_store); | ||
| 442 | |||
| 443 | static ssize_t trace_info_show(struct kobject *kobj, | ||
| 444 | struct kobj_attribute *attr, | ||
| 445 | char *buf) | ||
| 446 | { | ||
| 447 | u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; | ||
| 448 | int datalen; | ||
| 449 | |||
| 450 | etb_unlock(&tracer); | ||
| 451 | datalen = etb_getdatalen(&tracer); | ||
| 452 | etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); | ||
| 453 | etb_ra = etb_readl(&tracer, ETBR_READADDR); | ||
| 454 | etb_st = etb_readl(&tracer, ETBR_STATUS); | ||
| 455 | etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); | ||
| 456 | etb_lock(&tracer); | ||
| 457 | |||
| 458 | etm_unlock(&tracer); | ||
| 459 | etm_ctrl = etm_readl(&tracer, ETMR_CTRL); | ||
| 460 | etm_st = etm_readl(&tracer, ETMR_STATUS); | ||
| 461 | etm_lock(&tracer); | ||
| 462 | |||
| 463 | return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" | ||
| 464 | "ETBR_WRITEADDR:\t%08x\n" | ||
| 465 | "ETBR_READADDR:\t%08x\n" | ||
| 466 | "ETBR_STATUS:\t%08x\n" | ||
| 467 | "ETBR_FORMATTERCTRL:\t%08x\n" | ||
| 468 | "ETMR_CTRL:\t%08x\n" | ||
| 469 | "ETMR_STATUS:\t%08x\n", | ||
| 470 | datalen, | ||
| 471 | tracer.ncmppairs, | ||
| 472 | etb_wa, | ||
| 473 | etb_ra, | ||
| 474 | etb_st, | ||
| 475 | etb_fc, | ||
| 476 | etm_ctrl, | ||
| 477 | etm_st | ||
| 478 | ); | ||
| 479 | } | ||
| 480 | |||
| 481 | static struct kobj_attribute trace_info_attr = | ||
| 482 | __ATTR(trace_info, 0444, trace_info_show, NULL); | ||
| 483 | |||
| 484 | static ssize_t trace_mode_show(struct kobject *kobj, | ||
| 485 | struct kobj_attribute *attr, | ||
| 486 | char *buf) | ||
| 487 | { | ||
| 488 | return sprintf(buf, "%d %d\n", | ||
| 489 | !!(tracer.flags & TRACER_CYCLE_ACC), | ||
| 490 | tracer.etm_portsz); | ||
| 491 | } | ||
| 492 | |||
| 493 | static ssize_t trace_mode_store(struct kobject *kobj, | ||
| 494 | struct kobj_attribute *attr, | ||
| 495 | const char *buf, size_t n) | ||
| 496 | { | ||
| 497 | unsigned int cycacc, portsz; | ||
| 498 | |||
| 499 | if (sscanf(buf, "%u %u", &cycacc, &portsz) != 2) | ||
| 500 | return -EINVAL; | ||
| 501 | |||
| 502 | mutex_lock(&tracer.mutex); | ||
| 503 | if (cycacc) | ||
| 504 | tracer.flags |= TRACER_CYCLE_ACC; | ||
| 505 | else | ||
| 506 | tracer.flags &= ~TRACER_CYCLE_ACC; | ||
| 507 | |||
| 508 | tracer.etm_portsz = portsz & 0x0f; | ||
| 509 | mutex_unlock(&tracer.mutex); | ||
| 510 | |||
| 511 | return n; | ||
| 512 | } | ||
| 513 | |||
| 514 | static struct kobj_attribute trace_mode_attr = | ||
| 515 | __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); | ||
| 516 | |||
| 517 | static int __init etm_probe(struct amba_device *dev, struct amba_id *id) | ||
| 518 | { | ||
| 519 | struct tracectx *t = &tracer; | ||
| 520 | int ret = 0; | ||
| 521 | |||
| 522 | if (t->etm_regs) { | ||
| 523 | dev_dbg(&dev->dev, "ETM already initialized\n"); | ||
| 524 | ret = -EBUSY; | ||
| 525 | goto out; | ||
| 526 | } | ||
| 527 | |||
| 528 | ret = amba_request_regions(dev, NULL); | ||
| 529 | if (ret) | ||
| 530 | goto out; | ||
| 531 | |||
| 532 | t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); | ||
| 533 | if (!t->etm_regs) { | ||
| 534 | ret = -ENOMEM; | ||
| 535 | goto out_release; | ||
| 536 | } | ||
| 537 | |||
| 538 | amba_set_drvdata(dev, t); | ||
| 539 | |||
| 540 | mutex_init(&t->mutex); | ||
| 541 | t->dev = &dev->dev; | ||
| 542 | t->flags = TRACER_CYCLE_ACC; | ||
| 543 | t->etm_portsz = 1; | ||
| 544 | |||
| 545 | etm_unlock(t); | ||
| 546 | ret = etm_readl(t, CSCR_PRSR); | ||
| 547 | |||
| 548 | t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; | ||
| 549 | etm_writel(t, 0x440, ETMR_CTRL); | ||
| 550 | etm_lock(t); | ||
| 551 | |||
| 552 | ret = sysfs_create_file(&dev->dev.kobj, | ||
| 553 | &trace_running_attr.attr); | ||
| 554 | if (ret) | ||
| 555 | goto out_unmap; | ||
| 556 | |||
| 557 | /* failing to create any of these two is not fatal */ | ||
| 558 | ret = sysfs_create_file(&dev->dev.kobj, &trace_info_attr.attr); | ||
| 559 | if (ret) | ||
| 560 | dev_dbg(&dev->dev, "Failed to create trace_info in sysfs\n"); | ||
| 561 | |||
| 562 | ret = sysfs_create_file(&dev->dev.kobj, &trace_mode_attr.attr); | ||
| 563 | if (ret) | ||
| 564 | dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); | ||
| 565 | |||
| 566 | dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); | ||
| 567 | |||
| 568 | out: | ||
| 569 | return ret; | ||
| 570 | |||
| 571 | out_unmap: | ||
| 572 | amba_set_drvdata(dev, NULL); | ||
| 573 | iounmap(t->etm_regs); | ||
| 574 | |||
| 575 | out_release: | ||
| 576 | amba_release_regions(dev); | ||
| 577 | |||
| 578 | return ret; | ||
| 579 | } | ||
| 580 | |||
| 581 | static int etm_remove(struct amba_device *dev) | ||
| 582 | { | ||
| 583 | struct tracectx *t = amba_get_drvdata(dev); | ||
| 584 | |||
| 585 | amba_set_drvdata(dev, NULL); | ||
| 586 | |||
| 587 | iounmap(t->etm_regs); | ||
| 588 | t->etm_regs = NULL; | ||
| 589 | |||
| 590 | amba_release_regions(dev); | ||
| 591 | |||
| 592 | sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); | ||
| 593 | sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); | ||
| 594 | sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); | ||
| 595 | |||
| 596 | return 0; | ||
| 597 | } | ||
| 598 | |||
| 599 | static struct amba_id etm_ids[] = { | ||
| 600 | { | ||
| 601 | .id = 0x0003b921, | ||
| 602 | .mask = 0x0007ffff, | ||
| 603 | }, | ||
| 604 | { 0, 0 }, | ||
| 605 | }; | ||
| 606 | |||
| 607 | static struct amba_driver etm_driver = { | ||
| 608 | .drv = { | ||
| 609 | .name = "etm", | ||
| 610 | .owner = THIS_MODULE, | ||
| 611 | }, | ||
| 612 | .probe = etm_probe, | ||
| 613 | .remove = etm_remove, | ||
| 614 | .id_table = etm_ids, | ||
| 615 | }; | ||
| 616 | |||
| 617 | static int __init etm_init(void) | ||
| 618 | { | ||
| 619 | int retval; | ||
| 620 | |||
| 621 | retval = amba_driver_register(&etb_driver); | ||
| 622 | if (retval) { | ||
| 623 | printk(KERN_ERR "Failed to register etb\n"); | ||
| 624 | return retval; | ||
| 625 | } | ||
| 626 | |||
| 627 | retval = amba_driver_register(&etm_driver); | ||
| 628 | if (retval) { | ||
| 629 | amba_driver_unregister(&etb_driver); | ||
| 630 | printk(KERN_ERR "Failed to probe etm\n"); | ||
| 631 | return retval; | ||
| 632 | } | ||
| 633 | |||
| 634 | /* not being able to install this handler is not fatal */ | ||
| 635 | (void)register_sysrq_key('v', &sysrq_etm_op); | ||
| 636 | |||
| 637 | return 0; | ||
| 638 | } | ||
| 639 | |||
| 640 | device_initcall(etm_init); | ||
| 641 | |||
