diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2006-12-19 07:24:25 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2007-02-06 11:46:44 -0500 |
commit | c265a762aa196de11f38f6f44cc817329f32a813 (patch) | |
tree | 7ce95686b85205535ba63a3e2f6eafc86a1b67dc /arch | |
parent | 62d0cfcb27cf755cebdc93ca95dabc83608007cd (diff) |
[ARM] oprofile: add ARM11 core support
Add basic support for the ARM11 profiling hardware. This is shared
between the ARM11 UP and ARM11 SMP oprofile support code.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/oprofile/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/oprofile/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_core.c | 162 | ||||
-rw-r--r-- | arch/arm/oprofile/op_model_arm11_core.h | 45 |
4 files changed, 211 insertions, 1 deletions
diff --git a/arch/arm/oprofile/Kconfig b/arch/arm/oprofile/Kconfig index 19d37730b664..615a6b9503a4 100644 --- a/arch/arm/oprofile/Kconfig +++ b/arch/arm/oprofile/Kconfig | |||
@@ -19,5 +19,8 @@ config OPROFILE | |||
19 | 19 | ||
20 | If unsure, say N. | 20 | If unsure, say N. |
21 | 21 | ||
22 | config OPROFILE_ARM11_CORE | ||
23 | bool | ||
24 | |||
22 | endmenu | 25 | endmenu |
23 | 26 | ||
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile index 6a94e54848fd..30352d6c4a25 100644 --- a/arch/arm/oprofile/Makefile +++ b/arch/arm/oprofile/Makefile | |||
@@ -8,4 +8,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ | |||
8 | 8 | ||
9 | oprofile-y := $(DRIVER_OBJS) common.o backtrace.o | 9 | oprofile-y := $(DRIVER_OBJS) common.o backtrace.o |
10 | oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o | 10 | oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o |
11 | 11 | oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o | |
diff --git a/arch/arm/oprofile/op_model_arm11_core.c b/arch/arm/oprofile/op_model_arm11_core.c new file mode 100644 index 000000000000..ad80752cb9fb --- /dev/null +++ b/arch/arm/oprofile/op_model_arm11_core.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /** | ||
2 | * @file op_model_arm11_core.c | ||
3 | * ARM11 Event Monitor Driver | ||
4 | * @remark Copyright 2004 ARM SMP Development Team | ||
5 | */ | ||
6 | #include <linux/types.h> | ||
7 | #include <linux/errno.h> | ||
8 | #include <linux/oprofile.h> | ||
9 | #include <linux/interrupt.h> | ||
10 | #include <linux/irq.h> | ||
11 | #include <linux/smp.h> | ||
12 | |||
13 | #include "op_counter.h" | ||
14 | #include "op_arm_model.h" | ||
15 | #include "op_model_arm11_core.h" | ||
16 | |||
17 | /* | ||
18 | * ARM11 PMU support | ||
19 | */ | ||
20 | static inline void arm11_write_pmnc(u32 val) | ||
21 | { | ||
22 | /* upper 4bits and 7, 11 are write-as-0 */ | ||
23 | val &= 0x0ffff77f; | ||
24 | asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); | ||
25 | } | ||
26 | |||
27 | static inline u32 arm11_read_pmnc(void) | ||
28 | { | ||
29 | u32 val; | ||
30 | asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); | ||
31 | return val; | ||
32 | } | ||
33 | |||
34 | static void arm11_reset_counter(unsigned int cnt) | ||
35 | { | ||
36 | u32 val = -(u32)counter_config[CPU_COUNTER(smp_processor_id(), cnt)].count; | ||
37 | switch (cnt) { | ||
38 | case CCNT: | ||
39 | asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); | ||
40 | break; | ||
41 | |||
42 | case PMN0: | ||
43 | asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); | ||
44 | break; | ||
45 | |||
46 | case PMN1: | ||
47 | asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); | ||
48 | break; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | int arm11_setup_pmu(void) | ||
53 | { | ||
54 | unsigned int cnt; | ||
55 | u32 pmnc; | ||
56 | |||
57 | if (arm11_read_pmnc() & PMCR_E) { | ||
58 | printk(KERN_ERR "oprofile: CPU%u PMU still enabled when setup new event counter.\n", smp_processor_id()); | ||
59 | return -EBUSY; | ||
60 | } | ||
61 | |||
62 | /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ | ||
63 | arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | | ||
64 | PMCR_C | PMCR_P); | ||
65 | |||
66 | for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { | ||
67 | unsigned long event; | ||
68 | |||
69 | if (!counter_config[CPU_COUNTER(smp_processor_id(), cnt)].enabled) | ||
70 | continue; | ||
71 | |||
72 | event = counter_config[CPU_COUNTER(smp_processor_id(), cnt)].event & 255; | ||
73 | |||
74 | /* | ||
75 | * Set event (if destined for PMNx counters) | ||
76 | */ | ||
77 | if (cnt == PMN0) { | ||
78 | pmnc |= event << 20; | ||
79 | } else if (cnt == PMN1) { | ||
80 | pmnc |= event << 12; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * We don't need to set the event if it's a cycle count | ||
85 | * Enable interrupt for this counter | ||
86 | */ | ||
87 | pmnc |= PMCR_IEN_PMN0 << cnt; | ||
88 | arm11_reset_counter(cnt); | ||
89 | } | ||
90 | arm11_write_pmnc(pmnc); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | int arm11_start_pmu(void) | ||
96 | { | ||
97 | arm11_write_pmnc(arm11_read_pmnc() | PMCR_E); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | int arm11_stop_pmu(void) | ||
102 | { | ||
103 | unsigned int cnt; | ||
104 | |||
105 | arm11_write_pmnc(arm11_read_pmnc() & ~PMCR_E); | ||
106 | |||
107 | for (cnt = PMN0; cnt <= CCNT; cnt++) | ||
108 | arm11_reset_counter(cnt); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * CPU counters' IRQ handler (one IRQ per CPU) | ||
115 | */ | ||
116 | static irqreturn_t arm11_pmu_interrupt(int irq, void *arg) | ||
117 | { | ||
118 | struct pt_regs *regs = get_irq_regs(); | ||
119 | unsigned int cnt; | ||
120 | u32 pmnc; | ||
121 | |||
122 | pmnc = arm11_read_pmnc(); | ||
123 | |||
124 | for (cnt = PMN0; cnt <= CCNT; cnt++) { | ||
125 | if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) { | ||
126 | arm11_reset_counter(cnt); | ||
127 | oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt)); | ||
128 | } | ||
129 | } | ||
130 | /* Clear counter flag(s) */ | ||
131 | arm11_write_pmnc(pmnc); | ||
132 | return IRQ_HANDLED; | ||
133 | } | ||
134 | |||
135 | int arm11_request_interrupts(int *irqs, int nr) | ||
136 | { | ||
137 | unsigned int i; | ||
138 | int ret = 0; | ||
139 | |||
140 | for(i = 0; i < nr; i++) { | ||
141 | ret = request_irq(irqs[i], arm11_pmu_interrupt, IRQF_DISABLED, "CP15 PMU", NULL); | ||
142 | if (ret != 0) { | ||
143 | printk(KERN_ERR "oprofile: unable to request IRQ%u for MPCORE-EM\n", | ||
144 | irqs[i]); | ||
145 | break; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | if (i != nr) | ||
150 | while (i-- != 0) | ||
151 | free_irq(irqs[i], NULL); | ||
152 | |||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | void arm11_release_interrupts(int *irqs, int nr) | ||
157 | { | ||
158 | unsigned int i; | ||
159 | |||
160 | for (i = 0; i < nr; i++) | ||
161 | free_irq(irqs[i], NULL); | ||
162 | } | ||
diff --git a/arch/arm/oprofile/op_model_arm11_core.h b/arch/arm/oprofile/op_model_arm11_core.h new file mode 100644 index 000000000000..6f8538e5a960 --- /dev/null +++ b/arch/arm/oprofile/op_model_arm11_core.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /** | ||
2 | * @file op_model_arm11_core.h | ||
3 | * ARM11 Event Monitor Driver | ||
4 | * @remark Copyright 2004 ARM SMP Development Team | ||
5 | * @remark Copyright 2000-2004 Deepak Saxena <dsaxena@mvista.com> | ||
6 | * @remark Copyright 2000-2004 MontaVista Software Inc | ||
7 | * @remark Copyright 2004 Dave Jiang <dave.jiang@intel.com> | ||
8 | * @remark Copyright 2004 Intel Corporation | ||
9 | * @remark Copyright 2004 Zwane Mwaikambo <zwane@arm.linux.org.uk> | ||
10 | * @remark Copyright 2004 Oprofile Authors | ||
11 | * | ||
12 | * @remark Read the file COPYING | ||
13 | * | ||
14 | * @author Zwane Mwaikambo | ||
15 | */ | ||
16 | #ifndef OP_MODEL_ARM11_CORE_H | ||
17 | #define OP_MODEL_ARM11_CORE_H | ||
18 | |||
19 | /* | ||
20 | * Per-CPU PMCR | ||
21 | */ | ||
22 | #define PMCR_E (1 << 0) /* Enable */ | ||
23 | #define PMCR_P (1 << 1) /* Count reset */ | ||
24 | #define PMCR_C (1 << 2) /* Cycle counter reset */ | ||
25 | #define PMCR_D (1 << 3) /* Cycle counter counts every 64th cpu cycle */ | ||
26 | #define PMCR_IEN_PMN0 (1 << 4) /* Interrupt enable count reg 0 */ | ||
27 | #define PMCR_IEN_PMN1 (1 << 5) /* Interrupt enable count reg 1 */ | ||
28 | #define PMCR_IEN_CCNT (1 << 6) /* Interrupt enable cycle counter */ | ||
29 | #define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ | ||
30 | #define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ | ||
31 | #define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ | ||
32 | |||
33 | #define PMN0 0 | ||
34 | #define PMN1 1 | ||
35 | #define CCNT 2 | ||
36 | |||
37 | #define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter)) | ||
38 | |||
39 | int arm11_setup_pmu(void); | ||
40 | int arm11_start_pmu(void); | ||
41 | int arm11_stop_pmu(void); | ||
42 | int arm11_request_interrupts(int *, int); | ||
43 | void arm11_release_interrupts(int *, int); | ||
44 | |||
45 | #endif | ||